
#include "stdafx.h"

#include "cameracontroller.h"

#include "ObjectManager.h"
#include "GameInterface.h"
#include "DrawInfo.h"
#include "Refresher.h"
#include <MW4\MW4.hpp>
#include <MW4\MW4Headers.hpp>
#include <MW4\VehicleInterface.hpp>

const float rotate_size = -Stuff::Pi * 0.2f;
const float pitch_size = 0.4f;
const float zoom_size = 44.0f;
const float speed_size = 40.0f;
const float height_size = 20.0f;
const float default_speed = 80.0f;
const float default_height = 5;



CameraController* CameraController::m_Instance = 0;

#define REALSMALLRANGE 0.0000001f

CameraController::CameraController(ObjectManager& object_manager)
: m_ObjectManager(object_manager)
, m_LastCameraAnimateTime(0)
{
	m_Speed=default_speed;
	Verify(m_Instance == 0);
	m_Instance = this;
	LastTime=0.0f;
	LookAtMode=false;
	m_YawPitchRange.range=REALSMALLRANGE;
	OldDistance=100.0f;
	RBMode=false;
	RevMouse=false;
	TerrainFollow=true;
	Reset();
}

CameraController::~CameraController()
{
	Verify(m_Instance == this);
	m_Instance = 0;
}

CameraController* CameraController::GetInstance()
{
	return (m_Instance);
}

inline bool PointIsInsideBounds(const Stuff::Point3D& point, const Stuff::Point3D& map_size)
{
	if((point.x >= map_size.x * 0.5f) || 
		(point.z >= map_size.z * 0.5f) || 
		(point.x < -map_size.x * 0.5f) || 
		(point.z < -map_size.z * 0.5f))
	{
		return (false);
	}
	
	return (true);
}

bool CameraController::StaysInsideBounds(const Stuff::Point3D& new_point, const Stuff::YawPitchRange& new_ypr) const
{
	if (PointIsInsideBounds(new_point,m_ObjectManager.GetMapSize()) == false)
	{
		return (false);
	}
	
	Stuff::LinearMatrix4D matrix(new_point);
	matrix.BuildLookAt(new_ypr);
	
	return (PointIsInsideBounds((Stuff::Point3D)matrix,m_ObjectManager.GetMapSize()));
}

void CameraController::PutPointInsideBounds(Stuff::Point3D& point)
{
	Stuff::Point3D map_size(m_ObjectManager.GetMapSize());
	
	Clamp(point.x,-map_size.x * 0.5f,map_size.x * 0.5f);
	Clamp(point.z,-map_size.z * 0.5f,map_size.z * 0.5f);
}

void CameraController::PutYPRInsideBounds(Stuff::YawPitchRange& ypr)
{
	if (StaysInsideBounds(m_LookAtPoint,ypr) == true)
	{
		return;
	}
	
	Stuff::YawPitchRange ypr2(ypr);
	
	{for (Stuff::Scalar yaw_add = 0.1f;
		  yaw_add < (GetMaxYaw() * 0.5f) + 0.1f;
		  yaw_add += 0.1f)
		  {
			  {
				  ypr2.yaw = ypr.yaw + yaw_add;
				  if (StaysInsideBounds(m_LookAtPoint,ypr2) == true)
				  {
					  ypr = ypr2;
					  return;
				  }
			  }
			  
			  {
				  ypr2.yaw = ypr.yaw - yaw_add;
				  if (StaysInsideBounds(m_LookAtPoint,ypr2) == true)
				  {
					  ypr = ypr2;
					  return;
				  }
			  }
	}}
}

void CameraController::Update()
{
	if (!GameInitilized)
		return;
	
	if ((Adept::Player::GetInstance() == 0) ||
		(Adept::Player::GetInstance()->playerInterface == 0))
	{
		return;
	}
	
	PutPointInsideBounds(m_LookAtPoint);
	PutYPRInsideBounds(m_YawPitchRange);
	
	if (StaysInsideBounds(m_LookAtPoint,m_YawPitchRange) == false)
	{
		return;
	}
	
	Stuff::LinearMatrix4D matrix(m_LookAtPoint);
	matrix.BuildLookAt(m_YawPitchRange);
	
	Stuff::Point3D camera_pos(matrix);
	
	if(TerrainFollow)
	{
		Stuff::Scalar terrain_height(ComputeTerrainElevation(camera_pos.x,camera_pos.z,0));
		
		
		if (camera_pos.y < terrain_height + GetMinHeight())
		{
			camera_pos.y = terrain_height + GetMinHeight();
			matrix.BuildTranslation(camera_pos);
		}
	}
	
	Adept::Player::GetInstance()->SetNewLocalToParent(matrix);
	Adept::Player::GetInstance()->SyncMatrices(true);
}

