//#include <windows.h>
#include <D3dx9.h>
#include <D3d9.h>
#include <ctime>
#include <D3dx9math.h>

#include "mesh.h"
#include "terrain.h"
#include "utils.h"
#include "llist.h"
#include "ball.h"
#include "forest.h"
#include "messages.h"
#include "collisiondetector.h"

#include "Flock.h"
#include "ScreenShotCapture.h"

#define LANDSIZE 40

IDirect3D9*			g_pD3D			= NULL;
IDirect3DDevice9*	g_pd3dDevice	= NULL;
Terrain* TerrainObject;
//Ball* BouncingBall;
Forest* MainForest;
CollisionDetector* CollisionListener;
LList<Entity*> EntityList;
MessageManager MsgDispatcher;
bool Keys[256];
bool MouseDown = false;
bool interactive = false;
bool reset = false;
POINT MousePosition;

float CamUDPos;
float CamLRPos;
float CamRollPos;
D3DXVECTOR3 look_vector;
D3DXVECTOR3 up_vector;
D3DXVECTOR3 right_vector;
D3DXVECTOR3 camera_position;

////////////////////////////////////////////////////////////////////////////////////
// Joel's Camera Stuff
// These variables hold the current position of the camera, in polar coordinates.
float gTheta	= float(PI);
float gPhi		= float( PI / 4 );
float gRho		= 30;
D3DXVECTOR3 gCameraCenter = D3DXVECTOR3(LANDSIZE/2,LANDSIZE/2,0);

// Joel's global input stuff
bool bKeyboardInputChanged = false; // set to true on a keyup or keydown; cleared when input is processed

// Joel's Flock stuff
Flock* g_pFlock = NULL;

// Joel's Font stuff
#include "font/Font.h"
Font* small_font;

// Joel's Profiling stuff
#include "profiler/profiler_lowlevel.h"
#include "profiler/profiler_highlevel.h"
Profile_Tracker *profile_tracker;
////////////////////////////////////////////////////////////////////////////////////

#define SCREENWIDTH 800
#define SCREENHEIGHT 600

void SetupLights()
{
		// Set up a light
	D3DXVECTOR3 vecDir;
	D3DLIGHT9 light;
	ZeroMemory( &light, sizeof(D3DLIGHT9) );
	light.Type       = D3DLIGHT_POINT;
	light.Type       = D3DLIGHT_DIRECTIONAL;
	light.Diffuse.r  = 1.0f;
	light.Diffuse.g  = 1.0f;
	light.Diffuse.b  = 1.0f;

  	//light.Attenuation1 = .05f;
	//light.Range       = LANDSIZE;
	//light.Position.x = LANDSIZE;
	//light.Position.y = LANDSIZE;
	//light.Position.z = LANDSIZE/2;
	//D3DXVec3Normalize( (D3DXVECTOR3*)&light.Direction, &D3DXVECTOR3(light.Position.x, light.Position.y, light.Position.z) );
	D3DXVECTOR3 SourceUnnormalizedVector = D3DXVECTOR3(0,0,-1);
	D3DXVec3Normalize( (D3DXVECTOR3*)&light.Direction, &SourceUnnormalizedVector);
	g_pd3dDevice->SetLight( 0, &light );
	g_pd3dDevice->LightEnable( 0, TRUE);

	g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE );
	g_pd3dDevice->SetRenderState( D3DRS_AMBIENT, 0x00646464 );
	g_pd3dDevice->SetRenderState( D3DRS_SPECULARENABLE, FALSE);

	//Enable wireframe rendering
	//g_pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME);
}

void ProcessMouse()
{
	//if(MouseDown)
	//{
	//	DWORD FaceHit = 0;
	//	Mesh* CheckMeshes[2];        
	//	TerrainObject->GetMeshes(CheckMeshes);
	//	if(FindClickedFace(g_pd3dDevice,SCREENWIDTH, SCREENHEIGHT, CheckMeshes[0]->GetD3DXMesh(), MousePosition, FaceHit))
	//	{
	//		TerrainObject->SetSelectedFace(FaceHit);
	//	}
	//}

}

