#include "../../Scene/Controller/RuController_Camera.h"

#pragma unmanaged

// ************************************************************************************************************************************************************

ruRTTI_DEFINE_SUBCLASS(CRuController_Camera, IRuEntity_Controller)
ruHEAP_DEFINE_SUBCLASS(CRuController_Camera, "CRuController_Camera", "IRuEntity_Controller")
ruCLASSGUID_DEFINE(CRuController_Camera, 0xC3AA17A3, 0x8CD943ba, 0x93B10784, 0x48C6F096)

// ************************************************************************************************************************************************************

CRuController_Camera::CRuController_Camera()
:	m_cameraMovementSpeed(10.0f),
	m_collisionExclusionMask(0x00000000),
	m_targetCamera(NULL),
	m_collisionObject(NULL)
{
}

CRuController_Camera::~CRuController_Camera()
{
	ruSAFE_RELEASE(m_targetCamera);

	// Release old collision object
	ruSAFE_RELEASE(m_collisionObject);
}

void CRuController_Camera::SetCollisionExclusionMask(UINT32 mask)
{
	m_collisionExclusionMask = mask;
}

void CRuController_Camera::Dolly(REAL offset, BOOL buffered)
{
	m_targetCamera->MoveCamera(CRuVector3(0.0f, 0.0f, offset), TRUE);
}

void CRuController_Camera::UpdatePresetCameraConfiguration()
{
	m_currentCameraPosition = m_targetCamera->GetTargetPosition();

	CRuVector3 cameraAxis = m_targetCamera->GetCameraPosition() - m_targetCamera->GetTargetPosition();
	m_presetCameraToTargetDistance = cameraAxis.Magnitude();
}

BOOL CRuController_Camera::Update(REAL elapsedTime)
{
	if(m_targetCamera)
	{
		// Perform buffered operations
		CRuVector3 cameraAxis = m_targetCamera->GetCameraPosition() - m_targetCamera->GetTargetPosition();
		REAL currentCameraToTargetDistance = cameraAxis.Magnitude();

		if(m_targetCameraToTargetDistance != currentCameraToTargetDistance)
		{
			if(currentCameraToTargetDistance < m_targetCameraToTargetDistance)
			{
				Dolly(max(-m_cameraMovementSpeed * elapsedTime, currentCameraToTargetDistance - m_targetCameraToTargetDistance), FALSE);
			}
			else
			{
				Dolly(min(m_cameraMovementSpeed * elapsedTime, currentCameraToTargetDistance - m_targetCameraToTargetDistance), FALSE);
			}
		}
/*
		// Spring-load the camera
		CRuVector3 absCamPos = m_targetCamera->GetTargetPosition();
		CRuVector3 movementVector = absCamPos - m_currentCameraPosition;

		if(movementVector.Magnitude() > 500.0f)
		{
			m_currentCameraPosition = m_targetCamera->GetTargetPosition();
		}
		else
		{
			m_currentCameraPosition = m_currentCameraPosition + movementVector * 0.01f;

			m_targetCamera->Move(movementVector * -1.0f, FALSE);
			m_targetCamera->Move(movementVector * 10.0f * elapsedTime, FALSE);
//			m_targetCamera->SetTargetPosition(m_currentCameraPosition);

		}
*/
		// Check for collision
		if(m_collisionObject)
		{
			CRuCollisionQuery_Gaia_CamTest colQuery(m_targetCamera->GetTargetPosition(), m_targetCamera->GetInverseCameraTransform(), m_targetCamera->GetProjectionTransform());

			colQuery.m_exclusionMask = 0x00800000;		// Exclude foliage surfaces

			if(m_collisionObject->GetType().IsSubClassOf(CRuEntity::Type))
			{
				static_cast<CRuEntity *>(m_collisionObject)->QueryCollision(&colQuery);
			}
			else if(m_collisionObject->GetType().IsSubClassOf(IRuCollisionHierarchy::Type))
			{
				static_cast<IRuCollisionHierarchy *>(m_collisionObject)->QueryCollision(&colQuery);
			}

			if(colQuery.m_positiveHit)
			{
				REAL colDistance = max(5.0f, colQuery.m_colVector.Magnitude());

				m_cameraMovementSpeed = 1000.0f;
				m_targetCameraToTargetDistance = min(colDistance, m_targetCameraToTargetDistance);
			}
			else
			{
				m_cameraMovementSpeed = 30.0f;
				m_targetCameraToTargetDistance = m_presetCameraToTargetDistance;
			}
		}
	}

	// Chain update call to base controller class
	return IRuEntity_Controller::Update(elapsedTime);
}

REAL CRuController_Camera::GetStartTime() const
{
	return 0.0f;
}

REAL CRuController_Camera::GetDuration() const
{
	return FLT_MAX;
}

BOOL CRuController_Camera::IsLooping() const
{
	return FALSE;
}

IRuObject *CRuController_Camera::GetTarget()
{
	return m_targetCamera;
}

BOOL CRuController_Camera::SetTarget(IRuObject *target)
{
	// Release any old target
	ruSAFE_RELEASE(m_targetCamera);

	// Track new target
	if(target->GetType().IsSubClassOf(CRuCamera::Type))
	{
		m_targetCamera = static_cast<CRuCamera *>(target);
		m_targetCamera->AddRef();

		UpdatePresetCameraConfiguration();
	}

	return TRUE;
}

void CRuController_Camera::SetCollisionObject(IRuObject *colObject)
{
	// Release old collision object
	ruSAFE_RELEASE(m_collisionObject);

	// Save reference to new collision object
	if(colObject)
	{
		m_collisionObject = colObject;
		m_collisionObject->AddRef();
	}
}

// ************************************************************************************************************************************************************

#pragma managed
