#include "hud.h"
#include "cl_util.h"
#include "parsemsg.h"
#include "velocityparticle.h"
#include "acceleratedparticle.h"
#include "curvedpathparticle.h"
#include "spiritbombparticlesystem.h"
#include "flamethrowerparticlesystem.h"
#include "explosionparticlesystem.h"
#include "collisionparticle.h"
#include "psblobsystem.h"
#include "clientballsystem.h"
#include "bodysplashsystem.h"
#include "../common/particletypes.h"
// Added by Pcjoe
#include "fxscript/fxparticlesystemmanager.h"

DECLARE_MESSAGE(m_FXManager, AddEffect);
DECLARE_MESSAGE(m_FXManager, RemEffect);
DECLARE_MESSAGE(m_FXManager,UpdEffect);

extern cvar_t *cl_explosionsmoke;

using namespace std;
int CHudFXManager::Init(void)
{	HOOK_MESSAGE(AddEffect);
	HOOK_MESSAGE(RemEffect);
	HOOK_MESSAGE(UpdEffect);
	m_iFlags |= HUD_ACTIVE;
	gHUD.AddHudElem(this);
	return 1;
}

int CHudFXManager::VidInit()
{	//precache all particle sprites
	m_hsprSBCharge = LoadSprite("sprites/spiritbombcharge.spr");
	m_hsprDust = LoadSprite("sprites/dust.spr");
	m_hsprFlameThrower = LoadSprite("sprites/flamethrower.spr");
	m_hsprBodySplash = LoadSprite("sprites/bodysplash.spr");
	return 1;
}

void CHudFXManager::InitHUDMap()
{
	//remove all systems
	
	vector<FXSystem*>::iterator s = systems.begin();
 	while (s != systems.end())
 	{
  		FXSystem *temp = *s;
		s=systems.erase(s);
		delete temp;
	}	
}

void CHudFXManager::Think( void )
{	float flTime = gEngfuncs.GetClientTime();//gHUD.m_flTime;

	vector<FXSystem*>::iterator s = systems.begin();
 	while (s != systems.end())
 	{
		if ( (*s)->m_fRemove)
		{
			FXSystem *temp = *s;
			s=systems.erase(s);
			delete temp;
		}
		else
		{	(*s)->think(flTime);
  			s++;
		}
 	}
}

void CHudFXManager::Draw()
{	vector<FXSystem*>::iterator s = systems.begin();
 	while (s != systems.end())
 	{
  		(*s)->draw();
  		s++;
 	}
}

void CHudFXManager::addSystem(FXSystem *s)
{
	systems.push_back(s);
}