void ProcessKeys()
{
	//////////////////////////////////////////////////////////////
	// Joel's Camera Stuff

	if(Keys[VK_NEXT])
	{
		gRho += 1;
	}
	if(Keys[VK_PRIOR])
	{
		gRho -= 1;
	}

	if(Keys[VK_SPACE])
	{
		interactive = !interactive;
		reset = true;
	}
	//position the camera at the average 3d position of the flock
	gCameraCenter = g_pFlock->ave3Pos(0);
	//orient the camera to the average direction of the flock
	//Vector2D vel = g_pFlock->aveVel(0);
	//gTheta = vel.angle() + ( PI / 2 );
	//gTheta = atan( vel.y / vel.x );
	//gPhi = acos( 1 / gRho ) - ( PI / 4 );
	
	//Vector2D grad(TerrainObject->GetGradient(gCameraCenter.x, gCameraCenter.y));

		
	//if(Keys[VK_LEFT])
	//{
	//	gCameraCenter.x += 1;
	//}
	//if(Keys[VK_RIGHT])
	//{
	//	gCameraCenter.x -= 1;
	//}
	//if(Keys[VK_UP])
	//{
	//	gCameraCenter.y += 1;
	//}
	//if(Keys[VK_DOWN])
	//{
	//	gCameraCenter.y -= 1;
	//}
	if(Keys['G'] && bKeyboardInputChanged)
	{
		if(g_pFlock->team_goals[0].x < 10)
			g_pFlock->team_goals[0] = Vector2D(LANDSIZE-5,LANDSIZE-5);
		else
			g_pFlock->team_goals[0] = Vector2D(5,5);
	}
	
	//////////////////////////////////////////////////////////////

	/*
	if (Keys[VK_LEFT])
	{
		//CamLRPos-=0.003f;
		CamLRPos=-0.03f;
	}
	if (Keys[VK_RIGHT])
	{
		//CamLRPos+=0.003f;
		CamLRPos=0.03f;
	}
	if(Keys[VK_END])
	{
		CamRollPos=-0.03f;
	}
	if(Keys[VK_DELETE])
	{
		CamRollPos=0.03f;
	}
	*/


//	if (Keys[VK_UP])
//	{
//		/*float udposx=(float)(PI/2.0f-CamUDPos);
//		float lrposx=CamLRPos;
//		CamPosition.Z+=(float)sin(udposx) *(float)cos(lrposx) * 0.06f;
//		CamPosition.X+=(float)sin(udposx) *(float)sin(lrposx) * 0.06f;
//		CamPosition.Y+=(float)cos(udposx) * 0.06f;*/
//		//TODO: Make the speed scale based on time elapsed
//		float speed = 1.0f;
//		camera_position.x+=speed*look_vector.x;
//		camera_position.y+=speed*look_vector.y;
//		camera_position.z+=speed*look_vector.z;
//	}
//	if (Keys[VK_DOWN])
//	{
//		/*float udposx=(float)(PI/2.0f-CamUDPos);
//		float lrposx=CamLRPos;
//		CamPosition.Z-=(float)sin(udposx)*(float)cos(lrposx) * 0.06f;
//		CamPosition.X-=(float)sin(udposx)*(float)sin(lrposx) * 0.06f;
//		CamPosition.Y-=(float)cos(udposx) * 0.06f;*/
//		float speed = 1.0f;
//		camera_position.x-=speed*look_vector.x;
//		camera_position.y-=speed*look_vector.y;
//		camera_position.z-=speed*look_vector.z;
//	}
	/*
	if (Keys[VK_NEXT])
	{
		CamUDPos=0.03f;
	}
	
	if (Keys[VK_PRIOR])
	{
		CamUDPos=-0.03f;
	}

	if(!Keys[VK_PRIOR]&&!Keys[VK_NEXT])
	{
		CamUDPos=0;
	}
	if(!Keys[VK_LEFT]&&!Keys[VK_RIGHT])
	{
		CamLRPos=0;
	}
	if(!Keys[VK_END]&&!Keys[VK_DELETE])
	{
		CamRollPos=0;
	}
	*/
	static bool pressed = false;
	/*
	if(Keys['W'])
	{
		//TerrainObject->Erode(5000);
		TerrainObject->DropWater(10);
		//TerrainObject->FIRErode(.5);
		//TerrainObject->Rain();
	}
	if(Keys['F'])
	{
		for(int i=0;i<100;i++)
		{	
			TerrainObject->FlowWater();
		}
	}
	if(Keys['A'])
	{
		TerrainObject->AddWater(3, .5);
	}
	if(Keys[VK_SPACE])
	{
		TerrainObject->Erode(5000);
	//	//TerrainObject->DropWater(10);
	//	//TerrainObject->FIRErode(.5);
	//	//TerrainObject->Rain();
	}

	static int RiverX = RandInt(LANDSIZE);
	static int RiverY = RandInt(LANDSIZE);
	*/
	//if(Keys['N'])
	//{
	//	RiverX = RandInt(LANDSIZE);
	//	RiverY = RandInt(LANDSIZE);
	//}
	//if(Keys['R'])
	//{
	//	
	//	TerrainObject->AddWaterToPoint(RiverX, RiverY, .01f);
	//	TerrainObject->FlowWater();
	//}

	//if(Keys['E'])
	//{
	//	if(!pressed)
	//	{
	//		TerrainObject->FIRErode(.1f);
	//		pressed = true;
	//	}
	//}
	//if(!Keys['E'])
	//{
	//	pressed = false;
	//}
/*
	if(Keys['O'])
	{
		BouncingBall->Position.x-=.1f;
	}
	if(Keys['P'])
	{
		BouncingBall->Position.x+=.1f;
	}
*/
	if(Keys['U'])
	{
		MainForest->Grow();
	}

	bKeyboardInputChanged = false;
}


