/**
* FX Object
* @version: 01-29-2005
* @author Joseph 'Pcjoe' Florencio
*/

#include "fxsys.h"
#include "r_studioint.h"
#include "../vgui_TeamFortressViewport.h"
#include "../vgui_fxtoolpanel.h"
#include "../tf_defs.h"
extern engine_studio_api_t IEngineStudio;

/*
* BASE: FX Object
*/
// Constructor
CFXObject::CFXObject( CFXEvent *pEvent )
{	// Reset start/end time
	setStartTime(0);
	setEndTime(0);
	// Reset id
	setID(0);
	// Set FXEvent link
	setFXEvent(pEvent);
	// setup timeline
	getTimelineManager()->setFXObject(this);
	if(pEvent)
		setupTimelines();
}

// Can Use ( if the specified time is within the objects life time )
bool CFXObject::canUse( float flTime )
{	if((flTime < getStartTime()) || (flTime > getEndTime()))
	{	return false;
	}
	return true;
}

// Can Remove (will always return false for fx tool)
bool CFXObject::canRemove( float flTime )
{
	// FX Tool is now integrated into the game, dont delete objects for now
/*	if((getEndTime() > 0) && (flTime > getEndTime()))
	{	return true;
	}*/
	return false;
}

// Think
void CFXObject::Think( float flTime )
{	// Set previous time
	getTimelineManager()->setPrevTime(getTimelineManager()->getTime());
	// Set current time
	getTimelineManager()->setTime(flTime-getStartTime());
	// Run update functions
	getTimelineManager()->updateData();
}

// Add timeline(s)
void CFXObject::setupTimelines( void )
{
}

// Read XML
void CFXObject::readXML( TiXmlElement *pElement )
{	setName(pElement->Attribute("name"));
	setStartTime(atof(pElement->Attribute("stime")));
	setEndTime(atof(pElement->Attribute("etime")));
	// read to timelines
	getTimelineManager()->readXML(pElement->FirstChildElement());
}

// Write XML
TiXmlElement *CFXObject::writeXML( void )
{	TiXmlElement *pObject = new TiXmlElement("object");
	// set trivial
	pObject->SetAttribute("type",getType().c_str());
	pObject->SetAttribute("name",getName().c_str());
	char szTime[32];
	sprintf(szTime,"%f", getStartTime());
	pObject->SetAttribute("stime",szTime);
	sprintf(szTime,"%f", getEndTime());
	pObject->SetAttribute("etime",szTime);
	// Add timelines
	pObject->LinkEndChild( getTimelineManager()->writeXML());
	return pObject;
}

/*
* FX Object Trigger
*/
// Constructor
CFXObjectTrigger::CFXObjectTrigger( CFXEvent *pEvent ) : CFXObject( pEvent )
{
}

/*
* FX Object Sound
*/
// Constructor
CFXObjectSound::CFXObjectSound( CFXEvent *pEvent ) : CFXObject( pEvent )
{	setStream1(NULL);
	setStream2(NULL);
	setChannel1(0);
	setChannel2(0);

	if(pEvent)
		setupTimelines();
}

// clean up ( called on delete )
void CFXObjectSound::cleanUp( void )
{	resetStreams();
	ConsolePrint("hahaha bitches!\n");
}

// Think
void CFXObjectSound::Think( float flTime )
{	CFXObject::Think(flTime);
	// Reset streams since we're not playing
/*	if((gViewPort->m_pFXToolPanel->getTimeline()->getTimelineMeter()->getPlayStatus() != TIMELINE_PLAY) &&
		(getStream1() || getStream2()))
	{	resetStreams();
	}*/
}

// Reset Streams
void CFXObjectSound::resetStreams( void )
{	if (getStream1())
	{	g_FMODWrapper.FWRAPPER_Stream_Stop(getStream1());
		g_FMODWrapper.FWRAPPER_Stream_Close(getStream1());
		setStream1(NULL);
		ConsolePrint("ftw bitch 1\n");
	}
	if (getStream2())
	{	g_FMODWrapper.FWRAPPER_Stream_Stop(getStream2());
		g_FMODWrapper.FWRAPPER_Stream_Close(getStream2());
		setStream2(NULL);
		ConsolePrint("ftw bitch 2\n");
	}
	setChannel1(0);
	setChannel2(0);
}

