/***************************************************************************/
/*                                                                         */
/* FileName: main.cpp                                                      */
/*                                                                         */
/* Details: Simple Initilisation of DirectX with XDK (prt2)                */
/*                                                                         */
/* Author: Ben_3D                                                          */
/*                                                                         */
/*                                                                         */
/***************************************************************************/
/*
<g0at3r> D3DXMatrixLookAtLH() or RH depending what you prefer
<g0at3r> SetTransform()
<g0at3r> D3DXMatrixPerspectiveFovLH() or RH
<g0at3r> D3DTS_PROJECTION D3DTS_VIEW
*/
#define PI 3.1415926535897932384626433832795
//Main header file for the XDK
#include <xtl.h>
#include <time.h>
#include <math.h>

#include "CXLoader.h"
#include "xbfont.h"
#include "text.h"
#include "gamepad.h"
#include "camera.h"
#include "asteroid.h"
#include "bullet.h"
LPDIRECT3D8 g_pD3D = NULL;                      // DirectX Object
LPDIRECT3DDEVICE8 g_pD3DDevice = NULL;          // Screen Object

time_t inittime = 0;
time_t currenttime = 0;
int score = 0;
void InitialiseD3D()
{
    //First of all, create the main D3D object. If it is created successfully we 
    //should get a pointer to an IDirect3D8 interface.
    g_pD3D = Direct3DCreate8(D3D_SDK_VERSION);

    //Create a structure to hold the settings for our device
    D3DPRESENT_PARAMETERS d3dpp; 
    ZeroMemory(&d3dpp, sizeof(d3dpp));

    //Fill the structure. 
    // Set fullscreen 640x480x32 mode

	d3dpp.BackBufferWidth = 640;
	d3dpp.BackBufferHeight = 480;
	d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;


	// Create one backbuffer
	d3dpp.BackBufferCount = 1;

	d3dpp.MultiSampleType = D3DMULTISAMPLE_4_SAMPLES_MULTISAMPLE_GAUSSIAN;

 
	// Set up how the backbuffer is "presented" to the frontbuffer each time
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;

    //Create a Direct3D device.
    g_pD3D->CreateDevice(0, D3DDEVTYPE_HAL, NULL, 
                                   D3DCREATE_HARDWARE_VERTEXPROCESSING, 
                                                   &d3dpp, &g_pD3DDevice);
}

void OurLight()
{

	//This is our light!...Well the structure which will hold our light details
	D3DLIGHT8 d3dLight;

	//Initialize the light structure.
	ZeroMemory(&d3dLight, sizeof(D3DLIGHT8));

	//Set up a light point!
	d3dLight.Type = D3DLIGHT_POINT;

	d3dLight.Diffuse.r = 1.0f;
	d3dLight.Diffuse.g = 1.0f;
	d3dLight.Diffuse.b = 1.0f;
	
	d3dLight.Ambient.r = 0.0f;
	d3dLight.Ambient.g = 0.0f;
	d3dLight.Ambient.b = 0.0f;
	
	d3dLight.Specular.r = 0.0f;
	d3dLight.Specular.g	= 0.0f;
	d3dLight.Specular.b	= 0.0f;

	d3dLight.Position.x	= 0.0f;
	d3dLight.Position.y	= 15.0f;
	d3dLight.Position.z	= -4.0f;

	d3dLight.Attenuation0 = 1.0f; 
	d3dLight.Attenuation1 = 0.0f; 
	d3dLight.Attenuation2 = 0.0f; 
	d3dLight.Range = 100.0f;


	g_pD3DDevice->SetLight(0, &d3dLight);
	g_pD3DDevice->LightEnable(0,TRUE);
	//Turn on lighting
    g_pD3DDevice->SetRenderState(D3DRS_LIGHTING, TRUE);

	// The colour when "NO" light is falling on our object!
	g_pD3DDevice->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(45,60,60));
} 

void CleanUp()
{
    g_pD3DDevice->Release();
    g_pD3D->Release();
}