void UpdateCamera()
{
	/*
	//Renormalize the view basis vectors to eliminate
	// errors due to floating point rounding
	D3DXVec3Normalize(& look_vector,& look_vector);
	D3DXVec3Cross(& right_vector,& up_vector,& look_vector);
	D3DXVec3Normalize(& right_vector,& right_vector);
	D3DXVec3Cross(& up_vector, & look_vector, & right_vector);
	D3DXVec3Normalize(& up_vector, & up_vector);

	D3DXMATRIX UDMatrix, LRMatrix, RollMatrix;

	D3DXMatrixRotationAxis(& LRMatrix, & up_vector, CamLRPos ); 
	D3DXVec3TransformCoord(& look_vector , & look_vector, & LRMatrix); 
	D3DXVec3TransformCoord(& right_vector, & right_vector, & LRMatrix); 

	D3DXMatrixRotationAxis(& UDMatrix, & right_vector, CamUDPos ); 
	D3DXVec3TransformCoord(& look_vector , & look_vector, & UDMatrix); 
	D3DXVec3TransformCoord(& up_vector, & up_vector, & UDMatrix);

	D3DXMatrixRotationAxis(& RollMatrix, & look_vector, CamRollPos ); 
	D3DXVec3TransformCoord(& right_vector , & right_vector, & RollMatrix); 
	D3DXVec3TransformCoord(& up_vector, & up_vector, & RollMatrix);

	D3DXMATRIX matView;
	D3DXMatrixIdentity( &matView );// defined in d3dutil.h and d3dutil.cpp 
	matView._11 = right_vector.x; matView._12 = up_vector.x; matView._13 = look_vector.x;
	matView._21 = right_vector.y; matView._22 = up_vector.y; matView._23 = look_vector.y;
	matView._31 = right_vector.z; matView._32 = up_vector.z; matView._33 = look_vector.z;
	matView._41 = - D3DXVec3Dot( &camera_position, &right_vector ); // dot product defined in d3dtypes.h
	matView._42 = - D3DXVec3Dot( &camera_position, &up_vector );
	matView._43 = - D3DXVec3Dot( &camera_position, &look_vector );
	*/

	
	D3DXMATRIX matView;
	D3DXMATRIX matTemp;
	D3DXMatrixIdentity( &matView );// defined in d3dutil.h and d3dutil.cpp 

	/*D3DXMatrixLookAtLH( &matView, &D3DXVECTOR3( 0, 0, 0 ), // pos
                                  &D3DXVECTOR3( 0, 1, 0 ), // look at
                                  &D3DXVECTOR3( 0, 0, 1 ) ); // up
*/
	// translate
	D3DXMatrixTranslation( &matTemp, -gCameraCenter.x, -gCameraCenter.y, -gCameraCenter.z);
	D3DXMatrixMultiply( &matView, &matView , &matTemp );

	// rotate
    D3DXMatrixRotationZ( &matTemp, gTheta );
	D3DXMatrixMultiply( &matView, &matView , &matTemp );

	// rotate
    D3DXMatrixRotationX( &matTemp, float(PI + gPhi) );
	D3DXMatrixMultiply( &matView, &matView , &matTemp );

	// back up
	D3DXMatrixTranslation( &matTemp, 0, 0, gRho);
	D3DXMatrixMultiply( &matView, &matView , &matTemp );

	g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );
}

