/**
* 3D Hud Manager
* @version 12-26-03
* @author Joseph 'Pcjoe' Florencio
*/
#include "hud.h"
#include "cl_util.h"
#include "parsemsg.h"
#include "studio_util.h"
#include "r_studioint.h"
#include "com_model.h"
#include "studio.h"
#include "studio_util.h"
#include "r_studioint.h"
#include "StudioModelRenderer.h"
#include "GameStudioModelRenderer.h"
#include "demo_api.h"
extern CGameStudioModelRenderer g_StudioRenderer;
extern void VectorAngles( const float *forward, float *angles );
extern engine_studio_api_t IEngineStudio;
extern vec3_t v_origin;
extern vec3_t g_CamAngles;
extern int cam_thirdperson;

/**
* 3D Hud Class
* @version 12-26-03
* @author Joseph 'Pcjoe' Florencio
*/

// garbage function for temp ent callback
void TentCallback( struct tempent_s *ent, float frametime, float currenttime )
{
}

/*
* Constructor
*/
C3DHud::C3DHud( char *pModel, vec3_t vecMaxs, vec3_t vecMins )
{	C3DHud(IEngineStudio.Mod_ForName(pModel,0), vecMaxs, vecMins);
}

C3DHud::C3DHud( model_s *pModel, vec3_t vecMaxs, vec3_t vecMins )
{	// Set model + maxs/mins
	SetModel(pModel);
	// Convert 2d max/mins to a workable form
	Convert2D(vecMaxs.x, vecMaxs.y);
	Convert2D(vecMins.x, vecMins.y);
	m_vec2DMaxs = vecMaxs;
	m_vec2DMins = vecMins;

	// Set render amt
	m_flRenderAmt = 255;

	// Reset Varibles
	m_blRendered = false;
	m_blFirstPass = false;
	m_flTrans2DBBoxT = 0;
	m_flTrans2DBBoxTS = 0;
	m_flTransRAmtT = 0;
	m_flTransRAmtTS = 0;
	m_flTransRAmt = 0;
	m_pNext = NULL;
	Show();

	// Reset Animation
	StopAnimation();

	// Create entity
	m_pTempEntity = gEngfuncs.pEfxAPI->CL_TentEntAllocCustom(vec3_t(0,0,0), GetModel(), 1, TentCallback);
	m_pTempEntity->die = gHUD.m_flTime + 999999;	// Set die time
	m_pTempEntity->flags = (FTENT_CLIENTCUSTOM|FTENT_PERSIST); // Custom ent, let us handle deleting this
	m_pTempEntity->priority = TENTPRIORITY_HIGH;

	// Add to list
	if(!gHUD.m_3dhud.m_p3DHud)
	{	gHUD.m_3dhud.m_p3DHud = this;
	}
	else
	{	m_pNext = gHUD.m_3dhud.m_p3DHud;
		gHUD.m_3dhud.m_p3DHud = this;
	}
}

/*
* Advance Frame
*/
void C3DHud::AdvanceFrame( void )
{	// Non-animating model
	if(!m_blAnimating)
	{	return;
	}

	m_flFrame += gHUD.m_flTimeDelta * 200;
	if(m_flFrame > 255)
	{	m_flFrame = 0;
	}
}

/*
* Set Animation
*/
void C3DHud::SetAnimation( char *pAnim )
{	m_blAnimating = true;
	m_flFrame = 0;
	m_iSequence = g_StudioRenderer.GetSequenceNumber(pAnim);
}

/*
* Stop Animation
*/
void C3DHud::StopAnimation( void )
{	m_blAnimating = false;
	m_iSequence = 0;
	m_flFrame = 0;
}

/* 
* Resize 2D
*/
void C3DHud::Resize2D( vec3_t vecMaxs, vec3_t vecMins, float flTime )
{	Convert2D(vecMaxs.x, vecMaxs.y);
	Convert2D(vecMins.x, vecMins.y);
	// Transition
	if(flTime)
	{	m_vecTrans2DMaxs = vecMaxs;
		m_vecTrans2DMins = vecMins;
		m_flTrans2DBBoxTS = gHUD.m_flTime;
		m_flTrans2DBBoxT = gHUD.m_flTime + flTime;
	}
	// Instant
	else
	{	m_vec2DMaxs = vecMaxs;
		m_vec2DMins = vecMins;
	}
}