Stuff::YawPitchRange CameraController::GetYawPitchRange() const
{
	return (m_YawPitchRange);
}

void CameraController::SetYawPitchRange(Stuff::YawPitchRange ypr)
{
	Clamp(ypr.pitch,GetMinPitch(),GetMaxPitch());
	m_YawPitchRange = ypr;
}

void CameraController::Reset()
{
	m_YawPitchRange = Stuff::YawPitchRange(GetDefaultYaw(),GetDefaultPitch(),REALSMALLRANGE);
	LookAtMode=false;
	OldDistance=100.0f;
	m_LookAtPoint = Stuff::Point3D(0,0,0);
	m_Speed = default_speed;
	m_Height = default_height;
	
	// put camera in "editor view" mode
	if (GameInitilized && (Adept::Player::GetInstance() != 0))
	{
		Adept::Interface* iface = Adept::Player::GetInstance()->playerInterface;
		
		if ((iface != 0) &&
			(iface->IsDerivedFrom(MechWarrior4::VehicleInterface::DefaultData) == true))
		{
			MechWarrior4::VehicleInterface* vehicle_interface = Cast_Object(VehicleInterface*,iface);
			vehicle_interface->SetCameraMode(MechWarrior4::VehicleInterface::EditorCameraView);
		}
	}
	
	m_LookAtPoint.y = ComputeTerrainElevation(m_LookAtPoint.x,m_LookAtPoint.z,0) + m_Height;
	
	Update();
}

void CameraController::SetHeight(Stuff::Scalar height)
{
	m_Height = height;
	
	if(TerrainFollow)
		m_LookAtPoint.y = ComputeTerrainElevation(m_LookAtPoint.x,m_LookAtPoint.z,0) + m_Height;
	Update();
}

void CameraController::SetSpeed(Stuff::Scalar speed)
{
	m_Speed = speed;
}

Stuff::Scalar CameraController::GetHeight() const
{
	return (m_Height);
}

Stuff::Scalar CameraController::GetSpeed() const
{
	return (m_Speed);
}

void CameraController::SetLookAt(const Stuff::Point3D& look_at, bool adjust_to_terrain)
{
	m_LookAtPoint = look_at;
	
	const Stuff::Scalar terrain_elevation(ComputeTerrainElevation(m_LookAtPoint.x,m_LookAtPoint.z,0));
	
	if (adjust_to_terrain )
	{
		m_LookAtPoint.y = terrain_elevation + m_Height;
	}
	else
	{
		m_Height = m_LookAtPoint.y-terrain_elevation;
	}
}

Stuff::Point3D CameraController::GetLookAt() const
{
	return (m_LookAtPoint);
}

void CameraController::Draw(DrawInfo& dinf)
{
	dinf.SetPen(DrawInfo::HelperPen); 
	
	CPoint start(dinf.vinf.DataToScreen(m_LookAtPoint));
	Stuff::LinearMatrix4D matrix(m_LookAtPoint);
	
	matrix.BuildLookAt(Stuff::YawPitchRange(m_YawPitchRange.yaw,m_YawPitchRange.pitch,GetLookAtMode()?m_YawPitchRange.range:-100.0f));
	CPoint end(dinf.vinf.DataToScreen((Stuff::Point3D)matrix));
	
	/*
	RECT r;
	dinf.GetDC()->GetClipBox(&r);
	
	  if ((start.x > r.right)||
	  (end.x > r.right) ||
	  (start.y > r.bottom) ||
	  (end.y > r.bottom))
	  {
	  return;
	  }
	*/
/*	
	if(!GetLookAtMode())
	{
	int ydist,xdist;
	ydist=end.y-start.y;
	xdist=end.x-start.x;
	end.y=(ydist*10)/xdist;
	end.x=(xdist*10)/ydist;

	
	}
*/
	
	dinf.GetDC()->MoveTo(start);
	dinf.GetDC()->LineTo(end);
	
	{
		const int circle_size(4);
		CRect rect(start.x - circle_size,
			start.y - circle_size,
			start.x + circle_size,
			start.y + circle_size);
		dinf.GetDC()->Ellipse(&rect);
	}
	
	if(GetLookAtMode())
		{
			const int circle_size(6);
			CRect rect(end.x - circle_size,
				end.y - circle_size,
				end.x + circle_size,
				end.y + circle_size);
			dinf.GetDC()->Ellipse(&rect);
		}
}




float GetCurTime()
{
	return GetTickCount()/1000.0f;
}