//-----------------------------------------------------------------------------
// Name: SetupMatrices()
// Desc: Sets up the world, view, and projection transform matrices.
//-----------------------------------------------------------------------------
VOID SetupMatrices()
{
    // For our world matrix, we will just leave it as the identity
    D3DXMATRIX matWorld;
    D3DXMatrixIdentity( &matWorld );
 //   D3DXMatrixRotationX( &matWorld, timeGetTime()/1000.0f );
    g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );

    // Set up our view matrix. A view matrix can be defined given an eye point,
    // a point to lookat, and a direction for which way is up. Here, we set the
    // eye five units back along the z-axis and up three units, look at the
    // origin, and define "up" to be in the y-direction.
 //   D3DXMATRIX matView;

 //   //D3DXMatrixLookAtLH( &matView, &D3DXVECTOR3( 0.0f, 0.0f, 20.0f ),
 //   //                              &D3DXVECTOR3( 0.0f, 0.0f, 0.0f ),
 //   //                              &D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) );
 //   D3DXMATRIX Trans;
	//D3DXQUATERNION QRoll;
	//D3DXMATRIX Roll;
	//float Angle;
	//D3DXVECTOR3 Axis;
	//D3DXMatrixIdentity( &matView );

	//float scenerotx = (float)(2*PI - CamUDPos);
	//float sceneroty = (float)(2*PI - CamLRPos);
	//D3DXMatrixTranslation(&Trans, -CamPosition.X,-CamPosition.Y, -CamPosition.Z);
	//D3DXMatrixMultiply(&matView, &matView, &Trans);
	///*D3DXQuaternionRotationYawPitchRoll(&QRoll, sceneroty, scenerotx, 0);
	//D3DXQuaternionToAxisAngle(&QRoll, &Axis, &Angle);
	//D3DXMatrixRotationAxis(&MRoll, &Axis, Angle);
 // 	D3DXMatrixMultiply(&matView, &matView, &MRoll);*/
 // 
	//D3DXMatrixRotationX(&Roll, scenerotx);
	//D3DXMatrixMultiply(&matView, &matView, &Roll);
	//D3DXMatrixRotationY(&Roll, sceneroty);
	//D3DXMatrixMultiply(&matView, &matView, &Roll);
	//
	//
	//
	///*D3DXMatrixRotationYawPitchRoll(&Roll, sceneroty, scenerotx, 0);
	//D3DXMatrixTranslation(&Trans, -CamPosition.X,-CamPosition.Y, -CamPosition.Z);
	//D3DXMatrixMultiply(&matView, &Trans, &Roll);
	//D3DXMatrixMultiply(&matView, &Roll, &Trans);*/
	//g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );

    //D3DXMatrixRotationY( &matWorld, timeGetTime()/500.0f );
	//D3DXMatrixRotationY( &matWorld, (float)timeGetTime()/5000.f );
    

    // For the projection matrix, we set up a perspective transform (which
    // transforms geometry from 3D view space to 2D viewport space, with
    // a perspective divide making objects smaller in the distance). To build
    // a perpsective transform, we need the field of view (1/4 pi is common),
    // the aspect ratio, and the near and far clipping planes (which define at
    // what distances geometry should be no longer be rendered).
    D3DXMATRIX matProj;
    D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.0f, 1.0f, 300.0f );
//	D3DXMatrixOrthoLH( &matProj, 4, 4, -10, 10);
    g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
}

HRESULT InitializeGeometry()
{
	/*CamPosition.X = LANDSIZE/2;
	CamPosition.Y = LANDSIZE/2;
	CamPosition.Z = LANDSIZE/2;*/
	CamUDPos = 0;
	CamLRPos = 0;
	look_vector=D3DXVECTOR3(0.0f,0.0f,-1.0);
	up_vector=D3DXVECTOR3(0.0f,1.0f,0.0f);
	//right_vector=D3DXVECTOR3(1.0f,0.0f,0.0f);
	camera_position=D3DXVECTOR3(LANDSIZE/2,LANDSIZE/2,1.5*LANDSIZE);

	return S_OK;
}