// Add timeline(s)
void CFXObjectSound::setupTimelines( void )
{	getTimelineManager()->addTimeline(new CTimeSoundStream1);
	getTimelineManager()->addTimeline(new CTimeVolume1);
	getTimelineManager()->addTimeline(new CTimeSoundStream2);
	getTimelineManager()->addTimeline(new CTimeVolume2);
}

// Read XML
void CFXObjectSound::readXML( TiXmlElement *pElement )
{	CFXObject::readXML( pElement );
}

// Write XML
TiXmlElement *CFXObjectSound::writeXML( void )
{	TiXmlElement *pObject = CFXObject::writeXML();
	return pObject;
}

/*
* FX Object 3D
*/
// Constructors
CFXObject3D::CFXObject3D( CFXEvent *pEvent ) : CFXObject( pEvent )
{	// reset path data
	setPath(0);
	setDistance(0);
	// Setup timelines
	if(pEvent)
		setupTimelines();
}

// Think
void CFXObject3D::Think( float flTime )
{	CFXObject::Think(flTime);
	updateLocalPositionalData();
}

// Update Local Positional Data
void CFXObject3D::updateLocalPositionalData( void )
{	// No path, clear local position and use base position
	if(!getPath())
	{	setPosition(makePosFinal(vec3_t(0,0,0)));
		return;
	}

	// Get path manager
	CPathManager *pPathManager = (getPathSystem() ? getPathSystem()->getManager(getPath()) : NULL);
	// Valid path
	if(pPathManager)
	{	// Cap off distance
		float flTotalDistance = pPathManager->getLength();
		if(getDistance() > flTotalDistance)
		{	setDistance(flTotalDistance);
		}
		// Set origin
		setPosition(pPathManager->getPointAtDistance(getDistance()));
	}
	// invalid path, clear local position and use base position
	else
	{	setPosition(makePosFinal(vec3_t(0,0,0)));
	}

	// Iterate through channels and set position
	std::map<int,int>::iterator iterStart = getChannels()->begin();
	while(iterStart != getChannels()->end())
	{	// Channel was deleted, remove
		if(!g_FMODWrapper.FWRAPPER_3D_SetAttributes(iterStart->second, getPosition(), NULL))
		{	iterStart = getChannels()->erase(iterStart);
			continue;
		}

		iterStart++;
	}
}

// Add timeline(s)
void CFXObject3D::setupTimelines( void )
{	getTimelineManager()->addTimeline(new CTimePosition);
	getTimelineManager()->addTimeline(new CTimeRotation);
	getTimelineManager()->addTimeline(new CTimeSound);
}

// Read XML
void CFXObject3D::readXML( TiXmlElement *pElement )
{	CFXObject::readXML( pElement );
}

// Write XML
TiXmlElement *CFXObject3D::writeXML( void )
{	TiXmlElement *pObject = CFXObject::writeXML();
	return pObject;
}

// Get Path System
CPathSystem	*CFXObject3D::getPathSystem( void )
{	return (getFXEvent() ? (getFXEvent()->getPathSystem()) : NULL);
}

// Make local position final (global pos)
vec3_t CFXObject3D::makePosFinal( vec3_t vecPos )
{	return (getFXEvent() ? getFXEvent()->makePosFinal(vecPos) : vec3_t(0,0,0));
}

// Make local rotation final (global ang)
CQuaternion CFXObject3D::makeAngFinal( CQuaternion qAngle )
{	return (getFXEvent() ? getFXEvent()->makeAngFinal(qAngle) : CQuaternion());
}

// Channel Exist (channel index), return channel
int CFXObject3D::channelExist( int iChannelIndex )
{	std::map<int,int>::iterator iterFind = getChannels()->find(iChannelIndex);
	if(iterFind != getChannels()->end())
	{	return iterFind->second;
	}
	return 0;
}

// Create channel with specified channel index and channel
void CFXObject3D::createChannel( int iChannelIndex, int iChannel )
{	(*getChannels())[iChannelIndex] = iChannel;
}

/*
* FX Object Camera
*/
// Constructor
CFXObjectCamera::CFXObjectCamera( CFXEvent *pEvent ) : CFXObjectWRL( pEvent )
{	setRenderMode(kRenderNormal);
	// Setup timelines
	if(pEvent)
		setupTimelines();
}

// Can Use ( specified life span, and it's owned by the local user )
bool CFXObjectCamera::canUse( float flTime )
{	if(CFXObjectWRL::canUse(flTime))
	{	cl_entity_t *pLocal = gEngfuncs.GetLocalPlayer();
		// Belongs to local player, allow them to use the camera
		if(pLocal && getFXEvent() && getFXEvent()->getIndex() == pLocal->index)
		{	return true;
		}
	}
	return false;
}

