#include "Camera.h"
#include "Input.h"
#include "Global_Properties.h"

PerspectiveData::PerspectiveData()
{
	mFOV = GlobalProperties::CAMERA_FOV;
	mAspectRatio = GlobalProperties::CAMERA_ASPECT_RATIO;
	mNearPlane = GlobalProperties::CAMERA_NEAR_PLANE;
	mFarPlane = GlobalProperties::CAMERA_FAR_PLANE;
	RecalculateProjection();
}

void PerspectiveData::RecalculateProjection()
{
	mProjection = glm::perspective(mFOV, mAspectRatio, mNearPlane, mFarPlane);
}


ViewData::ViewData()
{
	mPosition = glm::vec3(0.0f, 0.0f, 0.0f);
	mStartUp = glm::vec3(0.0f, 1.0f, 0.0f);
	mStartRight = glm::vec3(1.0f, 0.0f, 0.0f);
	mStartForward = glm::vec3(0.0f, 0.0f, -1.0f);
	mRotations = glm::vec3(0.0f, 0.0f, 0.0f);

	mStartView = glm::lookAt(mPosition, mPosition + mStartForward, mStartUp);

	mView = mStartView;

	mPosition = glm::vec3(GlobalProperties::CAMERA_START_POS[0], GlobalProperties::CAMERA_START_POS[1], GlobalProperties::CAMERA_START_POS[2]);

	RecalculateView();
}

void ViewData::RecalculateView()
{
	glm::mat4 rotX = glm::rotate(mStartView, mRotations.x, mStartRight);
	glm::mat4 rotY = glm::rotate(mStartView, mRotations.y, mStartUp);

	mRotView = rotX * rotY;
	mView = glm::translate(mRotView, mPosition);
}

void ViewData::SetPosition(GLfloat aX, GLfloat aY, GLfloat aZ)
{
	mPosition.x = aX;
	mPosition.y = aY;
	mPosition.x = aX;
	StayInRoomBounds();
}

void ViewData::SetPosition(const glm::vec3& aPos)
{
	mPosition = aPos;
	StayInRoomBounds();
}

void ViewData::Translate(glm::vec3 aDir)
{
	mPosition += aDir;
	StayInRoomBounds();
}

void ViewData::StayInRoomBounds()
{
	if (!GlobalProperties::CAMERA_STAY_IN_ROOM)
		return;

	float size = GlobalProperties::ROOM_SIZE - GlobalProperties::CAMERA_IN_ROOM_TOLERANCE;

	if (mPosition.x >= size || mPosition.x <= -size
	||  mPosition.y >= size || mPosition.y <= -size
	||  mPosition.z >= size|| mPosition.z <= -size)
		mPosition = mLastPos;
	else
		mLastPos = mPosition;
}

glm::vec3 ViewData::GetViewUp()
{
	glm::vec3 up(mRotView[0][1], mRotView[1][1], mRotView[2][1]);
	return up;
}

glm::vec3 ViewData::GetViewRight()
{
	glm::vec3 right(mRotView[0][0], mRotView[1][0], mRotView[2][0]);
	return right;
}

glm::vec3 ViewData::GetViewForward()
{
	glm::vec3 forward(mRotView[0][2], mRotView[1][2], mRotView[2][2]);
	return forward;
}

void ViewData::GenMV(const glm::mat4& aModel, glm::mat4& aResult)
{
	aResult = mView * aModel;
}


PerspectiveCamera::PerspectiveCamera()
{
	RecalculateAll();
}

void PerspectiveCamera::RecalculateAll()
{
	RecalculateProjection();
	RecalculateView();
	mProjView = mProjection * mView;
}

void PerspectiveCamera::UpdateVP()
{
	mProjView = mProjection * mView;
}

void PerspectiveCamera::GenMVP(const glm::mat4& aModel, glm::mat4& aResult)
{
	aResult = mProjView * aModel;
}

void PerspectiveCamera::UpdateMouseRot(int aX, int aY)
{
	Input::InputManager& im = Input::InputManager::I();

	if (im.IsResetMousPos())
	{
		float horiz = im.GetMouseSpeedH() * float(GlobalProperties::SCREEN_WIDTH_HALF - im.GetMouseX());
		float vert = im.GetMouseSpeedV() * float(GlobalProperties::SCREEN_HEIGHT_HALF - im.GetMouseY());

		ModRotY(-horiz);
		ModRotX(-vert);

		// Clamp so you don't go upside down
		if (mRotations.x > 90.0f)
			mRotations.x = 90.0f;
		if (mRotations.x < -90.0f)
			mRotations.x = -90.0f;
	}
}

void PerspectiveCamera::UpdateKeyboardCameraMovement(float aDelta)
{
	Input::InputManager& im = Input::InputManager::I();

	// Get move speed
	float moveSpeed = GlobalProperties::CAMERA_MOVE_SPEED;
	if (im.GetShiftKeyState())
		moveSpeed *= GlobalProperties::CAMERA_MOVE_SPEED_MULTIPLIER;

	// Check if you are moving
	if (im.GetWKeyState())
		Translate(GetViewForward() * moveSpeed * aDelta);
	if (im.GetAKeyState())
		Translate(GetViewRight() * moveSpeed * aDelta); // Don't know why left is right and vice verse
	if (im.GetSKeyState())
		Translate(-GetViewForward() * moveSpeed * aDelta);
	if (im.GetDKeyState())
		Translate(-GetViewRight() * moveSpeed * aDelta);
}