///////////////////////////
// Joel's temporary addition
#include <vector>
using namespace std;
struct eqset
{
	vector<int> s;
};
void set_equal(vector<eqset>& table, int a, int b)
{
	
	vector<eqset>::iterator i;
	vector<int>::iterator j;

	bool afound = false, bfound = false;
	vector<eqset>::iterator ait;
	vector<eqset>::iterator bit;

	for(i = table.begin(); i != table.end(); i++)
	{
		for(j = i->s.begin(); j != i->s.end(); j++)
		{
			if(a == *j)
			{
				afound = true;
				ait = i;
			}
			if(b == *j)
			{
				bfound = true;
				bit = i;
			}
		}
	}

	if(afound && bfound)
	{
		//remove both eqsets and insert the union of the two

		//eqset newset = *ait;

		for(j = bit->s.begin(); j != bit->s.end(); j++)
		{
			bool already_exists = false;
			vector<int>::iterator k;
			for(k = ait->s.begin(); k != ait->s.end(); k++)
			{
				if(*j == *k)
					already_exists = true;
			}
			if(!already_exists)
				ait->s.push_back(*j);
		}

		//table.erase(ait);
		table.erase(bit);
		//table.push_back(newset);
	}
}

int whichset(vector<eqset>& table, int a)
{
	int i,j;
	for(i=0; i<table.size(); i++)
	{
		for(j=0; j<table[i].s.size(); j++)
		{
			if(a == table[i].s[j])
				return i;
		}
	}
	return 0;
}
///////////////////////////



