//============================================================
// Program:		CGFHUD
// Description:	Heads up display
// Author:		Benjamin Gottemoller
// Website:		http://www.particlefield.com
// Date:		who knows
// Legal:		Licensed under the gnu GPL (see gpl.txt for details)
//============================================================

#include "GameFrameHUD.h"
#include "GameFrameCamera.h"
#include "GameFrameSolarSystem.h"
#include "GameFrame.h"

CGFHUD::CGFHUD()
{
	GFParent = NULL;
	SolarSystem = NULL;
	Camera = NULL;
	NumHUDObjects = 0;
	NumUsed = 0;
	HUDObjects = NULL;
	XPPU = YPPU = 1;
}

CGFHUD::~CGFHUD()
{
	Destroy();
}

int CGFHUD::Initialize(GameFrame *gfparent, CGFCamera *camera, int num_nodes)
{
	GFParent = gfparent;
	Camera = camera;

	XPPU = (float)(GFParent->Width) / 100.0f;
	YPPU = (float)(GFParent->Height) / 100.0f;

	GFParent->D3DDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_ONE);
	GFParent->D3DDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_ONE);

	if(num_nodes > 0)
	{
		if(HUDObjects != NULL) Destroy();

		HUDObjects = new HUD_OBJECT[num_nodes];
		NumHUDObjects = num_nodes;
		NumUsed = 0;
		
		for(int i=0; i<NumHUDObjects; i++)
		{
			memset(&(HUDObjects[i]), 0, sizeof(HUD_OBJECT));
		}

		if(HUDObjects != NULL) return 1;
	}

	return 0;
}

int CGFHUD::AddHUDObject(HUD_OBJECT *obj)
{
	int result = -1;
	if((NumHUDObjects <= 0) || (HUDObjects == NULL) || (GFParent == NULL) || (obj == NULL)) return -1;

	if(NumUsed >= NumHUDObjects)
	{
		int i = 0;
		HUD_OBJECT *tmp = NULL;
		tmp = new HUD_OBJECT[NumHUDObjects * 2];

		if(tmp == NULL) return -1;

		for(i=0; i<NumHUDObjects; i++)
		{
			memcpy(&(tmp[i]), &(HUDObjects[i]), sizeof(HUD_OBJECT));
		}

		for(i=NumHUDObjects; i<(NumHUDObjects * 2); i++)
		{
			memset(&(tmp[i]), 0, sizeof(HUD_OBJECT));
		}

		delete[] HUDObjects;
		HUDObjects = tmp;
		tmp = NULL;
		NumHUDObjects *= 2;
	}

	memcpy(&(HUDObjects[NumUsed]), obj, sizeof(HUD_OBJECT));
	
	result = 1;
	NumUsed++;

	return result;
}

void CGFHUD::Destroy(void)
{
	GFParent = NULL;
	Camera = NULL;
	NumHUDObjects = 0;
	NumUsed = 0;
	SAFE_DELETE_ARRAY(HUDObjects);
}

void CGFHUD::Update(void)
{
	if(GFParent->IsGamePaused || GFParent->IsGameLocked) return;
	if(Camera != NULL) 
	{
		for(int i=0; i<NumUsed; i++)
		{
			HUDObjects[i].data.X = Camera->X + (Camera->Right_Vector.x * HUDObjects[i].X) + 
											   (Camera->Up_Vector.x * GFParent->H2WRatio * HUDObjects[i].Y) + 
											   (Camera->Look_Vector.x * HUDObjects[i].Z);

			HUDObjects[i].data.Y = Camera->Y + (Camera->Right_Vector.y * HUDObjects[i].X) + 
											   (Camera->Up_Vector.y * GFParent->H2WRatio * HUDObjects[i].Y) + 
											   (Camera->Look_Vector.y * HUDObjects[i].Z);

			HUDObjects[i].data.Z = Camera->Z + (Camera->Right_Vector.z * HUDObjects[i].X) + 
											   (Camera->Up_Vector.z * GFParent->H2WRatio * HUDObjects[i].Y) + 
											   (Camera->Look_Vector.z * HUDObjects[i].Z);

			if(!(HUDObjects[i].Flags & HUD_DONT_BILLBOARD_OBJECT))
			{
				HUDObjects[i].data.Yaw		= Camera->Yaw + HUDObjects[i].Yaw;
				HUDObjects[i].data.Pitch	= Camera->Pitch + HUDObjects[i].Pitch;
				HUDObjects[i].data.Roll		= Camera->Roll + HUDObjects[i].Roll;
			}

			if(HUDObjects[i].Modifier != NULL)
			{
				HUDObjects[i].Modifier(&(HUDObjects[i]), GFParent, Camera, SolarSystem);
			}

			if(HUDObjects[i].Modifier2 != NULL)
			{
				HUDObjects[i].Modifier2(&(HUDObjects[i]), GFParent, Camera, SolarSystem);
			}
		}
	}
}