//Application entry point
float calc_dis(float x1, float x2, float y1, float y2)
{
	return sqrt(((x1-x2)*(x1-x2))+((y1-y2)*(y1-y2)));
}
void __cdecl main()
{
      InitialiseD3D();
      OurLight();
	  CText text;
	  CXLoader ship;
	  CXLoader life[3];
	  bullet bullets[7];
	  asteroid rocks[16];

	  CGamePad m_gamepad;
	  CCamera m_camera;
	  float dis;
	  WCHAR my_text[200];
	  CXBFont m_Font; 
	  float radians;
	  float speed = 0;
	  float x_cur;
	  float y_cur;
	  float x_new;
	  float y_new;
      int i, j,rock_count=4,life_count = 3;
	  bool was_dwn = false;
      srand(time(0));

	  //=====================


	  m_camera.CreateFirstPerson();
	  m_gamepad.InitGamePad();
	  ship.Create(&g_pD3DDevice,"d:\\media\\models\\tship.x","d:\\");
      for(i = 0; i < 3; i++)
	    life[i].Create(&g_pD3DDevice,"d:\\media\\models\\life.x","d:\\");

	  text.Create(&g_pD3DDevice, L"D:\\media\\fonts\\font.ttf", FONT_TTF);
	  m_Font.Create( "D:\\media\\fonts\\Arial_18.xpr" );
	  text.SetHeight(24);

      for(i = 0; i < 7; i++)
		bullets[i].init(&g_pD3DDevice);

      for(i = 0; i < 16; i++)
		rocks[i].init(&g_pD3DDevice, 3);

      for(i = 0; i < rock_count; i++)
		rocks[i].visible = true;

	  ship.RotateRel(278,0,0);
	  life[0].Move(76,80,0);
	  life[1].Move(88,80,0);
	  life[2].Move(100,80,0);

	  wsprintfW(my_text, L"              [Controls]\nRotate: Left/Right on DPAD\nAccelerate: Left trigger\nStop:  Right trigger\nShoot:  A Button\n\n\n     Press Start to begin game.");  
	  
	  while(true)
	  {	  
           
		    g_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,  0, 0), 1.0f, 0);
            g_pD3DDevice->BeginScene();
              
			m_Font.Begin();
			m_Font.DrawText(150,150,0xff0687D9,my_text);
			m_Font.End();

			g_pD3DDevice->EndScene();
			g_pD3DDevice->Present(NULL, NULL, NULL, NULL);
            if(m_gamepad.bStartButton())
				break;
			m_gamepad.GetInput();
	  }

	  time ( &inittime );
	  while(true)
      {
            //Clear the backbuffer to black
		    //                                                          r   g   b
            g_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,  0, 0), 1.0f, 0);
            //Begin the scene
            g_pD3DDevice->BeginScene();



		/* update camera */
		m_camera.Setup3dCamera();
		m_camera.FirstPersonUpdate();

	
		if(speed > 0)
		  
			ship.MoveRel(-1*cos( radians )*speed,-1*sin(radians)*speed,0);
		
		//if( m_gamepad.iAnalogStickUp() )
		if( m_gamepad.fLeftTrigger() )
		{

			x_cur = -0.4 * cos(ship.GetXpos());
			y_cur = -0.4 * sin(ship.GetYpos());
            
			x_new = x_cur + cos(ship.GetZrot());
    	    y_new = y_cur + sin(ship.GetZrot());

            if(ship.GetZrot() > 0 && ship.GetZrot() < PI/2.0)
			{
				if(x_new > 0)
					x_new *= -1;
				if(y_new > 0)
					y_new *= -1;
			}

            if(ship.GetZrot() > PI/2.0  && ship.GetZrot() < PI)
			{
				if(x_new < 0)
					x_new *= -1;
				if(y_new > 0)
					y_new *= -1;
			}

            if(ship.GetZrot() > PI  && ship.GetZrot() < (3*PI)/2)
			{
				if(x_new < 0)
					x_new *= -1;
				if(y_new < 0)
					y_new *= -1;
			}

            if(ship.GetZrot() > (3*PI)/2  && ship.GetZrot() < 2*PI)
			{
				if(x_new > 0)
					x_new *= -1;
				if(y_new < 0)
					y_new *= -1;
			}

			radians = atan(y_new/x_new);
			if ( x_new > 0 && y_new < 0 && radians < 0 ) radians += PI;
			if ( x_new > 0 && y_new > 0 && radians < PI ) radians += PI;
			speed += (sqrt(( x_new*x_new ) + ( y_new*y_new )))*(m_gamepad.fLeftTrigger()/255.0f);
            
			if(speed > 12)
				speed = 12;
		}
			//m_camera.MoveForward();
		if( m_gamepad.fRightTrigger() )
		{ if(speed > 0) speed -= .5 * (m_gamepad.fRightTrigger()/255.0f); if(speed < 0) speed = 0; }
		float r = ship.GetZrot();
 
		if( m_gamepad.bGreenButtonPressed() )
			 was_dwn = true;

         if( !(m_gamepad.bGreenButtonPressed()) && was_dwn == true )
		 {
		   for(i = 0; i < 7; i++) 
		   {
			 if((bullets[i].shoot(r,ship.GetXpos(),ship.GetYpos())) == true)
				 break;
		   }
		   was_dwn = false;
		 }


		 if( m_gamepad.bButtonPadRight() )
			ship.RotateRel(0,0,-.05);

		  if ( m_gamepad.bButtonPadLeft() )
			ship.RotateRel(0,0,.05);
			m_gamepad.GetInput();

			
			//wsprintfW(my_text, L"%f   %f    %f", ship.GetXpos(), ship.GetYpos(), m_gamepad.fLeftTrigger() );
 			  wsprintfW(my_text, L"Score: %d",score);
			
			// Slow ship with friction
			if(speed > 0)
			  speed -= .09;

			for(i = 0; i < 7; i++)
			{
			  if( bullets[i].render() )
			  {
                for(j = 0; j < 16; j++)
				{
					if(bullets[i].visible == false)
						break;

					if(rocks[j].visible)
					{

						switch(rocks[j].size)
						{
						  case 1:
							        dis = calc_dis(rocks[j].asteroid_img.GetXpos()*4.4,bullets[i].b_img.GetXpos(),rocks[j].asteroid_img.GetYpos()*4.4,bullets[i].b_img.GetYpos());
									if(dis < 50)
									{
										score += 600/rocks[j].size;

										bullets[i].visible = false;
		   							    rocks[j].visible = false;
										
									}
									
									break;
						  case 2:
							        dis = calc_dis(rocks[j].asteroid_img.GetXpos()*5.7,bullets[i].b_img.GetXpos(),rocks[j].asteroid_img.GetYpos()*5.7,bullets[i].b_img.GetYpos());
									if(dis < 75)
									{
										score += 600/rocks[j].size;

										bullets[i].visible = false;
										rocks[j].size  = 1;
										rocks[j].reinit();
									    
										
									    rocks[rock_count].size = 1;
										rocks[rock_count].reinit();
										rocks[rock_count].visible = true;
										rocks[rock_count].asteroid_img.Move(rocks[j].asteroid_img.GetXpos(),rocks[j].asteroid_img.GetYpos(),0);
									
										rock_count++;
									}

							        break;
						  case 3:
							        dis = calc_dis(rocks[j].asteroid_img.GetXpos()*6.7,bullets[i].b_img.GetXpos(),rocks[j].asteroid_img.GetYpos()*6.7,bullets[i].b_img.GetYpos());
									if(dis < 100)
									{
										score += 600/rocks[j].size;

										bullets[i].visible = false;
										rocks[j].size = 2;
										rocks[j].reinit();
										 
										rocks[rock_count].size = 2;
										rocks[rock_count].reinit();
										rocks[rock_count].visible = true;
										rocks[rock_count].asteroid_img.Move(rocks[j].asteroid_img.GetXpos(),rocks[j].asteroid_img.GetYpos(),0);
										

										rock_count++;
										
									}
							        break;
					     }
					  }
					}
				}
			  }
			

			ship.Render(-1);
           
			for(i = 0; i < life_count; i++)
				life[i].Render(2);
			
			// ignore rock collisions for first 3 seconds;
			
			time(&currenttime);
	    
			
			for(i = 0; i < 16; i++)
			{
				if( rocks[i].render() )
				{
                    if(((int)difftime(currenttime, inittime)) < 2)
						continue;

					switch(rocks[i].size)
					{
					case 1:
					   dis = calc_dis(rocks[i].asteroid_img.GetXpos()*4.9,ship.GetXpos(),rocks[i].asteroid_img.GetYpos()*4.9,ship.GetYpos());
					   if( dis < 150 )
					   { life_count--; speed = 0; ship.Move(0,0,0); time ( &inittime ); }
					   break;
					case 2:
       					dis = calc_dis(rocks[i].asteroid_img.GetXpos()*6.3,ship.GetXpos(),rocks[i].asteroid_img.GetYpos()*6.3,ship.GetYpos());
						if( dis < 150 )
					    { life_count--; speed = 0; ship.Move(0,0,0); time ( &inittime );}
					   break;
					case 3:
					   dis = calc_dis(rocks[i].asteroid_img.GetXpos()*7.5,ship.GetXpos(),rocks[i].asteroid_img.GetYpos()*7.5,ship.GetYpos());
					   if( dis < 150 )
						{ life_count--; speed = 0; ship.Move(0,0,0); time ( &inittime );}
					   break;
					}
				}
			}

			m_Font.Begin();
			m_Font.DrawText(50,25,0xff0687D9,my_text);
			m_Font.End();

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

            if(ship.GetXpos() < -785)
			{
				ship.MoveX(ship.GetXpos() + 1570);
			}
			else if (ship.GetXpos() > 785)
			{
				ship.MoveX(ship.GetXpos() - 1570);
			}

            if(ship.GetYpos() < -659)
			{
				ship.MoveY(ship.GetYpos() + 1318);
			}
			else if (ship.GetYpos() > 659)
			{
				ship.MoveY(ship.GetYpos() - 1318);
			}

			g_pD3DDevice->Present(NULL, NULL, NULL, NULL);

			if(life_count == 0)
				break;
      }     
	  
	  wsprintfW(my_text, L"Final Score: %d\n\nThanks to:\n\nlaZmike, Noxz, kev2, g0at3r\n#xfactor and #xboxopensource\n\n\nPress Start button to quit.",score);  
	  
	  while(true)
	  {	  
           
		    g_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,  0, 0), 1.0f, 0);
            g_pD3DDevice->BeginScene();
              
			m_Font.Begin();
			m_Font.DrawText(200,80,0xff0687D9,my_text);
			m_Font.End();

			g_pD3DDevice->EndScene();
			g_pD3DDevice->Present(NULL, NULL, NULL, NULL);
            if(m_gamepad.bStartButton())
				break;
			m_gamepad.GetInput();
	  }
      void CleanUp();
	  XLaunchNewImage( NULL, NULL );
}