// Add timeline(s)
void CFXObjectCamera::setupTimelines( void )
{	getTimelineManager()->addTimeline(new CTimeRadialBlur);
}

// Render
void CFXObjectCamera::Render( void )
{	// Only render if we're outside camera view
	CFXTimeline_VGUI *pTimeline = gViewPort->m_pFXToolPanel->getTimeline();
	if(pTimeline && (!pTimeline->getInCamera()))
	{	CFXObjectWRL::Render();
	}
}

/*
* FX Object Render
*/
// Constructor
CFXObjectRender::CFXObjectRender( CFXEvent *pEvent ) : CFXObject3D( pEvent )
{	// Set various render info
	setSize(0);
	setAlpha(0);
	setRenderMode(kRenderTransAdd);
	getColor()->setColor(255,255,255);
	// Reset render flag
	setRenderFlag(true);
	// Setup timelines
	if(pEvent)
		setupTimelines();
}

// Add timeline(s)
void CFXObjectRender::setupTimelines( void )
{	getTimelineManager()->addTimeline(new CTimeAlpha);
	getTimelineManager()->addTimeline(new CTimeColor);
	getTimelineManager()->addTimeline(new CTimeSize);
}

// Read XML
void CFXObjectRender::readXML( TiXmlElement *pElement )
{	CFXObject3D::readXML( pElement );
}

// Write XML
TiXmlElement *CFXObjectRender::writeXML( void )
{	TiXmlElement *pObject = CFXObject3D::writeXML();
	return pObject;
}

/*
* FX Object Sprite
*/

// Constructor
CFXObjectSprite::CFXObjectSprite( CFXEvent *pEvent ) : CFXObjectRender( pEvent )
{
}

// Add timeline(s)
void CFXObjectSprite::setupTimelines( void )
{
}

// Render
void CFXObjectSprite::Render( void )
{
}

/*
* FX Object Particle System
*/
// Constructor
CFXObjectParticle::CFXObjectParticle( CFXEvent *pEvent ) : CFXObjectRender( pEvent )
{
}

// Add timeline(s)
void CFXObjectParticle::setupTimelines( void )
{
}

// Render
void CFXObjectParticle::Render( void )
{
}

/*
* FX Object MDL
*/
#include "../mdlcontrol.h"
#include "../mdlblur.h"
extern CModelControlManager gModelControl;
extern CModelBlurManager gModelBlur;
// Constructor
CFXObjectMDL::CFXObjectMDL( CFXEvent *pEvent ) : CFXObjectRender( pEvent )
{	setModelControl(NULL);
	setModelBlur(NULL);
	setTempEntity(NULL);
	setIndex(0);
	// Setup timelines
	if(pEvent)
		setupTimelines();
}

// Update Local Positional Data
void CFXObjectMDL::updateLocalPositionalData( void )
{	CFXObject3D::updateLocalPositionalData();
	// Set model position and angle
	CMDLControl *pCtrl = getModelControl();
	if(pCtrl)
	{	pCtrl->setPosition(getPosition());
		pCtrl->setAngle(makeAngFinal(*getRotation()).QuaternionToEuler());
	}
}

// Add timeline(s)
void CFXObjectMDL::setupTimelines( void )
{	getTimelineManager()->addTimeline(new CTimedSequence);
	getTimelineManager()->addTimeline(new CTimedFrameMulti);
	getTimelineManager()->addTimeline(new CTimedBodyPart);
	getTimelineManager()->addTimeline(new CTimedModelBlur);
}

// Read XML
void CFXObjectMDL::readXML( TiXmlElement *pElement )
{	if(pElement->Attribute("model"))
		setupModel(pElement->Attribute("model"));
	CFXObjectRender::readXML( pElement );
}

// Write XML
TiXmlElement *CFXObjectMDL::writeXML( void )
{	TiXmlElement *pObject = CFXObjectRender::writeXML();
	if(getModelName() != "")
		pObject->SetAttribute("model", getModelName().c_str());
	else
		pObject->SetAttribute("model", "local player");
	return pObject;
}

