/**
* Model Vgui
* @version: 8-24-05
* @author Joseph 'Pcjoe' Florencio
*/
#include "hud.h"
#include "cl_util.h"
#include "parsemsg.h"
#include "com_model.h"
#include "mdlcontrol.h"
#include "modelvgui.h"
#include "studio_util.h"
#include "r_studioint.h"
#include "StudioModelRenderer.h"
#include "GameStudioModelRenderer.h"
#include "fxscript/xmlinterface.h"
extern engine_studio_api_t IEngineStudio;
extern CGameStudioModelRenderer g_StudioRenderer;

/*
* Model Vgui Animation Sequence
*/
CMV_AnimSequence::CMV_AnimSequence()
{
}

// Add Animation
void CMV_AnimSequence::addAnimation( std::string strAnim, int iAnimType, float flDurration )
{	CMV_Anim newAnim;
	newAnim.setAnimation(strAnim);
	newAnim.setAnimType(iAnimType);
	newAnim.setDurration(flDurration);
	getAnimationList()->push_back(newAnim);
}

// Parse XML
void CMV_AnimSequence::parseXML( TiXmlElement *pSeq )
{	TiXmlElement* pAnimSeq=NULL;

	// Iterate through anims and add
	IterateXML(pAnimSeq, pSeq)
	{
		char *pSeqName = (char*)pAnimSeq->Attribute("seq");
		char *pAnimType = (char*)pAnimSeq->Attribute("anim_type");
		char *pDurration = (char*)pAnimSeq->Attribute("durration");
		// You at least need a sequence name and anim type
		if(!pSeqName || !pAnimType)
		{	continue;
		}
		// Set Anim type
		int iAnimType=0;
		if(!stricmp(pAnimType,"loop"))
		{	iAnimType = MV_LOOPING;
		}
		else if(!stricmp(pAnimType,"non_loop"))
		{	iAnimType = MV_NON_LOOPING;
		}
		else if(!stricmp(pAnimType,"switch_on_finish"))
		{	iAnimType = MV_SWITCH_ON_FINISH;
		}
		// Get Durration
		float flDurration=0;
		if(pDurration && (iAnimType != MV_SWITCH_ON_FINISH))
		{	flDurration = atof(pDurration);
		}

		// Add Animation
		addAnimation(pSeqName,iAnimType,flDurration);
	}
}

/*
* Model Vgui
*/
// Constructors
CModelVgui::CModelVgui()
{	setLastFrame(0);
	setStartSequenceTime(0);
	setLastSequence("");
	setCurrentAnimInSequence(0);
	setCurrentSequence("");
	getModelControl()->setAngle(vec3_t(0,180,0));
	getModelControl()->setBodyPart(0);
	getModelControl()->setIndex(0);
}

CModelVgui::CModelVgui(std::string strModel)
{	CModelVgui();
	setModel(strModel);
}

CModelVgui::CModelVgui(model_s *pModel)
{	CModelVgui();
	setModel(pModel);
}

// Set Model
void CModelVgui::setModel( model_s *pModel )
{	getModelControl()->setModel(pModel);
}

void CModelVgui::setModel( std::string strModel )
{	setModel(IEngineStudio.Mod_ForName(strModel.c_str(),0));
}

// Save Last Anim Data
void CModelVgui::saveLastAnimData( void )
{	setLastFrame(getModelControl()->getFrame());
	setLastSequence(getModelControl()->getSequence());
}

// Play Sequence
void CModelVgui::playSequence( std::string strSequence )
{	saveLastAnimData();
	setCurrentSequence(strSequence);
	setCurrentAnimInSequence(0);
	setStartSequenceTime(gEngfuncs.GetClientTime());
}

// Next Anim
void CModelVgui::nextAnim( void )
{	if(getCurrentSequence() != "")
	{	// Find sequence
		std::map<std::string,CMV_AnimSequence>::iterator iterFind = m_AnimSequence.find(getCurrentSequence());
		if(iterFind != m_AnimSequence.end())
		{	// Save frame + sequence, and move on
			saveLastAnimData();
			setStartSequenceTime(gEngfuncs.GetClientTime());
			// Start over
			if(getCurrentAnimInSequence() >= (signed(iterFind->second.getAnimationList()->size())-1))
			{	setCurrentAnimInSequence(0);
			}
			// Next anim
			else
			{	setCurrentAnimInSequence(getCurrentAnimInSequence()+1);
			}
		}
	}
}

