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

#pragma unmanaged

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

ruRTTI_DEFINE_SUBCLASS(CRuController_Bone, IRuEntity_Controller)
ruHEAP_DEFINE_SUBCLASS(CRuController_Bone, "CRuController_Bone", "IRuEntity_Controller")
ruCLASSGUID_DEFINE(CRuController_Bone, 0xED0A1539, 0x66B14cd9, 0xAB2A77C8, 0x1AC05748)

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

CRuController_Bone::CRuController_Bone()
:	m_blendGradient(1.0f),
	m_targetEntity(NULL)
{
	// Override base parameters
	m_duration = FLT_MAX;

	m_basisTransform.SetToBasis(CRuVector3(0.0f, -1.0f, 0.0f), CRuVector3(1.0f, 0.0f, 0.0f), CRuVector3(0.0f, 0.0f, 1.0f));
}

CRuController_Bone::~CRuController_Bone()
{
	if(m_targetEntity)
	{
		m_targetEntity->UnregisterListener(this);
	}
}

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

IRuObject *CRuController_Bone::GetTarget()
{
	return m_targetEntity;
}

BOOL CRuController_Bone::SetTarget(IRuObject *targetEntity)
{
	if(targetEntity->GetType().IsSubClassOf(CRuFrameHierarchy::Type))
	{
		m_targetEntity = static_cast<CRuFrameHierarchy *>(targetEntity);
//		m_targetEntity->LinkEntity(this);

		// Setup event listener
		m_targetEntity->RegisterListener(this, NULL);
	}

	return TRUE;
}

BOOL CRuController_Bone::SetBasis(CRuVector3 xBasis, CRuVector3 yBasis, CRuVector3 zBasis)
{
	m_basisTransform.SetToBasis(xBasis, yBasis, zBasis);

	return TRUE;
}

BOOL CRuController_Bone::SetTransformTarget(INT32 subNodeID, const CRuVector3 &scale, const CRuVector3 &originVector, const CRuVector3 &targetVector, REAL weight)
{
	if(m_targetEntity == NULL)
		return FALSE;

	// Find appropriate rotation descriptor
	INT32 descIndex = -1;
	for(INT32 i = 0; i < m_transformDescriptors.Count(); ++i)
	{
		if(m_transformDescriptors[i].m_subNodeID == subNodeID)
		{
			descIndex = i;
			break;
		}
	}

	// If no appropriate rotation descriptor is available, create one
	if(descIndex < 0)
	{
		descIndex = m_transformDescriptors.Count();
		m_transformDescriptors.Add(TransformDesc());
		m_transformDescriptors[descIndex].m_subNodeID = subNodeID;
		m_transformDescriptors[descIndex].m_subNodeIndex = m_targetEntity->GetSubNodeIndex(m_transformDescriptors[descIndex].m_subNodeID);

		m_transformDescriptors[descIndex].m_scale_Current.Set(1.0f, 1.0f, 1.0f);
		m_transformDescriptors[descIndex].m_scale_0.Set(1.0f, 1.0f, 1.0f);
		m_transformDescriptors[descIndex].m_scale_1.Set(1.0f, 1.0f, 1.0f);

		m_transformDescriptors[descIndex].m_rotation_Current.Set(0.0f, 0.0f, 0.0f, -1.0f);
		m_transformDescriptors[descIndex].m_rotation_0.Set(0.0f, 0.0f, 0.0f, -1.0f);
		m_transformDescriptors[descIndex].m_rotation_1.Set(0.0f, 0.0f, 0.0f, -1.0f);
	}

	// Save scaling information
	m_transformDescriptors[descIndex].m_scale_BlendWeight = 0.0f;
	m_transformDescriptors[descIndex].m_scale_0 = m_transformDescriptors[descIndex].m_scale_Current;
	m_transformDescriptors[descIndex].m_scale_1 = scale;

	// Calculate rotational quaternion
	CRuVector3 deltaVector = targetVector - originVector;
	CRuVector3 effectiveTargetVector = originVector + (deltaVector * weight);

	// Save rotation information
	m_transformDescriptors[descIndex].m_rotation_BlendWeight = 0.0f;
	m_transformDescriptors[descIndex].m_rotationWeight = weight;
	m_transformDescriptors[descIndex].m_rotation_0 = m_transformDescriptors[descIndex].m_rotation_Current;
	m_transformDescriptors[descIndex].m_rotation_1.RotateIntoVector(effectiveTargetVector, originVector);

	return TRUE;
}