int CGFHUD::Render(void)
{
	if((NumHUDObjects <= 0) || (HUDObjects == NULL) || (GFParent == NULL)) return 0;
	if(GFParent->IsGameLocked) return 0;

	int CustomRenderOnly = 0;

	D3DXVECTOR3 Up_Vector; 
	D3DXVECTOR3 Right_Vector; 
	D3DXVECTOR3 Look_Vector;

	D3DXMATRIX YawMat, PitchMat, RollMat;
	D3DXMATRIX TransMat;
	D3DXMATRIX WorldMat;

	OBJNODE *tmp_node = GFParent->ObjectLib[HUDObjects[0].LibraryIndex];
	int index = tmp_node->VertexLibraryIndex;
	if(index == -1) return 0;
	
	//All objects within the same entity must be in the same VB and have the same FVF
	GFParent->D3DDevice->SetStreamSource(0, GFParent->VertexLib[index], GFParent->VertexLib.GetSize(index));
	GFParent->D3DDevice->SetVertexShader(GFParent->VertexLib.GetFVF(index));
	GFParent->D3DDevice->SetTransform(D3DTS_PROJECTION, &(GFParent->OrthoProjectionMatrix));

	GFParent->D3DDevice->SetRenderState( D3DRS_ZENABLE, FALSE);
	GFParent->D3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE);

	for(int i=0; i<NumUsed; i++)
	{

		CustomRenderOnly = 0;
		if(HUDObjects[i].CustomRender != NULL)
		{
			CustomRenderOnly = HUDObjects[i].CustomRender(this, &(HUDObjects[i]), GFParent, Camera, SolarSystem);			
		}

		if(!CustomRenderOnly)
		{
			Up_Vector = D3DXVECTOR3(0,1,0);
			Right_Vector = D3DXVECTOR3(1,0,0);
			Look_Vector = D3DXVECTOR3(0,0,1);

			D3DXMatrixRotationAxis(&YawMat, &Up_Vector, HUDObjects[i].data.Yaw);
			D3DXVec3TransformCoord(&Look_Vector, &Look_Vector, &YawMat);
			D3DXVec3TransformCoord(&Right_Vector, &Right_Vector, &YawMat);

			D3DXMatrixRotationAxis(&PitchMat, &Right_Vector, HUDObjects[i].data.Pitch);
			D3DXVec3TransformCoord(&Look_Vector, &Look_Vector, &PitchMat);
			D3DXVec3TransformCoord(&Up_Vector, &Up_Vector, &PitchMat);

			D3DXMatrixRotationAxis(&RollMat, &Look_Vector, HUDObjects[i].data.Roll);
			D3DXVec3TransformCoord(&Up_Vector, &Up_Vector, &RollMat);
			D3DXVec3TransformCoord(&Right_Vector, &Right_Vector, &RollMat);

			D3DXMatrixTranslation(&TransMat, HUDObjects[i].data.X, HUDObjects[i].data.Y, HUDObjects[i].data.Z);
			D3DXMatrixIdentity(&WorldMat);

			D3DXMatrixMultiply(&WorldMat, &RollMat, &TransMat);
			D3DXMatrixMultiply(&WorldMat, &PitchMat, &WorldMat);
			D3DXMatrixMultiply(&WorldMat, &YawMat, &WorldMat);

			GFParent->D3DDevice->SetTransform(D3DTS_WORLD, &WorldMat);
			tmp_node = GFParent->ObjectLib[HUDObjects[i].LibraryIndex];
			index = tmp_node->TextureLibraryIndex;
			if(index != -1)
			{
				GFParent->D3DDevice->SetTexture(0, GFParent->TextureLib[index]);
			}

			if(HUDObjects[i].IsColorActive)
			{
				GFParent->D3DDevice->SetTextureStageState(0,D3DTSS_COLORARG2, D3DTA_TFACTOR);
				GFParent->D3DDevice->SetRenderState(D3DRS_TEXTUREFACTOR, HUDObjects[i].Color);
			}
			else
			{
				GFParent->D3DDevice->SetTextureStageState(0,D3DTSS_COLORARG2, D3DTA_CURRENT);
			}

			GFParent->D3DDevice->DrawPrimitive(tmp_node->PrimitiveType, tmp_node->StartVertex, tmp_node->PrimitiveCount);
			GFParent->D3DDevice->SetTexture(0, NULL);
		}
	}

	GFParent->D3DDevice->SetTextureStageState(0,D3DTSS_COLORARG2, D3DTA_CURRENT);
	GFParent->D3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,FALSE);
	GFParent->D3DDevice->SetRenderState( D3DRS_ZENABLE, TRUE);

	GFParent->D3DDevice->SetTransform(D3DTS_PROJECTION, &(GFParent->ProjectionMatrix));

	return 1;
}

