////////////////////////////////////////////////////////////////////////////////
//
//	Module:		camera.cpp
//	Author:		Tom Hicks
//	Creation:	09-01-2003
//	LastEdit:	10-09-2003
//	Editors:	Tom Hicks
//
//	Purpose:
//		To implement an OpenGL Camera
//
//	Summary of Methods:
//	CameraClass
//		-CameraClass();
//			Initalize PDM's.  Sets position to 0,0,0 up to 0,1,0 and view to 0,0,-1
//		-Vec3f Position();
//			Returns a copy of the position vector.
//		-Vec3f View();
//			Returns a copy of the view vector.
//		-Vec3f UpVector();
//			Returns a copy of the up vector.
//		-Vec3f Strafe();
//			Returns a copy of the strafe vector.
//		-void PositionCamera(double xpos,  double ypos,  double zpos,
//							double xview, double yview, double zview,
//							double xup,	  double yup,   double zup);
//			Sets the position, up, and view vectors to those created from the respective input values.
//		-void PositionCamera(Vec3f pos, Vec3f view, Vec3f up);
//			Sets the position, up, and view vectors to those passed in.
//		-void RotateView(double angle, double X, double Y, double Z);
//			Rotates the view angle degrees on the axis specified by X, Y, and Z.
//		-void SetViewByMouse(SCREEN g_Screen);
//			Rotates the pitch and yaw of the view based on the mouse.
//		-void RotateAroundPoint(double angle, double X, double Y, double Z, Vec3f vCenter);
//			Rotates the view angle degrees around a point vCenter on the axis specified by X, Y, Z.
//		-void StrafeCamera(double speed);
//			Moves the camera along an axis perpendicular to the plane specified by the up and view vectors a distance specified by speed.
//		-void MoveCamera(double speed);
//			Moves the camera along the axis specified by it's view vector a distance equal to speed.
//		-void Update();
//			Update the internal strafe vector.
//		-void Look();
//			Positions the current viewport at point of the camera and facing the same direction.
//
////////////////////////////////////////////////////////////////////////////////

#include "../include/camera.h"

CameraClass::CameraClass()
{
	m_vPosition = Vec3f(0,0,0);
	m_vView = Vec3f(0,0,-1);
	m_vUpVector = Vec3f(0,1,0);
}

void CameraClass::PositionCamera(double xpos,  double ypos,  double zpos,
								 double xview, double yview, double zview,
								 double xup,	  double yup,   double zup)
{
	m_vPosition = Vec3f(xpos, ypos, zpos);
	m_vView = Vec3f(xview, yview, zview);
	m_vUpVector = Vec3f(xup, yup, zup);
}

void CameraClass::PositionCamera(Vec3f pos, Vec3f view, Vec3f up)
{
	m_vPosition = pos;
	m_vView = view;
	m_vUpVector = up;
}

void CameraClass::SetViewByMouse(SCREEN g_Screen)
{
	static double currentRotX = 0.0f;
	POINT mpos;
	POINT middle;
	
	double angleZ;
	
	middle.x = g_Screen.width / 2;
	middle.y = g_Screen.height / 2;
	
	GetCursorPos(&mpos);
	SetCursorPos(middle.x, middle.y);							
	
	if(mpos.x != middle.x || mpos.y != middle.y)
	{
		angleZ = double(middle.y - mpos.y) / 1000.0f;		
		currentRotX -= angleZ;  
		
		if(currentRotX > 1.0f)
			currentRotX = 1.0f;
		else if(currentRotX < -1.0f)
			currentRotX = -1.0f;
		else
		{
			Vec3f axis = (m_vView - m_vPosition).cross(m_vUpVector);
			axis.normalize();
						
			RotateView(angleZ, axis.x, axis.y, axis.z);

			//need to switch these two when I figure out stuff for flight
			//till then I think the first is faster

			RotateView(double(middle.x - mpos.x) / 1000.0f, 0, 1, 0);
			//RotateView(double(middle.x - mpos.x) / 1000.0f, m_vUpVector.x, m_vUpVector.y, m_vUpVector.z);
		}
	}
}

void CameraClass::MoveCamera(double speed)
{
	Vec3f heading = (m_vView - m_vPosition).normalized();
	
	m_vPosition.x += heading.x * speed;
	m_vPosition.z += heading.z * speed;
	m_vView.x += heading.x * speed;
	m_vView.z += heading.z * speed;
}

void CameraClass::RotateView(double angle, double x, double y, double z)
{
	Vec3f nView;
	Vec3f cView;

	cView = m_vView - m_vPosition;

	double cosTheta = cos(angle);
	double sinTheta = sin(angle);

	nView.x = (cosTheta + (1 - cosTheta) * x * x) * cView.x +
			  ((1 - cosTheta) * x * y - z * sinTheta) * cView.y +
			  ((1 - cosTheta) * x * z + y * sinTheta) * cView.z;

	nView.y = (cosTheta + (1 - cosTheta) * y * y) * cView.y +
			  ((1- cosTheta) * x * y + z * sinTheta) * cView.x + 
			  ((1 - cosTheta) * y * z - x * sinTheta) * cView.z;

	nView.z = (cosTheta + (1 - cosTheta) * z * z) * cView.z+
			  ((1 - cosTheta) * x * z - y * sinTheta) * cView.x +
			  ((1 - cosTheta) * y * z + x * sinTheta) * cView.y;
	
	m_vView.x = m_vPosition.x + nView.x;	  
	m_vView.y = m_vPosition.y + nView.y;
	m_vView.z = m_vPosition.z + nView.z;
}

void CameraClass::StrafeCamera(double speed)
{
	m_vPosition.x += m_vStrafe.x * speed;
	m_vPosition.z += m_vStrafe.z * speed;
	m_vView.x += m_vStrafe.x * speed;
	m_vView.z += m_vStrafe.z * speed;
}



void CameraClass::Update()
{
	m_vStrafe =((m_vView - m_vPosition).cross(m_vUpVector)).normalized();
	//SetViewByMouse();	//TODO take this whole function out asap

}

void CameraClass::Look()
{
	gluLookAt(m_vPosition.x, m_vPosition.y, m_vPosition.z,
			  m_vView.x,	 m_vView.y,		m_vView.z,
			  m_vUpVector.x, m_vUpVector.y, m_vUpVector.z);
}

Vec3f CameraClass::Position()
{
	return m_vPosition;
}

Vec3f CameraClass::Strafe()
{
	return m_vStrafe;
}

Vec3f CameraClass::UpVector()
{
	return m_vUpVector;
}

Vec3f CameraClass::View()
{
	return m_vView;
}