// Think
void CModelVgui::Think( void )
{	
	// Get Sequence
	std::map<std::string,CMV_AnimSequence>::iterator iterFind = m_AnimSequence.find(getCurrentSequence());
	if(iterFind != m_AnimSequence.end())
	{	std::vector<CMV_Anim> *pAnimList = iterFind->second.getAnimationList();
		int iAnimInSeq = getCurrentAnimInSequence();
		// Get individual animation
		if(iAnimInSeq >= 0 && iAnimInSeq < signed(pAnimList->size()))
		{	CMV_Anim *pAnim = &((*pAnimList)[iAnimInSeq]);
			// Set Sequence
			std::string strAnim = pAnim->getAnimation();
			getModelControl()->setSequence(strAnim);
			int iSeq = getModelControl()->getSequenceNum();
			int iMaxFrames = getModelControl()->numFramesLookup(iSeq);
			float flFrame = (gEngfuncs.GetClientTime()-getStartSequenceTime())*getModelControl()->fpsLookup(iSeq);
			// Calculate frame and next anim time depending on anim mode
			switch(pAnim->getAnimType())
			{
			case MV_LOOPING:
				if(iMaxFrames > 0)
				{	while(flFrame >= iMaxFrames)
						flFrame -= iMaxFrames;
				}

				// Next Anim
				if((pAnim->getDurration()>0) && ((getStartSequenceTime()+pAnim->getDurration())<gEngfuncs.GetClientTime()))
				{	nextAnim();
				}
				break;
			case MV_NON_LOOPING:
				if(flFrame>(iMaxFrames-1))
					flFrame = (iMaxFrames-1);

				// Next Anim
				if((pAnim->getDurration()>0) && ((getStartSequenceTime()+pAnim->getDurration())<gEngfuncs.GetClientTime()))
				{	nextAnim();
				}
				break;
			default:
			case MV_SWITCH_ON_FINISH:
				// Next Anim
				if(flFrame>(iMaxFrames-1))
				{	flFrame = iMaxFrames-1;
					nextAnim();
				}
			}
			
			getModelControl()->setSequence(strAnim);
			getModelControl()->setFrame(flFrame);

			// Interpol
			float flInterpolTime = gEngfuncs.GetClientTime()-getStartSequenceTime();
			if(flInterpolTime >= 0 && flInterpolTime < 0.2)
			{	getModelControl()->setInterpolPercent(float(1)-flInterpolTime/0.2);
				getModelControl()->setInterpolFrame(getLastFrame());
				getModelControl()->setInterpolSequence(getLastSequence());
			}
			else
			{	getModelControl()->setInterpolPercent(0);
				getModelControl()->setInterpolFrame(0);
				getModelControl()->setInterpolSequence("");
			}
		}
	}
}

// Add Anim Sequence
CMV_AnimSequence *CModelVgui::addAnimSequence( std::string strName )
{	CMV_AnimSequence newAnim;
	m_AnimSequence[strName] = newAnim;
	return &m_AnimSequence[strName];
}

/*
* Character Form
*/
// Constructor
CCharacterFormVgui::CCharacterFormVgui()
{	// Set default camera
	setCameraPosition(vec3_t(-10, 0, 6));
	setCameraAngle(vec3_t(0,10,0));
}

// Add Attack
void CCharacterFormVgui::addAttack( std::string strAtkName, std::string strAnimSequence )
{	m_Attacks[strAtkName] = strAnimSequence;
}

// Add Statistic
void CCharacterFormVgui::addStatistic( std::string strStName, float flValue )
{	m_Statistics[strStName] = flValue;
}