HRESULT InitializeObjects()
{
	MsgDispatcher.SetEntityList(&EntityList);
	
	TerrainObject = new Terrain();
	TerrainObject->Initialize(LANDSIZE, LANDSIZE);
	//TerrainObject->MountainBuild(24, 20);

	//TerrainObject->FaultBuild(3, 64);
	//TerrainObject->MountainBuild(20, 12);
	//TerrainObject->FoldBuild(5, 5);
	TerrainObject->NewRandomSmoothTerrain();
	TerrainObject->NormalizeHeight();
/*
	////////////////////////////////////////////////////////
	// TEMP: test blob labeling
	int i,j;
	float threshold = TerrainObject->getMAXHEIGHT() * 0.6666f;
	int len = (int)TerrainObject->GetLength();
	int wid = (int)TerrainObject->GetWidth();
	int color_array[50][50];
	int current_color = 1;
	int next_color = 2;
	bool in_blob = false;
	vector<eqset> equality_table;

	for(i=0; i<len; i++)
	{
		for(j=0; j<wid; j++)
		{
			if(TerrainObject->GetHeight(i,j) > threshold)
			{
				if(i>0 && color_array[i-1][j] > 0) //TerrainObject->GetHeight(i-1,j) > threshold)
				{

					color_array[i][j] = color_array[i-1][j];

					if(in_blob)
					{
						if(color_array[i-1][j] != current_color)
						{
							//set current_color in equality table
							set_equal(equality_table, color_array[i-1][j], current_color);
						}
					}
					else
					{
						in_blob = true;
					}

					current_color = color_array[i-1][j];
					
				}
				else
				{
					if(!in_blob)
					{
						eqset newset;
						newset.s.push_back(current_color);
						equality_table.push_back(newset);
						in_blob = true;
					}	
					color_array[i][j] = current_color;
				}
				
			}
			else
			{
				color_array[i][j] = 0;
				if(in_blob)
				{
					current_color = next_color;
					next_color++;
					in_blob = false;
				}
			}
		}
		//if(in_blob)
		//{
			current_color = next_color;
			next_color++;
			in_blob = false;
		//}
	}

	for(i=0; i<len; i++)
		for(j=0; j<wid; j++)
		{
			if(color_array[i][j])
			{
				int c = whichset(equality_table, color_array[i][j]);
				switch(c)
				{
				case 0:
					TerrainObject->SetVertexColor(i,j, D3DCOLOR_XRGB(0, 255, 0));
					break;

				case 1:
					TerrainObject->SetVertexColor(i,j, D3DCOLOR_XRGB(0, 255, 255));
					break;

				case 2:
					TerrainObject->SetVertexColor(i,j, D3DCOLOR_XRGB(0, 0, 255));
					break;

				case 3:
					TerrainObject->SetVertexColor(i,j, D3DCOLOR_XRGB(255, 255, 0));
					break;

				case 4:
					TerrainObject->SetVertexColor(i,j, D3DCOLOR_XRGB(255, 0, 0));
					break;

				case 5:
					TerrainObject->SetVertexColor(i,j, D3DCOLOR_XRGB(255, 0, 255));
					break;

				default:
					TerrainObject->SetVertexColor(i,j, D3DCOLOR_XRGB(0, 0, 0));
					break;
				}
			}
		}
	
	// END TEMP
	/////////////////////////////////////////////////////////
*/
	//BouncingBall = new Ball(g_pd3dDevice, 1, 10, 10, D3DCOLOR_XRGB(255,0,0));
	//BouncingBall->Position.x = 40;
	//BouncingBall->Position.y = 40;
	//BouncingBall->Position.z = 20;
	//Force Gravity;
	//Gravity.Duration = -1;
	//Gravity.ForceVector = D3DXVECTOR3(0,0,-50);
	//BouncingBall->AddConstantForce(Gravity);

	CollisionListener = new CollisionDetector();
	CollisionListener->SetTerrainObject(TerrainObject);
	MsgDispatcher.AddListener(mMove, CollisionListener);

	MainForest = new Forest(g_pd3dDevice, 20,20);
	MainForest->SetTerrainObject(TerrainObject);
	//for(int i=0;i<500;i++)
	//	MainForest->Grow();
		
	EntityList.MoveToHead();
    EntityList.InsertAfter(TerrainObject);	
//	EntityList.InsertAfter(BouncingBall);
	EntityList.InsertAfter(CollisionListener);
	EntityList.InsertAfter(MainForest);

	g_pFlock = new Flock(Vector2D(LANDSIZE-5,LANDSIZE-5));
	const int delay = 1;
	for(int i=0; i<50; i++)
	{
		Boid* pBoid = new Boid(g_pd3dDevice, g_pFlock, TerrainObject);
		pBoid->behavior_update_delay = delay;
		pBoid->last_behavior_update = i % delay;
		g_pFlock->AddTo(pBoid);
		EntityList.InsertAfter(pBoid);
	}
	EntityList.InsertAfter(g_pFlock);

	// HACK: These need to be deleted at some point!!
	small_font = new Font( _T("Arial"), 12, D3DFONT_BOLD );
	small_font->InitDeviceObjects( g_pd3dDevice );
	//small_font->DrawText(50,50,1,_T("TEST"),D3DFONT_BOLD );
	profile_tracker = new Profile_Tracker();
	profile_tracker->set_displayed_quantity(Profile_Tracker::HIERARCHICAL_TIME);

/*
	for(i=0; i<100; i++)
	{
		Boid *pBoid = new Boid;
		pBoid->m_token.velocity.x = -pBoid->m_token.velocity.x;
		pBoid->m_token.position.x += 500;
		pBoid->m_token.setTexture("ship2.tga");
		pBoid->team=1;
		g_pFlock->AddTo(pBoid);
	}
  */  

	return S_OK;
}