bool CameraController::ProcessIO()
{
	Scalar new_time= GetCurTime();
	Scalar frame_time=new_time-LastTime;
	LastTime=new_time;
	
	if(frame_time<(1/1000.0f)) frame_time=(1/1000.0f);
	
	
	YawPitchRange ypr_offset(0,0,0);
	Point3D offset(0,0,0);
	bool yaw_changed = false;
	bool poschanged=false;
	
	if(GetLookAtMode())
	{
		
		
		if(GetAsyncKeyState('C') || GetAsyncKeyState('A'))
		{
			ypr_offset.yaw = rotate_size*frame_time;
			poschanged=true;
		}
		
		if(GetAsyncKeyState('Z')  || GetAsyncKeyState('D'))
		{
			ypr_offset.yaw = -rotate_size*frame_time;
			poschanged=true;
		}
		
		if(GetAsyncKeyState(VK_SHIFT))
		{
			if(GetAsyncKeyState('S'))
			{
				ypr_offset.pitch = rotate_size*frame_time * pitch_size;
				poschanged=true;
			}
			
			if(GetAsyncKeyState('W'))
			{
				ypr_offset.pitch = -rotate_size*frame_time * pitch_size;
				poschanged=true;
			}
			
		}
		else
		{
			if(GetAsyncKeyState('W'))
			{
				ypr_offset.range = -m_Speed * frame_time;
				poschanged=true;
			}
			
			if(GetAsyncKeyState('S'))
			{
				ypr_offset.range = m_Speed * frame_time;
				poschanged=true;
			}
		}
		
	}
	else
	{
		
		if(RBMode)
		{
			if(GetAsyncKeyState('W'))
			{
				TranslateForward(m_Speed*frame_time);
			}
			
			if(GetAsyncKeyState('S'))
			{
				TranslateForward(-m_Speed*frame_time);
			}
			
		}
		else
		{
			if(GetAsyncKeyState(VK_SHIFT))
			{
				if(GetAsyncKeyState('W'))
				{
					m_Height += m_Speed * frame_time;
					poschanged=true;
				}
				
				if(GetAsyncKeyState('S'))
				{
					m_Height -= m_Speed * frame_time;
					poschanged=true;
				}
				
			}
			else
			{
				if(GetAsyncKeyState('W'))
				{
					offset.z = m_Speed*frame_time;
					poschanged=true;
				}
				
				if(GetAsyncKeyState('S'))
				{
					offset.z = -m_Speed*frame_time;
					poschanged=true;
				}
			}
		}
		if(GetAsyncKeyState('A'))
		{
			offset.x = m_Speed*frame_time;
			poschanged=true;
		}
		
		if(GetAsyncKeyState('D'))
		{
			offset.x = -m_Speed*frame_time;
			poschanged=true;
		}
		
		
		
	}
	
	
	
	
	if(GetAsyncKeyState('X'))
	{
		m_Height = default_height;
		poschanged=true;
	}
	
	
	if(GetAsyncKeyState('E'))
	{
		ypr_offset.yaw = rotate_size*frame_time;
		poschanged=true;
		if(GetLookAtMode()) 
		{	
			SetLookAtMode(false);
		
		}
		m_YawPitchRange.range=REALSMALLRANGE;
	}
	
	if(GetAsyncKeyState('Q'))
	{
		ypr_offset.yaw = -rotate_size*frame_time;
		poschanged=true;
		if(GetLookAtMode())
		{
			SetLookAtMode(false);
		}
		m_YawPitchRange.range=REALSMALLRANGE;
	}
	
	if(GetAsyncKeyState('R'))
	{
		ypr_offset.pitch = rotate_size*frame_time * pitch_size;
		poschanged=true;
	}
	
	if(GetAsyncKeyState('F'))
	{
		ypr_offset.pitch = -rotate_size*frame_time * pitch_size;
		poschanged=true;
	}
	
	

	Point3D new_point(m_LookAtPoint);
	YawPitchRange new_ypr(m_YawPitchRange);

	new_ypr.yaw+=ypr_offset.yaw;
	new_ypr.pitch+=ypr_offset.pitch;
	new_ypr.range+=ypr_offset.range;


	while (new_ypr.yaw < 0)
	{
		new_ypr.yaw += Stuff::Two_Pi;
		poschanged=true;
	}

	while (new_ypr.yaw > Stuff::Two_Pi)
	{
		new_ypr.yaw -= Stuff::Two_Pi;
		poschanged=true;
	}
	
	

	if(poschanged)
	{
		Clamp(new_ypr.pitch,GetMinPitch(),GetMaxPitch());
		Clamp(new_ypr.range,GetMinDistance(),GetMaxDistance());
		
		Clamp(m_Height,GetMinHeight(),GetMaxHeight());
		
		
		m_YawPitchRange = new_ypr;
		
		LinearMatrix4D rotation(YawPitchRoll(m_YawPitchRange.yaw,0,0));
		offset *= rotation;
		new_point += offset;
		
		if(!GetLookAtMode())
		{
			
			if(TerrainFollow && !RBMode)
				new_point.y = ComputeTerrainElevation(new_point.x,new_point.z,0) + m_Height;
			else
				new_point.y = m_Height;
		}
		
		m_LookAtPoint=new_point;
		
	}
	
	Update();
	
	return true;
}

