/**
* FX Event
* @version: 02-28-2005
* @author Joseph 'Pcjoe' Florencio
*/

#include "fxsys.h"

// Constructor
CFXEvent::CFXEvent()
{	setDeveloperEvent(false);
	setLooping(false);
	setStaticBase(true);
	setDynamicBaseReference(-1);
	setIndex(0);
	setTime(0);
	setStartTime(0);
	setEndTime(0);
	getPathSystem()->setFXEvent(this);
}

// Destructor
CFXEvent::~CFXEvent()
{	// Delete all objects
	std::list<CFXObject*>::iterator del;
	ForEachIn(del, m_FXObjects)
	{	(*del)->cleanUp();
		delete (*del);
	}
	m_FXObjects.clear();
}

// Get Next ID
int CFXEvent::getNextID( void )
{	std::list<CFXObject*>::iterator iterCurrent;

	// Search for valid ID
	int iID = 0;
	bool bIDUsed=false;
	do
	{	iID++;
		bIDUsed=false;
		ForEachIn(iterCurrent, m_FXObjects)
		{	if((*iterCurrent)->getID() == iID)
			{	bIDUsed = true;
			}
		}
	}while(bIDUsed);
	return iID;
}

// Find object by ID
CFXObject *CFXEvent::getObjectByIndex( int idx )
{	std::list<CFXObject*>::iterator iterCurrent;
	ForEachIn(iterCurrent, m_FXObjects)
	{	if((*iterCurrent)->getID() == idx)
		{	return (*iterCurrent);
		}
	}
	return NULL;
}

// Add object
void CFXEvent::addObject( CFXObject *pObj, float flStart, float flEnd )
{	if(!pObj)
	{	return;
	}
	// Link object with local event
	pObj->setFXEvent( this );
	// Give index
	pObj->setID(getNextID());
	// Set start and end times
	pObj->setStartTime( flStart );
	pObj->setEndTime( flEnd );

	// Add to list
	m_FXObjects.push_back(pObj);
}

// Think
void CFXEvent::Think( float flTime )
{	// Set Time
	setTime(flTime);
	// Get relitive time for objects
	float flRelTime = getTime()-getStartTime();

	// Iterate through all objects and call thinks
	std::list<CFXObject*>::iterator iterCurrent = m_FXObjects.begin();
	while(iterCurrent != m_FXObjects.end())
	{	CFXObject *pObj = (*iterCurrent);
		if(pObj->canUse( flRelTime ))
		{	pObj->Think( flRelTime );
		}
		// Remove
		else if(pObj->canRemove( flRelTime ))
		{	// Delete memory
			delete pObj;
			// Remove iterator
			iterCurrent = m_FXObjects.erase( iterCurrent );
			continue;
		}
		iterCurrent++;
	}
}

// Reset Triggers (sound mainly)
void CFXEvent::resetTriggers( void )
{	float flRelTime = getTime()-getStartTime();
	std::list<CFXObject*>::iterator iterCurrent = NULL;
	ForEachIn(iterCurrent, m_FXObjects)
	{	(*iterCurrent)->getTimelineManager()->resetTriggers(flRelTime);
	}
}

// Render all objects
void CFXEvent::Render( void )
{	float flRelTime = getTime()-getStartTime();
	// Iterate through all objects and render
	std::list<CFXObject*>::iterator iterCurrent = NULL;
	ForEachIn(iterCurrent, m_FXObjects)
	{	CFXObject *pObj = (*iterCurrent);
		if(pObj->isRenderObject())
		{	CFXObjectRender *pRenderObj = (CFXObjectRender*)pObj;
			if(pRenderObj->canUse( flRelTime ) && pRenderObj->canRender())
			{	pRenderObj->Render();
			}
		}
	}
}

// Get base position/angle ( taking dynamic/static into account
vec3_t CFXEvent::getBasePosition( void )
{	// Not static, use dynamic data
	if(!isBasePositionStatic())
	{	cl_entity_t *pEnt = gEngfuncs.GetEntityByIndex(getDynamicBaseReference());
		if(pEnt)
		{	return pEnt->origin;
		}
	}
	// Use static
	return getBasePositionStatic();
}

