package treeEngine.entities;

import elgin.data.Reference;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.util.vector.Vector3f;
import treeEngine.rendering.DisplayManager;

/**
 * This is the class that stores all data for the camera, including methods to
 * calculate third and first person cameras.
 */
public class Camera {

	private Vector3f position = new Vector3f(0, 0, 0);
	private float pitch;
	private float yaw;
	private float roll;

	private boolean isFirstPerson = false;

	private Player player;

	private float distanceFromPlayer = 40;
	private float angleAroundPlayer = 0;

	private long lastFrameTime = 0;

	public Camera(Player player, Vector3f pos, float pitch, float yaw, float roll, boolean isFP) {
		this.player = player;
		this.pitch = pitch;
		this.yaw = yaw;
		this.roll = roll;
		this.position = pos;
		this.isFirstPerson = isFP;
	}

	/**
	 * Moving the camera method, uses other methods this is the one call fits all.
	 */
	public void move() {
		/*if (Keyboard.isKeyDown(Keyboard.KEY_X)) {
			if (DisplayManager.getCurrentTime() > (lastFrameTime + 50))
				this.isFirstPerson = !this.isFirstPerson;

			lastFrameTime = DisplayManager.getCurrentTime();
		}*/

		if (!isFirstPerson) {
			calculateZoom();
			calculatePitch();
			calculateAngleAroundPlayer();

			float horizontalDistance = calculateHorizontalDistance();
			float verticalDistance = calculateVerticalDistance() + Reference.CAMERA_YOFFSET;

			calculatePosition(horizontalDistance, verticalDistance);

			this.yaw = 180 - (player.getRotation().y + angleAroundPlayer);
		} else {
			position.x = player.getPosition().x;
			position.y = player.getPosition().y + Reference.CAMERA_YOFFSET;
			position.z = player.getPosition().z;

			calculateFPMovement();
		}
	}
	
	public void invertPitch() {
		this.pitch = -pitch;
	}

	/**
	 * Calculates first person movement
	 */
	private void calculateFPMovement() {
		this.yaw = 180 - player.getRotation().y;

		if (Mouse.isButtonDown(1)) {
			float angleChange = Mouse.getDX() * 0.3f;
			yaw += angleChange;

			float pitchChange = Mouse.getDY() * 0.2f;
			pitch -= pitchChange;

			if (pitch > 90)
				pitch = 90;
		}

		player.setRotation(new Vector3f(0, 180 - yaw, 0));
	}

	/**
	 * Calculates the position of the camera.
	 * @param horizontalDistance - Horizontal distance from player
	 * @param verticalDistance - Vertical distance from player
	 */
	private void calculatePosition(float horizontalDistance, float verticalDistance) {
		float theta = player.getRotation().y + angleAroundPlayer;
		float offsetX = (float) (horizontalDistance * Math.sin(Math.toRadians(theta)));
		float offsetZ = (float) (horizontalDistance * Math.cos(Math.toRadians(theta)));

		position.y = player.getPosition().y + verticalDistance;
		if (position.y < 0.1f)
			position.y = 0.1f;

		position.x = player.getPosition().x - offsetX;
		position.z = player.getPosition().z - offsetZ;
	}

	public boolean getFirstPerson() {
		return this.isFirstPerson;
	}

	public void setFirstPerson(boolean isFirstPerson) {
		this.isFirstPerson = isFirstPerson;
	}

	private float calculateHorizontalDistance() {
		return (float) (distanceFromPlayer * Math.cos(Math.toRadians(pitch)));
	}

	private float calculateVerticalDistance() {
		return (float) (distanceFromPlayer * Math.sin(Math.toRadians(pitch)));
	}

	/* Calculates pitch, yaw, roll, and zoom. */
	private void calculateZoom() {
		float zoomLevel = Mouse.getDWheel() * 0.1f;
		distanceFromPlayer -= zoomLevel;
		if (distanceFromPlayer < 12f)
			distanceFromPlayer = 12f;
		if (distanceFromPlayer > 100f)
			distanceFromPlayer = 100f;
	}

	private void calculatePitch() {
		if (Mouse.isButtonDown(1)) {
			float pitchChange = Mouse.getDY() * 0.1f;
			pitch -= pitchChange;

			if (pitch > 90)
				pitch = 90;
		}
	}

	private void calculateAngleAroundPlayer() {
		if (Mouse.isButtonDown(1)) {
			float angleChange = Mouse.getDX() * 0.3f;
			angleAroundPlayer -= angleChange;
		}
	}

	public Vector3f getPosition() {
		return position;
	}

	public float getPitch() {
		return pitch;
	}

	public float getYaw() {
		return yaw;
	}

	public float getRoll() {
		return roll;
	}

}