//#include <windows.h>
#include <D3dx8.h>
#include <ctime>
#include <D3dx8math.h>

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

#include "Flock.h"

#define LANDSIZE 40

IDirect3D8*			g_pD3D			= NULL;
IDirect3DDevice8*	g_pd3dDevice	= NULL;
Terrain* TerrainObject;
Ball* BouncingBall;
CollisionDetector* CollisionListener;
LList<Entity*> EntityList;
MessageManager MsgDispatcher;
bool Keys[256];
bool MouseDown = false;
POINT MousePosition;

#define PI 3.141592653592

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	= 0;//float(PI / 2.0);
float gPhi		= float(3.0/18.0 * PI);
float gRho = 50;
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;
////////////////////////////////////////////////////////////////////////////////////

#define SCREENWIDTH 700
#define SCREENHEIGHT 700

void SetupLights()
{
		// Set up a light
	D3DXVECTOR3 vecDir;
	D3DLIGHT8 light;
	ZeroMemory( &light, sizeof(D3DLIGHT8) );
	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;
	}
	//Vector2D p = g_pFlock->avePos();
	//gCameraCenter = D3DXVECTOR3(p.x, p.y, TerrainObject->GetHeight(p.x, p.y)); 
	gCameraCenter = g_pFlock->ave3Pos(0);
		/*
	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(35,35);
		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;
	}

	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, 10.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;
}

HRESULT InitializeObjects()
{
	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();

	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);

	EntityList.MoveToHead();
    EntityList.InsertAfter(TerrainObject);	
	EntityList.InsertAfter(BouncingBall);
	EntityList.InsertAfter(CollisionListener);

	g_pFlock = new Flock();
	for(int i=0; i<50; i++)
	{
		Boid* pBoid = new Boid(g_pd3dDevice, g_pFlock, TerrainObject);
		g_pFlock->AddTo(pBoid);
		EntityList.InsertAfter(pBoid);
	}
	EntityList.InsertAfter(g_pFlock);
/*
	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();
    
	// 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);
		D3DXMatrixMultiply(&Dest, &matWorld, &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();
	}


    ////////////////////////////////////////////////////////////////
	// End the scene
    g_pd3dDevice->EndScene();
    
    // 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_RBUTTON)
			{
				// 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 = Direct3DCreate8( 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 = FALSE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = d3ddm.Format;
    d3dpp.EnableAutoDepthStencil = TRUE;
    d3dpp.AutoDepthStencilFormat = D3DFMT_D16;

	////////////////////////////////////////////////////
	// Joel's addition
	d3dpp.BackBufferWidth  = 640;
	d3dpp.BackBufferHeight = 480;
	////////////////////////////////////////////////////

    // 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;
	}
}

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 );
				}

				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;
}