vec3_t CFXEvent::getBaseAngle( void )
{	// Not static, use dynamic data
	if(!isBaseAngleStatic())
	{	cl_entity_t *pEnt = gEngfuncs.GetEntityByIndex(getDynamicBaseReference());
		if(pEnt)
		{	vec3_t vecAngle = pEnt->angles;
			vecAngle[PITCH]=0;
			vecAngle[ROLL]=0;
			return vecAngle;
		}
	}
	// Use static
	return getBaseAngleStatic();
}

// Make local position final ( adjust to base angles, and add base position )
vec3_t CFXEvent::makePosFinal( vec3_t vecPos )
{	// Rotate local position by base angle
	vec3_t vecFinal;
	float rmatrix[3][4];
	CQuaternion qMatrix = getBaseAngle();
	qMatrix.Conjugate();
	qMatrix.QuaternionToMatrix(rmatrix);
	VectorTransform(vecPos, rmatrix, vecFinal);
	// Add base position, return final position
	return (vecFinal + getBasePosition());
}

// Make local rotation final ( Add base rotation )
CQuaternion CFXEvent::makeAngFinal( CQuaternion qAngle )
{	vec3_t vecBase = getBaseAngle();
	vec3_t vecAng = qAngle.QuaternionToEuler();
	// Add base angle
	for(int i = 0; i < 3; i++)
        vecAng[i] += vecBase[i];
	// Convert back to quaternion
	return CQuaternion(vecAng);
}

// Get camera object
CFXObjectCamera *CFXEvent::getCameraObject( void )
{	std::list<CFXObject*>::iterator iterCurrent;
	ForEachIn(iterCurrent, m_FXObjects)
	{	CFXObject *pObj = (*iterCurrent);
		if(pObj->isCameraObject())
		{	return (CFXObjectCamera*)pObj;
		}
	}
	return NULL;
}

// Remove Object
void CFXEvent::removeObject( int idx )
{	removeObject(getObjectByIndex(idx));
}

// Remove Object
void CFXEvent::removeObject( CFXObject *pRemove )
{	std::list<CFXObject*>::iterator iterRemove;
	ForEachIn(iterRemove, m_FXObjects)
	{	if((*iterRemove) == pRemove)
		{	pRemove->cleanUp();
			m_FXObjects.erase(iterRemove);
			return;
		}
	}
}

// Read XML
void CFXEvent::readXML( TiXmlElement *pElement )
{	TiXmlElement *pChildren = NULL;
	IterateXML(pChildren,pElement)
	{	if(pChildren->Value())
		{	// Path System
			if(string(pChildren->Value()) == "paths")
			{	getPathSystem()->readXML(pChildren);
			}
			// Object
			else if(string(pChildren->Value()) == "object")
			{	if(pChildren->Attribute("type"))
				{	std::string strType = pChildren->Attribute("type");
					CFXObject *pObj = NULL;
					if(strType == "CFXObjectSound")
						pObj = new CFXObjectSound(this);
					else if(strType == "CFXObjectMDL")
						pObj = new CFXObjectMDL(this);
					else if(strType == "CFXObjectWRL")
						pObj = new CFXObjectWRL(this);
					else if(strType == "CFXObjectCamera")
						pObj = new CFXObjectCamera(this);
					
					// Parse xml and add to list
					pObj->readXML(pChildren);
					addObject(pObj,pObj->getStartTime(),pObj->getEndTime());
				}
			}
		}
	}
}

// Write XML
TiXmlElement *CFXEvent::writeXML( void )
{	TiXmlElement *pRoot = new TiXmlElement("data");
	// Parse path system
	pRoot->LinkEndChild(getPathSystem()->writeXML());
	// Parse objects
	std::list<CFXObject*>::iterator iterCurrent;
	ForEachIn(iterCurrent, m_FXObjects)
	{	pRoot->LinkEndChild((*iterCurrent)->writeXML());
	}
	return pRoot;
}