/**************************HUD_OBJECT Modifier Section************************/

void YawSpinModifier(HUD_OBJECT *obj, GameFrame *gfparent, CGFCamera *camera, CGFSolarSystem *system)
{
	obj->Yaw += obj->Extra1;
	if(obj->Yaw >= PI2) obj->Yaw = 0;
	if(obj->Yaw <= -PI2) obj->Yaw = 0;
}

void PitchSpinModifier(HUD_OBJECT *obj, GameFrame *gfparent, CGFCamera *camera, CGFSolarSystem *system)
{
	obj->Pitch += obj->Extra1;
	if(obj->Pitch >= PI2) obj->Pitch = 0;
	if(obj->Pitch <= -PI2) obj->Pitch = 0;
}

void RollSpinModifier(HUD_OBJECT *obj, GameFrame *gfparent, CGFCamera *camera, CGFSolarSystem *system)
{
	obj->Roll += obj->Extra1;
	if(obj->Roll >= PI2) obj->Roll = 0;
	if(obj->Roll <= -PI2) obj->Roll = 0;
}

void GlowModifier(HUD_OBJECT *obj, GameFrame *gfparent, CGFCamera *camera, CGFSolarSystem *system)
{
	static UCHAR color_factor = 0;
	static float degree = 0;
	degree += obj->Extra1;
	color_factor = (UCHAR)(106.0f * ((float)cos(degree) + 1.4f));
	obj->Color = D3DCOLOR_ARGB(255, color_factor, color_factor, color_factor);
}

void SunLookModifier(HUD_OBJECT *obj, GameFrame *gfparent, CGFCamera *camera, CGFSolarSystem *system)
{
	if((system != NULL) && (obj != NULL) && (camera != NULL))
	{
		D3DXVECTOR3 cam_to_sun;
		D3DXVECTOR3 look = camera->Look_Vector;
		const SUN *sun = &(system->GetSun(0));
		float cs_angle = 0, fov_angle = 0, mag1 = 0, mag2 = 0, dot = 0;
		int color_factor = 0;

		cam_to_sun = D3DXVECTOR3(sun->X - camera->X, sun->Y - camera->Y, sun->Z - camera->Z);
		
		mag1 = D3DXVec3Length(&look);
		mag2 = D3DXVec3Length(&cam_to_sun);
		dot = D3DXVec3Dot(&look, &cam_to_sun);
		cs_angle = (float)acos(dot / (mag1 * mag2));
		fov_angle = (float)atan(5000.0f / mag2);

		color_factor = (int)(255.0f - cs_angle * (255.0f / fov_angle));
		if(color_factor < 0) color_factor = 0;

		obj->Color = D3DCOLOR_ARGB(255, color_factor, color_factor, (int)(color_factor / 1.1f));
	}
}