//-----------------------------------------------------------------------------
// Name: Render()
// Desc: Draws the scene
//-----------------------------------------------------------------------------
VOID Render()
{
    if( NULL == g_pd3dDevice )
        return;

	//Update the camera position based on key presses.
	ProcessKeys();
	ProcessMouse();
	UpdateCamera();
	
	if( !interactive )
	{
		float yaw = g_pFlock->aveYaw(0);
		//float pitch = g_pFlock->avePitch(0);
	
		gTheta = yaw;
		//gPhi = pitch;
	}
	// Clear the backbuffer to a blue color
    g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0), 1.0f, 0 );

    // Begin the scene
    g_pd3dDevice->BeginScene();
	////////////////////////////////////////////////////////////////

	SetupLights();
	SetupMatrices();

    D3DXMATRIX matWorld;
	D3DXMatrixIdentity(&matWorld);
	g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );

	//This is where we do the timestep stuff
	static DWORD LastTime = timeGetTime();
	//TODO: perhaps redo all of the code to use ms instead of s?
	float TimeStep = (timeGetTime() - LastTime)/1000.0f; //convert it to seconds.
	LastTime = timeGetTime();

	//This is the main action loop, where we find out what every object wants to do.
	EntityList.MoveToHead();
	EntityList.Next();
	Msg* NewMsgs[MAXMESSAGES];
	int NumNewMsgs;
	while(!EntityList.AtEnd())
	{
        NumNewMsgs = EntityList.GetCurrent()->Data->GetAction(TimeStep, NewMsgs);
		if(NumNewMsgs>0)
		{
			MsgDispatcher.QueueMessages(NewMsgs, NumNewMsgs);
		}
		EntityList.Next();
	}
	MsgDispatcher.ProcessAllMessages();

	//Now we draw every object in the list
	EntityList.MoveToHead();
	EntityList.Next();
	while(!EntityList.AtEnd())
	{
		//Before we draw the next object, offset the transformation matrix appropriately
		// based on its position.
		D3DXMATRIX Dest;
		D3DXMatrixTranslation(&Dest, EntityList.GetCurrent()->Data->Position.x,EntityList.GetCurrent()->Data->Position.y,EntityList.GetCurrent()->Data->Position.z);
        D3DXMATRIX Temp;
        D3DXMatrixScaling(&Temp, EntityList.GetCurrent()->Data->m_scale, EntityList.GetCurrent()->Data->m_scale, EntityList.GetCurrent()->Data->m_scale);
		D3DXMatrixMultiply(&Dest, &matWorld, &Dest);
        D3DXMatrixMultiply(&Dest, &Temp, &Dest);
        D3DXMatrixMultiply(&Dest, &EntityList.GetCurrent()->Data->m_rotationMatrix, &Dest);
		g_pd3dDevice->SetTransform( D3DTS_WORLD, &Dest);

		int MeshCount=EntityList.GetCurrent()->Data->GetMeshCount();
		Mesh** MeshesToDraw = new Mesh*[MeshCount];
		EntityList.GetCurrent()->Data->GetMeshes(MeshesToDraw);
		for(int i=0;i<MeshCount;i++)
		{
			MeshesToDraw[i]->draw();
		}
		delete []MeshesToDraw;
		EntityList.Next();
	}
	profile_tracker->update();
    //profile_tracker->draw(10, 425);

    ////////////////////////////////////////////////////////////////
	// End the scene
    g_pd3dDevice->EndScene();

	/////////////////////////////////////////////
	// Joel's Screen capture

	//IDirect3DSurface9 *pBackBuffer;
	//g_pd3dDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer);
	//	
	//HDC hdc;
	//pBackBuffer->GetDC(&hdc);

	//TakeSnapshot(hdc , SCREENWIDTH, SCREENHEIGHT);
	//pBackBuffer->ReleaseDC(hdc);
	//pBackBuffer->Release();
	
	/////////////////////////////////////////////
    
    // Present the backbuffer contents to the display
    g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}

LRESULT WINAPI WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
        case WM_DESTROY:
            PostQuitMessage( 0 );
            return 0;
		case WM_KEYDOWN:				// Is A Key Being Held Down?
		{
			bKeyboardInputChanged = true;
			Keys[wParam] = true;			// If So, Mark It As TRUE
			return 0;				// Jump Back
		}
		case WM_KEYUP:					// Has A Key Been Released?
		{
			bKeyboardInputChanged = true;
			Keys[wParam] = false;			// If So, Mark It As FALSE
			return 0;				// Jump Back
		}
		case WM_LBUTTONDOWN:
		{
			MouseDown = true;
			MousePosition.x = LOWORD(lParam);
			MousePosition.y = HIWORD(lParam);
			return 0;
		}
		case WM_LBUTTONUP:
		{
			MouseDown = false;
			MousePosition.x = LOWORD(lParam);
			MousePosition.y = HIWORD(lParam);
			return 0;
		}
		case WM_MOUSEMOVE:
		{
			///////////////////////////////////////////////
			// Joel's Camera Stuff
			if(wParam & MK_LBUTTON)
			{
				// see how much the mouse has moved,
				// and update the camera position (in polar coordinates rotating around the origin)
				gTheta -= float(LOWORD(lParam)-MousePosition.x)/60.0f;
				gPhi -= float(HIWORD(lParam)-MousePosition.y)/60.0f;
			}
			///////////////////////////////////////////////

			MousePosition.x = LOWORD(lParam);
			MousePosition.y = HIWORD(lParam);

			
			return 0;
		}
    }

    return DefWindowProc( hWnd, msg, wParam, lParam );
}

