/**
* Blob FX
* @version: 9-5-02
* @author Joseph 'Pcjoe' Florencio
*/

#include "hud.h"
#include "parsemsg.h"
#include "const.h"
#include "cl_util.h"
#include "r_efx.h"
#include "pm_defs.h"
#include "pmtrace.h"
#include "event_api.h"
#include "../common/explosioncolors.h"

DECLARE_MESSAGE(m_Blob, BlobStart);
DECLARE_MESSAGE(m_Blob, BlobUpdate);
DECLARE_MESSAGE(m_Blob, BlobKill);

// First Link in linked list
explosion *pBlobExp;

int CHudBlob::Init(void)
{	HOOK_MESSAGE(BlobStart);
	HOOK_MESSAGE(BlobUpdate);
	HOOK_MESSAGE(BlobKill);
	pBlobExp = NULL;
	m_flUpdateTime = 0;
	gHUD.AddHudElem(this);
	m_iFlags |= HUD_ACTIVE;
	return 1;
}

// Only reset on level switches
void CHudBlob::Reset( void )
{
	// Clear all data out
	explosion *pBlob = pBlobExp;

	while(pBlob)
	{	
		pBlob = RemoveBlob( pBlob );
	}
	pBlobExp = NULL;
	m_flUpdateTime = 0;

}

int CHudBlob::VidInit(void)
{	m_hsprBlobWhite = SPR_Load("sprites/blobwhite.spr");
	m_hsprBlobYellow = SPR_Load("sprites/blobyellow.spr");
	return 1;
}

explosion *CHudBlob::FindExplosion( int iIndex )
{	explosion *pBlob = pBlobExp;

	// Lets search through everything... Return null if it doesnt exist
	while(pBlob)
	{	
		if(pBlob->iIndex == iIndex)
		{	return pBlob;
		}
		else
		{	pBlob = pBlob->next;
		}
	}

	return NULL;
}

void CHudBlob::AddExplosion( int iSize, int iIndex, int iType, vec3_t vecOrigin, HSPRITE sprite )
{	explosion *pBlob = new explosion;

	// Setting origin
	pBlob->pos.x = vecOrigin.x;
	pBlob->pos.y = vecOrigin.y;
	pBlob->pos.z = vecOrigin.z;

	pBlob->sprite = sprite;
	pBlob->maxradius = iSize;
	pBlob->radius = iSize / 2;
	pBlob->iIndex = iIndex;
	pBlob->iType = iType;
	pBlob->m_iFrame = 0;
	pBlob->flFlashTime = 0;
	pBlob->explode = false;
	pBlob->expand = false;
	// Blob
	if(iType)
		pBlob->iAlpha = 110;
	// Shield
	else	// TEMP SETTING
		pBlob->iAlpha = 0;

	pBlob->next = NULL;

	// Is the first spot taken? If so, search for an empty slot
	if(!pBlobExp)
	{	pBlobExp = pBlob;
	}
	else
	{
		// Add the exp to the end
		explosion *pCurrent = pBlobExp;

		while(pCurrent->next)
		{	pCurrent = pCurrent->next;
		}
		pCurrent->next = pBlob;
	}
}

void CHudBlob::UpdateExplosion( int iSize, int iIndex )
{
	// Lets find the explosion...
	explosion *pUpdate = FindExplosion ( iIndex );

	// Lets update the explosion
	if(pUpdate)
	{	pUpdate->maxradius = iSize;
	}
}

explosion *CHudBlob::RemoveBlob( explosion *pExp )
{	// Taken from CHudExplosion::RemoveExplosion
	explosion *prev;
	prev = pBlobExp;
	
	//should never happen...
	if (!prev)
	{	return NULL;
	}
	
	//remove 1st explosion
	if (prev == pExp)
	{	pBlobExp = prev->next;
		delete pExp;
		return pBlobExp;
	}
	
	//find explosion
	while (prev->next != pExp)
	{	prev = prev->next;
		//should not happen...
		if (!prev)
		{	return NULL;
		}
	}
	prev->next = pExp->next;
	delete pExp;
	return prev->next;
}

void CHudBlob::AnimateSprite( explosion *pBlob, float flTime )
{
	if(pBlob->m_flFrameTime < flTime - 0.125)
	{	pBlob->m_flFrameTime = flTime;
		// Next Frame in the sprite
		if(pBlob->m_iFrame < SPR_Frames(pBlob->sprite)-1)
		{	pBlob->m_iFrame++;
		}
		// If it played its one lighting animation, kill it
		else
		{	pBlob->m_iFrame = 0;
		}
	}
}