int CHudFXManager::MsgFunc_AddEffect(const char *pszName, int iSize, void *pbuf)
{	int iEnt,iRadius=0,iDmg=0,iSpeed=0;
	vec3_t vecOrigin;
	vec3_t vecEnd;
	int iFlareA,iFlareB,iTrail,iWidth,iBend,iLength;

	BEGIN_READ( pbuf, iSize );
	int iType = READ_BYTE();
	switch (iType)
	{
	case SPIRITBOMB: 
		iEnt = READ_SHORT();
		vecOrigin.x = READ_COORD();
		vecOrigin.y = READ_COORD();
		vecOrigin.z = READ_COORD();
		addSystem(new TSpiritBombParticleSystem<VelocityParticle>(iEnt,iType,vecOrigin,m_hsprSBCharge));
		break;
	case EXPLOSION:
		iEnt = READ_SHORT();
		vecOrigin.x = READ_COORD();
		vecOrigin.y = READ_COORD();
		vecOrigin.z = READ_COORD();
		iRadius = READ_SHORT();
		
		if (cl_explosionsmoke->value!=0.0)
		{	addSystem(new TExplosionParticleSystem<Particle>(iEnt,iType,vecOrigin,iRadius,m_hsprDust));
		}
		break;
	case FLAMETHROWER:
		iEnt = READ_SHORT();
		iRadius = READ_SHORT();
		addSystem(new TFlameThrowerParticleSystem<CurvedPathParticle>(iEnt,iType,iRadius,m_hsprFlameThrower));
		break;
	case ROTATECROSSHAIR:
		gHUD.m_Crosshair.m_fRotate = true;
		gHUD.m_Crosshair.m_flRotateTime=gHUD.m_flTime;			
		break;
	case LOCKCROSSHAIR:
		gHUD.m_Crosshair.m_fLock = READ_BYTE();
		break;
	case BEAMBOXCROSSHAIR:
		gHUD.m_Crosshair.m_iBeamEnt = READ_SHORT();
		break;
	case PSBLOB:
		iEnt = READ_SHORT();
		iDmg = READ_LONG();
		iSpeed = READ_LONG();
		addSystem(new PSBlobSystem(iEnt, iType, iDmg, iSpeed));
		break;
	case CLIENTBALL:
		iEnt = READ_SHORT();
		vecOrigin.x = READ_COORD();
		vecOrigin.y = READ_COORD();
		vecOrigin.z = READ_COORD();
		vecEnd.x = READ_COORD();
		vecEnd.y = READ_COORD();
		vecEnd.z = READ_COORD();
		iSpeed = READ_LONG();
		iFlareA = READ_SHORT();
		iFlareB = READ_SHORT();	
		iWidth = READ_SHORT() * 10;
		iLength = READ_SHORT();
		iTrail = READ_SHORT();
		iBend = READ_BYTE();
		addSystem(new ClientBallSystem(iEnt, iType,vecOrigin, vecEnd,iSpeed,iFlareA,iFlareB,iWidth,iLength,iTrail,iBend));
		break;
	case BODYPARTSPLASH:
		iEnt = READ_SHORT();
		addSystem(new TBodySplashSystem<CollisionParticle>(iEnt,iType,m_hsprBodySplash));
		break;/*
		// Pcjoe: Charged turbo
	case CHARGEDTURBO:
		{	cl_entity_t *pEnt = gEngfuncs.GetEntityByIndex(READ_SHORT());
			g_FXPsysManager.createManagedParticleSystem("test_sys3", pEnt);
			g_FXPsysManager.createManagedParticleSystem("test_sys4", pEnt);
			g_FXPsysManager.createManagedParticleSystem("test_sys5", pEnt);
			break;
		}*/
	default:
		break;	
	}
	
	return 1;
}

int CHudFXManager::MsgFunc_UpdEffect(const char *pszName, int iSize, void *pbuf)
{
	BEGIN_READ(pbuf,iSize);
	int iType = READ_BYTE();	
	int iId = READ_SHORT();
	
	//find system (assuming there is only one system for this id/type combination)
	vector<FXSystem*>::iterator s = systems.begin();
	vector<FXSystem*>::iterator e = systems.end();	
	FXSystem *pSystem = NULL;
 	while (s != e)
	{
		if( (*s)->m_iId == iId && (*s)->m_iType == iType)
		{
			pSystem = *s;
			break;
		}
		s++;
	}
	
	if(pSystem)
	{
		//do the update, based on the type
		switch (iType)	
		{
		case FLAMETHROWER:
			int iRadius = READ_SHORT();
			((TFlameThrowerParticleSystem<CurvedPathParticle>*)pSystem)->setRadius(iRadius);
			break;
		}
	}
	else
	{
		ConsolePrint("Error: can't find fx system to update.");
	}
	return 1;
}

void CHudFXManager::RemoveSystem(int type, int id)
{
	//remove systems
	vector<FXSystem*>::iterator s = systems.begin();
	vector<FXSystem*>::iterator e = systems.end();
	
 	while (s != systems.end())
 	{
  		FXSystem *temp = *s;
		if(temp->m_iId == id && (temp->m_iType==type || type==0)) //0 means remove all
		{	s=systems.erase(s);
			delete temp;
		}
		else
		{	s++;
		}
 	}
}

int CHudFXManager::MsgFunc_RemEffect(const char *pszName, int iSize, void *pbuf)
{	BEGIN_READ( pbuf, iSize );
	int iType = READ_BYTE();
	int iId;
	switch(iType)
	{
		case 0: //0 means remove all types
		case SPIRITBOMB:
		case FLAMETHROWER:	
		case PSBLOB:
			iId = READ_SHORT();
			RemoveSystem(iType,iId);
			break;
		case ROTATECROSSHAIR:
			gHUD.m_Crosshair.m_fRotate = false;
			break;
		case BEAMBOXCROSSHAIR:
			gHUD.m_Crosshair.m_iBeamEnt = 0;
			break;
		// Pcjoe: Charged turbo
			/*
		case CHARGEDTURBO:
			iId = READ_SHORT();
			g_FXPsysManager.Remove(iId);
			break;
			*/
		default:
			break;
	}	
	return 1;
}
