/**
* Bone Record System
* @version: 03-02-2005
* @author Joseph 'Pcjoe' Florencio
*/

#include "hud.h"
#include "cl_util.h"
#include "studio_util.h"
#include "r_studioint.h"
#include "bonerec.h"
extern engine_studio_api_t IEngineStudio;

CBoneManager gBoneManager;

/*
* Bone Data Class
*/
// Set position and rotation from bones matrix data
void CBoneData::setFromMatrix( float matrix[3][4] )
{	// Set quaternion rotation
	getRotation()->MatrixToQuaternion(matrix);

	// Set position
	vec3_t vecPos;
	for(int i = 0; i < 3; i++)
		vecPos[i] = matrix[i][3];
	setPosition(vecPos);
}

/*
* Bone Accessor Class
*/

// Get Studio Header
studiohdr_t *CBoneAccessor::getStudioHeader( void )
{	// No ent index
	if(!getEntIndex())
	{	return NULL;
	}

	studiohdr_t *pStudioHeader = NULL;
	cl_entity_t *pEnt = NULL;

	// temp entity
	if(getTempEntity())
		pEnt = getTempEntity();
	// regular entity
	else
		pEnt = gEngfuncs.GetEntityByIndex(getEntIndex());

	if(pEnt)
		pStudioHeader = (studiohdr_t *)IEngineStudio.Mod_Extradata (pEnt->model);
	
	return pStudioHeader;
}

// Check model (call updateInitialInfo if model name changes)
void CBoneAccessor::checkModel( void )
{	// No ent index
	if(!getEntIndex())
	{	return;
	}

	// Get pointer to studio header
	studiohdr_t *pStudioHeader = getStudioHeader();

	// Model name changed, update info
	if(pStudioHeader && (getModel() != pStudioHeader->name))
		updateInitialInfo();
}

// Update initial information ( bone names ), must set ent index prior to calling this
void CBoneAccessor::updateInitialInfo( void )
{	// No ent index
	if(!getEntIndex())
	{	return;
	}
	
	// Get pointer to studio header
	studiohdr_t *pStudioHeader = getStudioHeader();
	if(!pStudioHeader)
	{	return;
	}

	// Get pointer to bones data
	mstudiobone_t *pBones = (mstudiobone_t *)((byte *)pStudioHeader + pStudioHeader->boneindex);
	if(!pBones)
	{	return;
	}

	// Set model name
	setModel(pStudioHeader->name);

	// Resize bone array
	if(pStudioHeader->numbones != getBoneData()->size())
        getBoneData()->resize(pStudioHeader->numbones);

	// Update bone names
	for(unsigned int i = 0; i < getBoneData()->size(); i++)
	{	(*getBoneData())[i].setName(pBones[i].name);
	}

	// Resize attachment array
	if(pStudioHeader->numattachments != getAttachments()->size())
		getAttachments()->resize(pStudioHeader->numattachments);

	// Update attachments
	updateAttachments();
}

// Update attachments
void CBoneAccessor::updateAttachments( void )
{	// No ent index
	if(!getEntIndex())
	{	return;
	}

	// Get entity
	cl_entity_t *pEnt = gEngfuncs.GetEntityByIndex(getEntIndex());
	if(!pEnt)
	{	return;
	}

	// Update attachments
	for(unsigned int i = 0; i < getAttachments()->size(); i++)
	{	(*getAttachments())[i] = pEnt->attachment[i];
	}
}

// Set bone data from bone transform array
void CBoneAccessor::setBoneData( float (*pBoneTransform)[128][3][4] )
{	for(unsigned int i = 0; i < getBoneData()->size(); i++)
	{	(*getBoneData())[i].setFromMatrix((*pBoneTransform)[i]);
	}
}

// Set render data ( render position + angle )
void CBoneAccessor::setRenderData( float (*pRotationMatrix)[3][4], vec3_t vecAng )
{	vec3_t vecPos;
//	vec3_t vecAng;
	// Grab position
	for(int i = 0; i < 3; i++)
		vecPos[i] = (*pRotationMatrix)[i][3];

	// Grab rotation
	// FIXME: Doesn't seem to be working, for the time being use the angle from studiomodelrender
//	AngleMatrix (vecAng, (*pRotationMatrix));

	// Set data
	setRenderPosition(vecPos);
	setRenderAngle(vecAng);
}

// Get bone
CBoneData *CBoneAccessor::getBone( int iBone )
{	// Invalid bone
	if(iBone < 0 || (unsigned int)iBone > getBoneData()->size())
	{	return NULL;
	}
	// Return bone data
	return (&(*getBoneData())[iBone]);
}

// Get bone
CBoneData *CBoneAccessor::getBone( std::string name )
{	CBoneData *pBone = NULL;
	// Find bone
	for(unsigned int i = 0; i < getBoneData()->size(); i++)
	{	if((*getBoneData())[i].getName() == name)
		{	pBone = (&(*getBoneData())[i]);
			break;
		}
	}
	return pBone;
}

// DEBUG: print bone data
void CBoneAccessor::printData( void )
{	std::string strPrint = "\n\n*----------\n* DEBUG MODEL PRINTOUT\n";
	strPrint += "model=";
	strPrint += getModel();
	strPrint += "\nBone List\n__________\n";
	for(unsigned int i = 0; i < getBoneData()->size(); i++)
	{	strPrint += (*getBoneData())[i].getName();
		strPrint += "\n";
	}
	strPrint += "\n__________\n";
	strPrint += "DEBUG MODEL PRINTOUT: DONE\n----------\n\n";
	ConsolePrint(strPrint.c_str());
}

/*
* Bone Manager Class
*/
CBoneManager::CBoneManager()
{
}

// Add Accessor
CBoneAccessor *CBoneManager::addAccessor( int idx )
{	if(!idx)
	{	return NULL;
	}
	CBoneAccessor *pExist = findAccessor(idx);
	// Already exist, just increase dependencies
	if(pExist)
	{	pExist->incDependencies();
		return pExist;
	}
	else
	{	// Create new accessor
		CBoneAccessor newAccessor;
		newAccessor.setEntIndex(idx);
		if(idx>0)
			newAccessor.updateInitialInfo();
		newAccessor.incDependencies();

		// Add to list
		getBoneAccessors()->push_front(newAccessor);
		return (&(*getBoneAccessors()->begin()));
	}
	return NULL;
}

// Remove Accessor
void CBoneManager::removeAccessor( int idx )
{	CBoneAccessor *pFind = findAccessor(idx);
	if(pFind)
	{	removeAccessor(pFind);
	}
}

// Remove Accessor
void CBoneManager::removeAccessor( CBoneAccessor *pBoneAccessor )
{	if(pBoneAccessor)
	{	pBoneAccessor->decDependencies();
		// No more depdencies, remove
		if(pBoneAccessor->getDependencies() <= 0)
		{	std::list<CBoneAccessor>::iterator iterDelete;
			ForEachIn(iterDelete, (*getBoneAccessors()))
			{	if((&(*iterDelete)) == pBoneAccessor)
				{	getBoneAccessors()->erase(iterDelete);
					break;
				}
			}	
		}
	}
}

// Find Accessor
CBoneAccessor *CBoneManager::findAccessor( int idx )
{	CBoneAccessor *pFind = NULL;
	std::list<CBoneAccessor>::iterator iterFind;
	ForEachIn(iterFind, (*getBoneAccessors()))
	{	if(iterFind->getEntIndex()==idx)
		{	pFind = (&(*iterFind));
			break;
		}
	}
	return pFind;
}