int PlanetLocationModifier(CGFHUD *hud, HUD_OBJECT *obj, GameFrame *gfparent, CGFCamera *camera, CGFSolarSystem *system)
{
	if((hud != NULL) && (gfparent != NULL) && (system != NULL) && (obj != NULL) && (camera != NULL))
	{
		D3DXVECTOR3 cam_to_planet;
		D3DXVECTOR3 look = camera->Look_Vector;
		PLANET *planet = NULL;		
		int num_planets = 0;
		float dist_sq = 0;
		float minimum_dist_sq = 0;
		
		OBJNODE *tmp_node = NULL;
		int index = 0;

		D3DXVECTOR3 Up_Vector; 
		D3DXVECTOR3 Right_Vector; 
		D3DXVECTOR3 Look_Vector;

		D3DXMATRIX YawMat, PitchMat, RollMat;
		D3DXMATRIX TransMat;
		D3DXMATRIX WorldMat;

		Up_Vector = D3DXVECTOR3(0,1,0);
		Right_Vector = D3DXVECTOR3(1,0,0);
		Look_Vector = D3DXVECTOR3(0,0,1);

		D3DXMatrixRotationAxis(&YawMat, &Up_Vector, obj->data.Yaw);
		D3DXVec3TransformCoord(&Look_Vector, &Look_Vector, &YawMat);
		D3DXVec3TransformCoord(&Right_Vector, &Right_Vector, &YawMat);

		D3DXMatrixRotationAxis(&PitchMat, &Right_Vector, obj->data.Pitch);
		D3DXVec3TransformCoord(&Look_Vector, &Look_Vector, &PitchMat);
		D3DXVec3TransformCoord(&Up_Vector, &Up_Vector, &PitchMat);

		D3DXMatrixRotationAxis(&RollMat, &Look_Vector, obj->data.Roll);
		D3DXVec3TransformCoord(&Up_Vector, &Up_Vector, &RollMat);
		D3DXVec3TransformCoord(&Right_Vector, &Right_Vector, &RollMat);

		tmp_node = gfparent->ObjectLib[obj->LibraryIndex];
		index = tmp_node->TextureLibraryIndex;
		if(index != -1)
		{
			gfparent->D3DDevice->SetTexture(0, gfparent->TextureLib[index]);
		}

		if(obj->IsColorActive)
		{
			gfparent->D3DDevice->SetTextureStageState(0,D3DTSS_COLORARG2, D3DTA_TFACTOR);
			gfparent->D3DDevice->SetRenderState(D3DRS_TEXTUREFACTOR, obj->Color);
		}
		else
		{
			gfparent->D3DDevice->SetTextureStageState(0,D3DTSS_COLORARG2, D3DTA_CURRENT);
		}

		num_planets = system->GetNumActivePlanets();
		gfparent->D3DDevice->SetTransform(D3DTS_PROJECTION, &(gfparent->ProjectionMatrix));
		for(int i=0; i<num_planets; i++)
		{
			planet = &(system->GetPlanet(i));
			cam_to_planet = D3DXVECTOR3(planet->X - camera->X, planet->Y - camera->Y, planet->Z - camera->Z);
			dist_sq = D3DXVec3LengthSq(&cam_to_planet);
			
			minimum_dist_sq = (194.4277f) * planet->PlanetRadius;
			minimum_dist_sq = minimum_dist_sq * minimum_dist_sq;

			if(dist_sq > minimum_dist_sq)
			{
				D3DXVec3Normalize(&cam_to_planet, &cam_to_planet);

				D3DXMatrixTranslation(&TransMat, camera->X + (80 * cam_to_planet.x), 
												 camera->Y + (80 * cam_to_planet.y), 
												 camera->Z + (80 * cam_to_planet.z));
				D3DXMatrixIdentity(&WorldMat);

				D3DXMatrixMultiply(&WorldMat, &RollMat, &TransMat);
				D3DXMatrixMultiply(&WorldMat, &PitchMat, &WorldMat);
				D3DXMatrixMultiply(&WorldMat, &YawMat, &WorldMat);

				gfparent->D3DDevice->SetTransform(D3DTS_WORLD, &WorldMat);
				gfparent->D3DDevice->DrawPrimitive(tmp_node->PrimitiveType, tmp_node->StartVertex, tmp_node->PrimitiveCount);
			}
		}

		gfparent->D3DDevice->SetTexture(0, NULL);
		gfparent->D3DDevice->SetTransform(D3DTS_PROJECTION, &(gfparent->OrthoProjectionMatrix));
	}

	return 1;
}