// Parse XML
void CCharacterFormVgui::parseXML( TiXmlElement *pForm )
{	char *pName = (char*)pForm->Attribute("name");
	char *pModel = (char*)pForm->Attribute("model");
	// Set form name and model
	if(pName)
		setName(std::string(pName));
	if(pModel)
		getModel()->setModel(std::string(pModel));

	TiXmlElement *pFormData=NULL;

	// Iterate through groups
	IterateXML(pFormData, pForm)
	{	// Animation Sequence
		if(!stricmp(pFormData->Value(),"animseq"))
		{	TiXmlElement *pSequences=NULL;

			// Iterate through sequences
			IterateXML(pSequences, pFormData)
			{	char *pName = (char*)pSequences->Attribute("name");
				if(pName)
				{	// Create animation sequence with name set
					CMV_AnimSequence *pAnimSeq = getModel()->addAnimSequence(std::string(pName));
					// Parse XML
					pAnimSeq->parseXML(pSequences);
				}
			}
		}
		// Statistics
		else if(!stricmp(pFormData->Value(),"statistics"))
		{	TiXmlElement* pStats=NULL;

			// Iterate through groups
			IterateXML(pStats, pFormData)
			{	char *pStatName = (char*)pStats->Attribute("name");
				char *pStatValue = (char*)pStats->Attribute("value");
				if(!pStatName)
					continue;
				
				if(!pStatValue)
					m_Statistics[std::string(pStatName)] = 0;
				else
					m_Statistics[std::string(pStatName)] = atof(pStatValue);
			}
		}
		// Attacks
		else if(!stricmp(pFormData->Value(),"attacks"))
		{	TiXmlElement* pAttacks=NULL;

			// Iterate through groups
			IterateXML(pAttacks, pFormData)
			{	char *pAttack = (char*)pAttacks->Attribute("name");
				char *pAnimSeq = (char*)pAttacks->Attribute("animseq");
				if(!pAttack)
					continue;
				
				if(!pAnimSeq)
					m_Attacks[std::string(pAttack)] = "";
				else
					m_Attacks[std::string(pAttack)] = std::string(pAnimSeq);
			}
		}
		// Camera
		else if(!stricmp(pFormData->Value(),"camera"))
		{	vec3_t vecCam = vec3_t(0,0,0);
			vec3_t vecCamAngle = vec3_t(0,0,0);
			char *pValue = (char*)pFormData->Attribute("x");
			if(pValue) { vecCam.x = atof(pValue); }
			pValue = (char*)pFormData->Attribute("y");
			if(pValue) { vecCam.y = atof(pValue); }
			pValue = (char*)pFormData->Attribute("z");
			if(pValue) { vecCam.z = atof(pValue); }

			if(vecCam != vec3_t(0,0,0))
			{	setCameraPosition(vecCam);
			}

			pValue = (char*)pFormData->Attribute("yaw");
			if(pValue) { vecCamAngle[YAW] = atof(pValue); }
			pValue = (char*)pFormData->Attribute("pitch");
			if(pValue) { vecCamAngle[PITCH] = atof(pValue); }
			pValue = (char*)pFormData->Attribute("roll");
			if(pValue) { vecCamAngle[ROLL] = atof(pValue); }

			if(vecCamAngle != vec3_t(0,0,0))
			{	setCameraAngle(vecCamAngle);
			}
		}
	}

	// By default, set sequence to idle
	if(getModel())
	{	getModel()->setCurrentSequence("idle");
	}
}

// Play Sequence From Attack
void CCharacterFormVgui::playSequenceFromAttack( std::string strAttack )
{	std::map<std::string,std::string>::iterator iterFind = m_Attacks.find(strAttack);
	if(iterFind != m_Attacks.end())
	{	if(iterFind->second != "")
		{	getModel()->playSequence(iterFind->second);
		}
	}
}

// Render
void CCharacterFormVgui::Render( void )
{	gEngfuncs.pTriAPI->RenderMode( kRenderNormal );
	getModel()->Think();
	g_StudioRenderer.StudioDrawModel( getModel()->getModelControl() );
}

/*
* Character
*/
// Default constructor
CCharacterVgui::CCharacterVgui()
{
}
// Constructor that automatically parses
CCharacterVgui::CCharacterVgui( std::string strClass )
{	// Make dir
	char szDir[512];
	sprintf(szDir, "%s/classes/%s.xml", gEngfuncs.pfnGetGameDirectory(), strClass.c_str());
	// Parse XML
	parseXML(szDir);
}

void CCharacterVgui::parseXML(std::string strFile)
{	TiXmlBase::SetCondenseWhiteSpace( false ); 
	// Load
	TiXmlDocument document(strFile.c_str());
	if(document.LoadFile())
	{	TiXmlElement *pIndexElement = document.RootElement();
		// Set Character Name
		char *pName = (char*)pIndexElement->Attribute("name");
		if(pName)
		{	setName(std::string(pName));
		}

		TiXmlElement* pForms=NULL;

		// Iterate through groups
		IterateXML(pForms, pIndexElement)
		{	CCharacterFormVgui *pCharForm = addForm();
			pCharForm->parseXML(pForms);
		}
	}
	else
	{	std::string strPrint = "Can't load XML file \"";
		strPrint.append(strFile);
		strPrint.append("\" for parsing!\n");
		ConsolePrint(strPrint.c_str());
	}
	TiXmlBase::SetCondenseWhiteSpace( true ); 
}

// Add Form
CCharacterFormVgui *CCharacterVgui::addForm( void )
{	CCharacterFormVgui newForm;
	m_Forms.push_back(newForm);
	std::list<CCharacterFormVgui>::iterator iterLast = m_Forms.end();
	iterLast--;
	return (&(*iterLast));
}


// Get Form by name
CCharacterFormVgui *CCharacterVgui::getForm( std::string strName )
{	std::list<CCharacterFormVgui>::iterator iterSearch = m_Forms.begin();
	while(iterSearch != m_Forms.end())
	{	if(iterSearch->getName() == strName)
		{	return (&(*iterSearch));
		}
		iterSearch++;
	}
	return NULL;
}