#include "BstmCamera.h"

namespace Brainstem
{
	CameraFirstPerson::CameraFirstPerson(IKernel* kernel, IDirect3DDevice9* device, float fov, float znear, float zfar, float rotationSpeed, float movementSpeed, bool isNoClip) 
		: Camera(kernel, device, fov, znear, zfar)
	{
		RotationSpeed = rotationSpeed;
		MovementSpeed = movementSpeed;
		IsNoClip = isNoClip;
	}

	CameraFirstPerson::~CameraFirstPerson() { }

	void CameraFirstPerson::Init() 
	{ 
		Yaw = 0.0f;
		Pitch = 0.0f;

		MousePosition_Previous = Kernel->GetWindowCenter();
		AspectRatio = Kernel->GetWindowSize().Width / Kernel->GetWindowSize().Height;

		Update();
	}

	void CameraFirstPerson::Update()
	{
		OldYaw = Yaw;
		OldPitch = Pitch;

		float timeDelta = Kernel->GetDeltaTime() / 1000.0f;

		MousePosition_Current = Kernel->GetMousePosition();

		if (MousePosition_Current != MousePosition_Previous)
		{
			Vector2<float> mouseDelta = Vector2<float>(MousePosition_Current.X - MousePosition_Previous.X,
				MousePosition_Current.Y - MousePosition_Previous.Y);

			Yaw -= RotationSpeed * -mouseDelta.X * timeDelta;
			Pitch -= RotationSpeed * mouseDelta.Y * timeDelta;

			if (Yaw > BSTM_PI * 2.0f)
				Yaw = 0.0f;
			else if (Yaw < 0.0f)
				Yaw = BSTM_PI * 2.0f;

			if (Pitch > BSTM_PI)
				Pitch = -BSTM_PI;
			else if (Pitch < -BSTM_PI)
				Pitch = BSTM_PI;

			if (Pitch < -BSTM_PI_OVER_2)
				Pitch = -BSTM_PI_OVER_2;

			if (Pitch > BSTM_PI_OVER_2)
				Pitch = BSTM_PI_OVER_2;

			Kernel->SetMousePosition(Kernel->GetWindowCenter());

			UpdateViewMatrix();
		}

		UpdatePosition(timeDelta);

		Velocity *= Vector3<float>(0, 0, 0);

		UpdateProjectionMatrix();
		UpdateViewMatrix();

		MousePosition_Previous = Kernel->GetWindowCenter();

		YawDelta = Yaw - OldYaw;
		PitchDelta = Pitch - OldPitch;

		Target = GetDirection();

		UpdateFrustumPlanes();
	}

	void CameraFirstPerson::UpdatePosition(float timeDelta)
	{
		Matrix4<float> cameraRotation;

		if (!IsNoClip)
			cameraRotation.SetRotationRadians(Vector3<float>(0.0f, Yaw, 0.0f));
		else
			cameraRotation.SetRotationRadians(Vector3<float>(Pitch, Yaw, 0.0f));

		Vector3<float> cameraRotatedVector;
		cameraRotation.TransformVector(cameraRotatedVector, Velocity * timeDelta);

		Position += cameraRotatedVector * MovementSpeed;
	}

	void CameraFirstPerson::UpdateViewMatrix()
	{
		Matrix4<float> cameraRotation;
		cameraRotation.SetRotationRadians(Vector3<float>(Pitch, Yaw, 0.0f));

		Vector3<float> cameraOriginalTarget = Vector3<float>(0, 0, -1);
		Vector3<float> cameraOriginalUpVec = Vector3<float>(0, 1, 0);

		Vector3<float> cameraRotatedTarget;
		cameraRotation.TransformVector(cameraRotatedTarget, cameraOriginalTarget);

		Vector3<float> cameraFinalTarget = Position + cameraRotatedTarget;

		Vector3<float> cameraRotatedUpVector;
		cameraRotation.TransformVector(cameraRotatedUpVector, cameraOriginalUpVec);

		Matrix4<float> LookAt;
		View = LookAt.CreateCameraLookAtMatrixLH(Position, cameraFinalTarget, cameraRotatedUpVector);
	}

	void CameraFirstPerson::UpdateProjectionMatrix()
	{
		Matrix4<float> PerspFOV;
		Projection = PerspFOV.CreateProjectionMatrixPerspectiveFovLH(FOV, AspectRatio, zNear, zFar);
	}
}