void CHudBlob::Think( void )
{
	// Update all the origins every frame
	explosion *pCurrent = pBlobExp;
	while(pCurrent)
	{
		// Only use this origin if the Shield isnt exploding/dying
		// Blobs dont get their origin updated, they are stationary objects
		if(!pCurrent->explode && !pCurrent->iType )
		{
			cl_entity_t *pClient = gEngfuncs.GetEntityByIndex( pCurrent->iIndex );
			pCurrent->pos = pClient->origin;
		}
		pCurrent = pCurrent->next;
	}

	// Increase size
	if(m_flUpdateTime < gHUD.m_flTime)
	{	double flTimeDiff = gHUD.m_flTime-m_flUpdateTime;
		/*modified by harSens
		m_flUpdateTime = gHUD.m_flTime + 0.01;
		*/
		m_flUpdateTime = gHUD.m_flTime;

		pCurrent = pBlobExp;
		while(pCurrent)
		{	// Set the origin
			cl_entity_t *pEntity = gEngfuncs.GetEntityByIndex(pCurrent->iIndex);

			// Animate sprite
			AnimateSprite(pCurrent, gHUD.m_flTime);

			// Is it a blob, or exploding blob? If its not exploding, do the normal run
			if(!pCurrent->explode)
			{
				if(pCurrent->radius <= pCurrent->maxradius)
				{
					/*modified by harSens
					pCurrent->radius += 2;
					*/
					pCurrent->radius += 2*(flTimeDiff/0.01);

					if(pCurrent->radius >= pCurrent->maxradius)
					{	
						pCurrent->radius = pCurrent->maxradius;
					}
					
					// Ki Shield
					if(!pCurrent->iType)
					{
						pCurrent->iAlpha++;
						if(pCurrent->iAlpha >= 110)
						{
							pCurrent->iAlpha = 110;
							pCurrent->flFlashTime = -1;
						}
						else
						{
							if(pCurrent->flFlashTime < gHUD.m_flTime && pCurrent->flFlashTime != -1)
							{
								pCurrent->flFlashTime = gHUD.m_flTime + gEngfuncs.pfnRandomFloat(0.15, 0.25);
								pCurrent->iAlpha -= 5;
								if(pCurrent->iAlpha < 0)
									pCurrent->iAlpha = 0;
							}
						}
					}
				}
				pCurrent = pCurrent->next;
				continue;
			}
			// Exploding!
			else
			{	// Increase/Decrease size
				if (pCurrent->expand)
				{	if(pCurrent->radius >= pCurrent->maxradius)
					{	pCurrent->expand = false;
						pCurrent->flFlashTime = 0;
					}
					else
					{	pCurrent->radius *= 1.35;
					}
					pCurrent = pCurrent->next;
					continue;
				}
				else if (pCurrent->radius > 1 && pCurrent->iAlpha)
				{	// Blob
					if(pCurrent->iType)
						pCurrent->radius *= 0.895;
					// Shield
					else
					{
						pCurrent->iAlpha-=2;

						if(pCurrent->iAlpha < 0)
						{
							pCurrent->iAlpha = 0;
							pCurrent->flFlashTime = -1;
						}

						if(pCurrent->flFlashTime < gHUD.m_flTime && pCurrent->flFlashTime != -1)
						{
							pCurrent->flFlashTime = gHUD.m_flTime + gEngfuncs.pfnRandomFloat(0.15, 0.25);
							pCurrent->iAlpha += 13;
							if(pCurrent->iAlpha > 110)
								pCurrent->iAlpha = 110;
						}
					}
					pCurrent = pCurrent->next;
					continue;
				}
				else
				{	pCurrent = RemoveBlob( pCurrent );
				}
			}
		}
	}
}

int CHudBlob::MsgFunc_BlobStart(const char *pszName, int iSize, void *pbuf)
{	BEGIN_READ( pbuf, iSize );
	int iType = READ_BYTE();
	vec3_t vecOrigin;
	vecOrigin.x = READ_COORD();
	vecOrigin.y = READ_COORD();
	vecOrigin.z = READ_COORD();
	short iIndex = READ_SHORT();
	short iRadius = READ_SHORT();	
	
	HSPRITE hsprSprite;
	if (iType)
	{
		hsprSprite = m_hsprBlobWhite;//blob
	}
	else
	{
		hsprSprite = m_hsprBlobYellow;//shield
	}
	// Delete any other shields that are attached to the same entity
	explosion *pExp = FindExplosion( iIndex );
	while(pExp)
	{
		pExp = RemoveBlob( pExp );

		if(pExp)
			pExp = FindExplosion( iIndex );
		else
			break;
	}
	
	// Add Explosion to linked list
	AddExplosion( iRadius, iIndex, iType, vecOrigin,hsprSprite );

	return 1;
}

int CHudBlob::MsgFunc_BlobUpdate(const char *pszName, int iSize, void *pbuf)
{	BEGIN_READ( pbuf, iSize );
	short iRadius = READ_SHORT();
	short iIndex = READ_SHORT();

	// Update the exp
	UpdateExplosion( iRadius, iIndex );

	return 1;
}

int CHudBlob::MsgFunc_BlobKill(const char *pszName, int iSize, void *pbuf)
{	BEGIN_READ( pbuf, iSize );
	short iIndex = READ_SHORT();
	short iRadius = READ_SHORT();

	explosion *pExp = FindExplosion( iIndex );
	if(pExp)
	{	// only blob sets a new max radius
		if(pExp->iType)
		{
			pExp->maxradius = iRadius;
			pExp->expand = true;
		}
		// Shield
		else
		{
			pExp->maxradius = pExp->radius;
			pExp->expand = false;
			pExp->flFlashTime = 0;
		}

		pExp->explode = true;
		pExp->iIndex = -1;
	}

	return 1;
}