/*
* Set Player Model
*/
void C3DHud::SetPlayerModel( char *pModel )
{	char szModel[64];
	sprintf(szModel,"models/player/%s/%s.mdl", pModel, pModel);
	SetModel(szModel);
}

/*
* Set Model
*/
void C3DHud::SetModel( char *pModel )
{	SetModel(IEngineStudio.Mod_ForName(pModel,0));
}


/*
* Set Model
*/
void C3DHud::SetModel( model_s *pModel )
{	// Set model
	m_pModel = pModel;
	// Get 3d max/mins of model
	g_StudioRenderer.GetMaxMinModel(m_pModel, m_vec3DMaxs, m_vec3DMins);
}

/*
* Set/Fade Renderamt
*/
void C3DHud::SetrenderAmt( float flRenderamt, float flTime )
{	// Transition
	if(flTime)
	{	m_flTransRAmt = flRenderamt;
		m_flTransRAmtTS = gHUD.m_flTime;
		m_flTransRAmtT = gHUD.m_flTime + flTime;
	}
	// Instant
	else
	{	m_flRenderAmt = flRenderamt;
	}
}

/*
* Get 2d maxs/mins (includes transition)
*/
void C3DHud::GetMaxsMins( vec3_t &vecMaxs, vec3_t &vecMins )
{	bool blDone;
	float flTrans = HandleTrans(m_flTrans2DBBoxTS, m_flTrans2DBBoxT, blDone);
	if(blDone)
	{	m_vec2DMaxs = m_vecTrans2DMaxs;
		m_vec2DMins = m_vecTrans2DMins;
	}

	vecMaxs = m_vec2DMaxs + (m_vecTrans2DMaxs - m_vec2DMaxs) * flTrans;
	vecMins = m_vec2DMins + (m_vecTrans2DMins - m_vec2DMins) * flTrans;
}

/*
* Get Render Amt
*/
float C3DHud::GetRenderAmt( void )
{	bool blDone;
	float flTrans = HandleTrans(m_flTransRAmtTS, m_flTransRAmtT, blDone);
	if(blDone)
	{	m_flRenderAmt = m_flTransRAmt;
	}
	return (m_flRenderAmt + (m_flTransRAmt - m_flRenderAmt)*flTrans);
}

/*
* Handle Transition
*/
float C3DHud::HandleTrans( float &flStart, float &flEnd, bool &blDone )
{	blDone = false;
	float flTrans = flEnd;
	if(!flTrans)
	{	flTrans = 0;
	}
	else
	{	if(flTrans <= gHUD.m_flTime)
		{	flEnd = 0;
			flStart = 0;
			flTrans = 0;
			blDone = true;
		}
		else
		{	flTrans -= flStart;
			flTrans = 1 - ((flEnd - gHUD.m_flTime) / flTrans);
		}
	}
	return flTrans;
}

/*
* Convert 2d to workable form
*/
void C3DHud::Convert2D( float &x, float &y )
{	x = ((x / ScreenWidth) * 2 - 1);
	y = -((y / ScreenHeight) * 2 - 1);
}