void CameraController::SetLookAtMode(bool lmode)
{
	if(LookAtMode && !lmode)
	{
		OldDistance=m_YawPitchRange.range;
		m_YawPitchRange.range=REALSMALLRANGE;
		TranslateForward(-OldDistance);
		//	LinearMatrix4D pos(),rot,trans;
		
		
		LookAtMode=false;
	}
	else if(!LookAtMode && lmode)
	{
		LookAtMode=true;
		m_YawPitchRange.range=OldDistance;
		Point3D top_left,bottom_right;
		
		if (m_ObjectManager.GetSelectionExtents(&top_left,&bottom_right))
		{
			Point3D center(top_left);
			SetLookAt(center,false);	
			center += bottom_right;
			center *= 0.5f;
		}
		
		
		
	}
	
}




Stuff::Scalar CameraController::GetCameraYaw() const
{
	Stuff::Scalar rv(m_YawPitchRange.yaw + Stuff::Pi);
	
	while (rv > Stuff::Two_Pi)
	{
		rv -= Stuff::Two_Pi;
	}
	
	return (rv);
}

void CameraController::SetCameraYaw(Stuff::Scalar yaw)
{
	Stuff::LinearMatrix4D old_matrix(m_LookAtPoint);
	old_matrix.BuildLookAt(m_YawPitchRange);
	
	m_YawPitchRange.yaw = yaw - Stuff::Pi;
	while (m_YawPitchRange.yaw < 0)
	{
		m_YawPitchRange.yaw += Stuff::Two_Pi;
	}
	
	Stuff::LinearMatrix4D new_matrix(m_LookAtPoint);
	new_matrix.BuildLookAt(m_YawPitchRange);
	
	Stuff::Point3D old_pos(old_matrix);
	Stuff::Point3D new_pos(new_matrix);
	Stuff::Point3D delta;
	delta.Subtract(old_pos,new_pos);
	
	Stuff::Point3D pos(m_LookAtPoint);
	pos += delta;
	
	SetLookAt(pos,TerrainFollow);
}

void  CameraController::TranslateForward(float dist)
{
	Stuff::LinearMatrix4D mat(m_LookAtPoint),tmat(Point3D(0,0,dist));
	mat.BuildLookAt(m_YawPitchRange);
	
	tmat*=mat;
	Stuff::Point3D new_pos(tmat);
	SetLookAt(new_pos,false);
//	if(!TerrainFollow)
	{
	m_Height=new_pos.y;
	}
	
}


void CameraController::UpdateCamera()
{
	
	Point3D top_left,bottom_right;
	
	if(GetLookAtMode())
	{
		if (m_ObjectManager.GetSelectionExtents(&top_left,&bottom_right))
		{
			Point3D center(top_left);
			center += bottom_right;
			center *= 0.5f;
			SetLookAt(center,false);	
		}
		else
		{
			SetLookAtMode(false);
		}
	}
	
}

void CameraController::SaveToReg()
{
	CString wintitle="Camera";
	gos_SaveDataToRegistry((char *)(LPCSTR)(wintitle+"TerrainFollow"),&TerrainFollow,sizeof(TerrainFollow));
	gos_SaveDataToRegistry((char *)(LPCSTR)(wintitle+"RevMouse"),&RevMouse,sizeof(RevMouse));
	gos_SaveDataToRegistry((char *)(LPCSTR)(wintitle+"Speed"),&m_Speed,sizeof(m_Speed));
}

void CameraController::LoadFromReg()
{
	CString wintitle="Camera";
	unsigned long size;
	CRect rct;
	
	size=sizeof(TerrainFollow);
	gos_LoadDataFromRegistry((char *)(LPCSTR)(wintitle+"TerrainFollow"),&TerrainFollow,&size);
	size=sizeof(RevMouse);
	gos_LoadDataFromRegistry((char *)(LPCSTR)(wintitle+"RevMouse"),&RevMouse,&size);
	size=sizeof(m_Speed);
	gos_LoadDataFromRegistry((char *)(LPCSTR)(wintitle+"Speed"),&m_Speed,&size);

}

/*
LinearMatrix4D &CameraController::GetCameraMat()
{
	Stuff::LinearMatrix4D matrix(m_LookAtPoint);
	matrix.BuildLookAt(m_YawPitchRange);
	
	return matrix;
}

void CameraController::SetCameraMat(LinearMatrix4D &mat)
{

	m_LookAtPoint=mat;
	m_Height=m_LookAtPoint.y-ComputeTerrainElevation(m_LookAtPoint.x,m_LookAtPoint.z,0);
	if(GetLookAtMode())
	{
	
	
	}


}
*/