HRESULT initialize_D3D(HWND hWnd)
{
   // Create the D3D object.
    if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
        return E_FAIL;

    // Get the current desktop display mode, so we can set up a back
    // buffer of the same format
    D3DDISPLAYMODE d3ddm;
    if( FAILED( g_pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm ) ) )
        return E_FAIL;

    // Set up the structure used to create the D3DDevice. Since we are now
    // using more complex geometry, we will create a device with a zbuffer.
    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory( &d3dpp, sizeof(d3dpp) );
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = d3ddm.Format;
    d3dpp.EnableAutoDepthStencil = TRUE;
    d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
	d3dpp.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;

	////////////////////////////////////////////////////
	// Joel's addition
	d3dpp.BackBufferWidth  = SCREENWIDTH;
	d3dpp.BackBufferHeight = SCREENHEIGHT;
	////////////////////////////////////////////////////

    // Create the D3DDevice
    if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
                                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                      &d3dpp, &g_pd3dDevice ) ) )
    {
        return E_FAIL;
    }

    // Turn off culling
    g_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );

    // Turn off D3D lighting
    g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );

    // Turn on the zbuffer
    g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE );

   // Set diffuse blending for alpha set in vertices.
    g_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE,   TRUE );
    g_pd3dDevice->SetRenderState( D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA );
    g_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
        g_pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE );
        g_pd3dDevice->SetRenderState( D3DRS_ALPHAREF,        0x08 );
		g_pd3dDevice->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL );

		//seed the random number generator
	srand(time(NULL));

    return S_OK;
}

VOID Cleanup_D3D()
{
	EntityList.MoveToHead();
	EntityList.Next();
	while(!EntityList.AtEnd())
	{
		delete EntityList.GetCurrent()->Data;
		EntityList.Next();
	}
	
	if( g_pd3dDevice != NULL )
	{
        g_pd3dDevice->Release();
		g_pd3dDevice = NULL;
	}

    if( g_pD3D != NULL )
	{
        g_pD3D->Release();
		g_pD3D = NULL;
	}
}

VOID Reset()
{
	TerrainObject->Initialize(LANDSIZE, LANDSIZE);
	TerrainObject->NewRandomSmoothTerrain();
	TerrainObject->NormalizeHeight();
	
	MainForest->SetTerrainObject(TerrainObject);
	MainForest->KillTrees();
	//delete MainForest;
	//MainForest = NULL;
	//MainForest = new Forest(g_pd3dDevice, 20,20);
	
}
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, int nCmdShow)
{
	MSG msg;

    // Register the window class.
    WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, 
                      GetModuleHandle(NULL), NULL, LoadCursor(0, IDC_CROSS), NULL, NULL,
                      "D3D Tutorial", NULL };
    RegisterClassEx( &wc );

    // Create the application's window.
    HWND hWnd = CreateWindow( "D3D Tutorial", "D3D Tutorial 01: CreateDevice", 
                              WS_OVERLAPPEDWINDOW, 50, 50, SCREENWIDTH, SCREENHEIGHT,
                              GetDesktopWindow(), NULL, wc.hInstance, NULL );


    // Initialize Direct3D
    if( SUCCEEDED(initialize_D3D(hWnd)) )
    {
		// Create the scene geometry
        if( SUCCEEDED( InitializeGeometry() ) && SUCCEEDED(InitializeObjects()))
        {
			// Show the window
			ShowWindow( hWnd, SW_SHOWDEFAULT );

			// Enter the message loop
			ZeroMemory( &msg, sizeof(msg) );
			while( msg.message!=WM_QUIT )
			{
				if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
				{
					TranslateMessage( &msg );
					DispatchMessage( &msg );
				}

				if( reset )
				{
					reset = false;
					Reset();
				}else
				{
					Render();
				}
			}
		}
		else
			MessageBox(NULL,TEXT("Error initializing geometry"), TEXT("Error"), NULL);
    }
	else
		MessageBox(NULL, TEXT("Error initializing Direct3D"), TEXT("Error"), NULL);

    // Clean up everything and exit the app
    Cleanup_D3D();
    UnregisterClass( "D3D Tutorial", wc.hInstance );
	return 0;
}