/*
* Setup Entity Before Render
*/
void C3DHud::SetupEntBeforeRender( void )
{	// Update die time
	m_pTempEntity->die = gHUD.m_flTime + 999999;

	// Udpdate render varibles
	m_blRendered = true;
	if(!m_blFirstPass)
		m_blFirstPass = true;

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

	// Set controller bones
	pEntity->curstate.controller[0] = 127;
	pEntity->curstate.controller[1] = 127;
	pEntity->curstate.controller[2] = 127;
	pEntity->curstate.controller[3] = 127;
	pEntity->latched.prevcontroller[0] = pEntity->curstate.controller[0];
	pEntity->latched.prevcontroller[1] = pEntity->curstate.controller[1];
	pEntity->latched.prevcontroller[2] = pEntity->curstate.controller[2];
	pEntity->latched.prevcontroller[3] = pEntity->curstate.controller[3];

	// Set model
	pEntity->model = GetModel();

	// Set render amt
	pEntity->curstate.renderamt = GetRenderAmt();

	// Set Animation
	if(m_blAnimating)
	{	pEntity->curstate.sequence = m_iSequence;
		pEntity->curstate.frame = m_flFrame;
		pEntity->curstate.gaitsequence = 0;
		// Next Frame
		AdvanceFrame();
	}
	else
	{	pEntity->curstate.sequence = 0;
	}

	// Scale according to height or width
	// Get world positions of screen coords
	vec3_t vecWScreenMaxs, vecWScreenMaxs2, vecWScreenMins, vecWScreenMins2;
	vec3_t vecScreenMaxs, vecScreenMaxs2, vecScreenMins, vecScreenMins2;
	GetMaxsMins( vecScreenMaxs, vecScreenMins );
	vecScreenMaxs2 = vecScreenMaxs;
	vecScreenMins2 = vecScreenMins;
	vecScreenMaxs2.x = vecScreenMins.x;
	vecScreenMins2.y = vecScreenMaxs.y;
	gEngfuncs.pTriAPI->ScreenToWorld(vecScreenMaxs, vecWScreenMaxs);
	gEngfuncs.pTriAPI->ScreenToWorld(vecScreenMins, vecWScreenMins);
	gEngfuncs.pTriAPI->ScreenToWorld(vecScreenMaxs2, vecWScreenMaxs2);
	gEngfuncs.pTriAPI->ScreenToWorld(vecScreenMins2, vecWScreenMins2);

	// Set Origin
	pEntity->origin = vecWScreenMaxs + (vecWScreenMins-vecWScreenMaxs) * 0.5;
	
	// Scale model
	float flHeight = (vecWScreenMaxs2-vecWScreenMins).Length();
	float flWidth = (vecWScreenMaxs-vecWScreenMaxs2).Length();
	if(Get3DHeight() > Get3DWidth())
	{	pEntity->curstate.scale = flHeight / Get3DHeight();
	}
	else
	{	pEntity->curstate.scale = flWidth / Get3DWidth();
	}


	// Get Angles, set entity angles
	vec3_t vecViewAngles;
	vec3_t v_forward;
	if(cam_thirdperson || gEngfuncs.pDemoAPI->IsPlayingback())
	{	vecViewAngles = g_CamAngles;
	}
	else
	{	gEngfuncs.GetViewAngles( (float *)vecViewAngles);
	}
	AngleVectors(vecViewAngles, v_forward, NULL, NULL);
	VectorAngles(-v_forward, pEntity->angles);
}

/*
* After Render Check
*/
void C3DHud::AfterRenderCheck( void )
{	if(!m_blFirstPass)
		return;

	if(!m_blRendered)
	{	m_pTempEntity = gEngfuncs.pEfxAPI->CL_TentEntAllocCustom(vec3_t(0,0,0), GetModel(), 1, TentCallback);
		m_pTempEntity->die = gHUD.m_flTime + 6;	// Set die time
		m_pTempEntity->flags = (FTENT_CLIENTCUSTOM|FTENT_PERSIST); // Custom ent, let us handle deleting this
		m_pTempEntity->priority = TENTPRIORITY_HIGH;
		m_blFirstPass = false;
		ConsolePrint("Recreate temp ent\n");
	}
	m_blRendered = false;
}

/*
* Remove Entity
*/
C3DHud *C3DHud::Remove( void )
{	// Remove temp entity
	m_pTempEntity->die = gHUD.m_flTime;
	m_pTempEntity->flags = 0;

	// Remove from linked list
	C3DHud *pCurrent = gHUD.m_3dhud.m_p3DHud;
	// First in list, remove
	if(pCurrent == this)
	{	gHUD.m_3dhud.m_p3DHud = pCurrent->m_pNext;
		/* uhm... Joe... delete this??
		delete this;
		*/
		return gHUD.m_3dhud.m_p3DHud;
	}

	// Search and delete
	while(pCurrent && pCurrent->m_pNext != this)
	{	pCurrent = pCurrent->m_pNext;
	}
	if(pCurrent)
	{	pCurrent->m_pNext = m_pNext;
		/* uhm... Joe... delete this??
		delete this;
		*/
		return pCurrent->m_pNext;
	}

	return NULL;
}

/*
* End of 3d hud class
*/

int CHud3D::Init(void)
{	m_p3DHud = NULL;

	gHUD.AddHudElem(this);
	m_iFlags |= HUD_ACTIVE;
	return 1;
}


void CHud3D::Reset(void)
{/*	if(gHUD.m_3dhud.m_p3DHud)
	{	while(gHUD.m_3dhud.m_p3DHud = gHUD.m_3dhud.m_p3DHud->Remove()) { }
		gHUD.m_3dhud.m_p3DHud = NULL;
	}*/
}


int CHud3D::VidInit(void)
{	return 1;
}


int CHud3D::Draw(float flTime)
{	return 1;
}