// Setup Model ( attach to existing model )
void CFXObjectMDL::setupModel( int idx )
{	cl_entity_t *pEnt = gEngfuncs.GetEntityByIndex(idx);
	if(pEnt)
	{	CMDLControl *pCtrl = gModelControl.addModelControl(idx);
		pCtrl->setModel(pEnt->model);
		setModelControl(pCtrl);
		setIndex(idx);
	}	
}
int iStartIdx = -1;
// Setup Model ( create new temp entity
void CFXObjectMDL::setupModel( std::string strModel )
{	if(strModel == "local player")
	{	cl_entity_t *pLocal = gEngfuncs.GetLocalPlayer();
		if(pLocal)
		{	setupModel(pLocal->index);
		}
		return;
	}
	setModelName(strModel);
	model_s *pModel = IEngineStudio.Mod_ForName(strModel.c_str(),0);
	if(!pModel)
	{	return;
	}
	
	TEMPENTITY *pTempEntity = gEngfuncs.pEfxAPI->CL_TempEntAlloc(vec3_t(0,0,100), pModel);
	if(!pTempEntity)
	{	return;
	}
	pTempEntity->die = gEngfuncs.GetClientTime()+999999;
	pTempEntity->fadeSpeed = 0;
	pTempEntity->flags = 0;

	// shorter code...
	cl_entity_t *pEntity = &pTempEntity->entity;

	// Set Index
	pEntity->baseline.movetype = iStartIdx;
	setIndex(iStartIdx);
	iStartIdx--;

	// Set model control
	CMDLControl *pCtrl = gModelControl.addModelControl(pEntity->baseline.movetype);
	pCtrl->setModel(pModel);
	setModelControl(pCtrl);

	// Set temp entity
	setTempEntity(pTempEntity);
}

// clean up ( called on delete )
void CFXObjectMDL::cleanUp( void )
{	// Remove model control
	if(getModelControl())
		gModelControl.removeModelControl(getModelControl()->getIndex());
	// Remove model blur
	if(getModelBlur())
		gModelBlur.removeBlur(getModelBlur()->getIndex());
	// Remove temp entity
	if(getTempEntity())
		getTempEntity()->die = 0;
}

/*
* FX Object WRL
*/
// Constructor
CFXObjectWRL::CFXObjectWRL( CFXEvent *pEvent ) : CFXObjectRender( pEvent )
{	// reset model and texture
	setModel(NULL);
	setTexture(NULL);
	// Setup timelines
	if(pEvent)
		setupTimelines();
}

// Add timeline(s)
void CFXObjectWRL::setupTimelines( void )
{
}

// Read XML
void CFXObjectWRL::readXML( TiXmlElement *pElement )
{	if(pElement->Attribute("model"))
		setModelName(pElement->Attribute("model"));
	if(pElement->Attribute("texture"))
		setTextureName(pElement->Attribute("texture"));
	// Update model data
	updateModelData();

	CFXObjectRender::readXML( pElement );
}

// Write XML
TiXmlElement *CFXObjectWRL::writeXML( void )
{	TiXmlElement *pObject = CFXObjectRender::writeXML();
	pObject->SetAttribute("model", getModelName().c_str());
	pObject->SetAttribute("texture", getTextureName().c_str());
	return pObject;
}

// update model data ( find model/texture using string data )
void CFXObjectWRL::updateModelData( void )
{	CFXPrecache *pm = NULL;
	CFXPrecache *pt = NULL;

	// Get Data
	pm = g_ParseMain.getPrecache(getModelName());
	pt = g_ParseMain.getPrecache(getTextureName());

	if(pm)
	{	setModel(pm->getWRL());
	}
	else
	{	string strPrint = "\"";
		strPrint += getModelName() + "\" is not a valid WRL file!\n";
		ConsolePrint(strPrint.c_str());
		// Disable Render
		setRenderFlag(false);
	}

	if(pt)
	{	setTexture(pt->getSPR());
	}
	else
	{	string strPrint = "\"";
		strPrint += getTextureName() + "\" is not a valid SPR file!\n";
		ConsolePrint(strPrint.c_str());
		// Disable Render
		setRenderFlag(false);
	}
}

// Render
void CFXObjectWRL::Render( void )
{	VRMLData render;
	RotateVRML(render, (*getModel()), makeAngFinal((*getRotation())).QuaternionToEuler());
	ScaleVRML(render, render, getSize());
	float flAlphaP = getAlpha()/255;
	DrawVRML(render, getPosition(), (*getTexture()), getRenderMode(), TRI_NONE, 0, getColor()->getR()*flAlphaP, getColor()->getG()*flAlphaP, getColor()->getB()*flAlphaP, getAlpha());
}