BOOL CRuController_Bone::HandleEvent(CRuEntity *entity, INT32 eventID, INT32 eventParameter0, INT32 eventParameter1, const char *eventString, REAL eventDuration, void *userData)
{
	// Chain event handler to base class
	if(IRuEntity_Controller::HandleEvent(entity, eventID, eventParameter0, eventParameter1, eventString, eventDuration, userData) == TRUE)
	{
		return TRUE;
	}

	// Ensure that the broadcasting entity is the target entity for this controller
	if(entity != m_targetEntity)
	{
		return FALSE;
	}

	// Handle event
	switch(eventID)
	{
		case ruENTITYEVENT_ONDESTROY:
			{
				if(m_targetEntity)
				{
					m_targetEntity->UnregisterListener(this);
					m_targetEntity = NULL;
				}
			}

			break;

		case ruENTITYEVENT_ONUPDATE:
			{
				if(m_targetEntity)
				{
					for(INT32 i = 0; i < m_transformDescriptors.Count(); ++i)
					{
						// Update blend weights
						m_transformDescriptors[i].m_scale_BlendWeight += m_blendGradient;
						m_transformDescriptors[i].m_scale_BlendWeight = min(m_transformDescriptors[i].m_scale_BlendWeight, 1.0f);

						m_transformDescriptors[i].m_rotation_BlendWeight += m_blendGradient;
						m_transformDescriptors[i].m_rotation_BlendWeight = min(m_transformDescriptors[i].m_rotation_BlendWeight, 1.0f);

						// Calculate current scale
						m_transformDescriptors[i].m_scale_Current = m_transformDescriptors[i].m_scale_0 * (1.0f - m_transformDescriptors[i].m_scale_BlendWeight) + m_transformDescriptors[i].m_scale_1 * m_transformDescriptors[i].m_scale_BlendWeight;

						// Calculate current rotational quaternion
						m_transformDescriptors[i].m_rotation_Current = QuaternionSlerp(m_transformDescriptors[i].m_rotation_0, m_transformDescriptors[i].m_rotation_1, m_transformDescriptors[i].m_rotation_BlendWeight);

						// Break down rotational quaternion into angle-axis
						CRuVector3 rotAxis;
						REAL rotAngle;
						m_transformDescriptors[i].m_rotation_Current.GetAngleAxis(rotAxis, rotAngle);

						// Blend into key frame
						BlendDeltaKey(m_transformDescriptors[i].m_subNodeID, m_transformDescriptors[i].m_scale_Current, rotAxis, rotAngle, CRuVector3(0.0f, 0.0f, 0.0f));
					}
				}
			}

			break;
	}

	return FALSE;
}

void CRuController_Bone::BlendDeltaKey(INT32 boneID, const CRuVector3 &scale, const CRuVector3 &rotAxis, REAL rotAngle, const CRuVector3 &position)
{
	INT32 targetNodeIndex = m_targetEntity->GetSubNodeIndex(boneID);

	if(targetNodeIndex >= 0)
	{
		CRuVector3 rotationAxis;
		CRuVector3 localScale;
		CRuVector3 localPosition;

		m_basisTransform.TransformVector(rotAxis, rotationAxis);
		m_basisTransform.TransformVector(scale, localScale);
		m_basisTransform.TransformVector(position, localPosition);

		localScale.m_x = fabs(localScale.m_x);
		localScale.m_y = fabs(localScale.m_y);
		localScale.m_z = fabs(localScale.m_z);

		CRuQuaternion rotation;
		rotation.FromAngleAxis(rotationAxis, rotAngle);

		m_targetEntity->GetInterpolator()->BlendDeltaKey(targetNodeIndex, localScale, rotation, localPosition);
	}
}

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

#pragma managed
