#ifndef _CAMERA_H_
#define _CAMERA_H_

#include <glm\glm.hpp>
#include <glm\ext.hpp>
#include <GL\glew.h>

const float TWO_PI = glm::pi<float>() * 2;

class PerspectiveData
{
public:
	PerspectiveData();

	// Projection based data
	float mFOV;
	float mAspectRatio;
	float mNearPlane;
	float mFarPlane;
	// Calculated from above data...
	glm::mat4 mProjection;

	void RecalculateProjection();
};

class ViewData
{
protected:
	glm::vec3 mLastPos;

	void StayInRoomBounds();

public:
	enum AxisType { AxisX, AxisY, AxisZ };

	ViewData();

	// View based data
	glm::vec3 mStartUp;
	glm::vec3 mStartRight;
	glm::vec3 mStartForward;

	glm::vec3 mRotations;
	glm::vec3 mPosition;

	// Calculated from above data...
	glm::mat4 mStartView;
	glm::mat4 mRotView;
	glm::mat4 mView;

	void RecalculateView();
	
	void SetPosition(GLfloat aX, GLfloat aY, GLfloat aZ);
	void SetPosition(const glm::vec3& aPos);
	glm::vec3 GetPosition() const { return -mPosition; } // Have no idea why the position is reversed

	void ModRotX(float aV) { mRotations.x += aV; }
	void ModRotY(float aV) { mRotations.y += aV; }
	void ModRotZ(float aV) { mRotations.z += aV; }

	float GetRotX() const { return mRotations.x; }
	float GetRotY() const { return mRotations.y; }
	float GetRotZ() const { return mRotations.z; }
	glm::vec3 GetRotations() { return mRotations; }

	void Translate(glm::vec3 aDir);

	glm::vec3 GetViewUp();
	glm::vec3 GetViewRight();
	glm::vec3 GetViewForward();

	void GenMV(const glm::mat4& aModel, glm::mat4& aResult);
};

class PerspectiveCamera : public PerspectiveData, public ViewData
{
public:
	PerspectiveCamera();

	// Projection * View
	glm::mat4 mProjView;
	void RecalculateAll();
	void UpdateVP();
	void GenMVP(const glm::mat4& aModel, glm::mat4& aResult);

	// Camera Movement
	void UpdateMouseRot(int aX, int aY);
	void UpdateKeyboardCameraMovement(float aDelta);
};

#endif // Close _CAMERA_H_