// Game.cpp: implementation of the CGame class.
//
//////////////////////////////////////////////////////////////////////

#include "Game.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CGame::CGame()
{
	m_fEnableLogging = false;
	aa= 0;
	bb = 0;
	m_pD3D = NULL;
	m_pD3DDevice = NULL;
	m_dwFrames = 0;
	m_dwStartTime = 0;
	m_dwEndTime = 0;

	C_WORK =0;

	WORK_VAL = FALSE;
	m_pCube1 = NULL;
	m_pCube2 = NULL;
	m_pCube3 = NULL;
	m_pCube4 = NULL;
	m_pCube5 = NULL;

	C_Z = C_X = 0;

	C_UP = 0;
	C_ACCEL = 0;
	C_LEFT = 0;
	C_ACCELX = 0;
	PushUp = false;
	PushDown = false;
	PushLeft = false;
	PushRight = false;
    D3DXMatrixLookAtLH(&matView, &D3DXVECTOR3(0.0f, 0.0f, 0.0f),		//Camera Position
                                 &D3DXVECTOR3(0.0f, 0.0f, 100.0f),		//Look At Position
                                 &D3DXVECTOR3(0.0f, 1.0f, 0.0f));		//Up Direction
}

CGame::~CGame()
{
	//Game finished, so record time
	m_dwEndTime = timeGetTime();

	DWORD dwDuration = (m_dwEndTime - m_dwStartTime) / 1000;
	
	//Log stats
	WriteToLog("Statistics:");
	WriteToLog("\tStart Time (ms): %d", m_dwStartTime);
	WriteToLog("\tEnd Time (ms): %d", m_dwEndTime);
	WriteToLog("\tDuration (s): %d", dwDuration);
	WriteToLog("\tTotal Frame Count: %d", m_dwFrames);
	WriteToLog("\tAverage FPS: %d", (m_dwFrames / dwDuration));

	//Clean up objects/interfaces
	SafeDelete(m_pCube1);
	SafeDelete(m_pCube2);
	SafeDelete(m_pCube3);
	SafeDelete(m_pCube4);
	SafeDelete(m_pCube5);
	
	SafeRelease(m_pD3DDevice);
	SafeRelease(m_pD3D);
}

bool CGame::Initialise(HWND hWnd, UINT nWidth, UINT nHeight)
{
	if(SUCCEEDED(InitialiseD3D(hWnd, nWidth, nHeight)))
	{
		return InitialiseGame();
	}
	else
	{
		return false;
	}

	return true;
}

bool CGame::InitialiseGame()
{

	m_pCube1 = new CCuboid(m_pD3DDevice);
	m_pCube1->SetPosition(-130.0, -1.0, 140);
	m_pCube1->SetSize(0, 25, 500);
	m_pCube1->SetTexture("1_2.bmp");

	m_pCube2 = new CCuboid(m_pD3DDevice);
	m_pCube2->SetPosition(-10, -1.0, -90);
	m_pCube2->SetSize(300, 25, 0);
	m_pCube2->SetTexture("1_2.bmp");

	m_pCube3 = new CCuboid(m_pD3DDevice);
	m_pCube3->SetPosition(0.0, -15.0, 150.0);
	m_pCube3->SetSize(300, 1, 500);
	m_pCube3->SetTexture("11.bmp");

	m_pCube4 = new CCuboid(m_pD3DDevice);
	m_pCube4->SetPosition(130.0, -1.0, 140.0);
	m_pCube4->SetSize(0, 25, 500);
	m_pCube4->SetTexture("1_2.bmp");

	m_pCube5 = new CCuboid(m_pD3DDevice);
	m_pCube5->SetPosition(-10, -1.0, 380);
	m_pCube5->SetSize(300, 25, 0);
	m_pCube5->SetTexture("1_2.bmp");
/*
	//Setup games objects here
	m_pCube1 = new CCuboid(m_pD3DDevice);
	m_pCube1->SetPosition(-20.0, -14.7, 150.0);
	m_pCube1->SetSize(2, 1, 500);
	//m_pCube1->SetTexture("8.bmp");

	m_pCube2 = new CCuboid(m_pD3DDevice);
	m_pCube2->SetPosition(20.0, -14.7, 150.0);
	m_pCube2->SetSize(2, 1, 500);
//	m_pCube2->SetTexture("1.bmp");
*/
/*	m_pCube3 = new CCuboid(m_pD3DDevice);
	m_pCube3->SetPosition(35.0, -3.0, 80.0);
	m_pCube3->SetSize(25, 25, 25);
	m_pCube3->SetTexture("1_2.bmp");

	m_pCube4 = new CCuboid(m_pD3DDevice);
	m_pCube4->SetPosition(35.0, -3.0, 150.0);
	m_pCube4->SetSize(25, 25, 25);
	m_pCube4->SetTexture("1_2.bmp");

	m_pCube5 = new CCuboid(m_pD3DDevice);
	m_pCube5->SetPosition(0.0, -15.0, 150.0);
	m_pCube5->SetSize(300, 1, 500);
	m_pCube5->SetTexture("11.bmp");
*/
	return true;
}

D3DFORMAT CGame::CheckDisplayMode(UINT nWidth, UINT nHeight, UINT nDepth)
{
	UINT x;
	D3DDISPLAYMODE d3ddm;

	for(x = 0; x < m_pD3D->GetAdapterModeCount(0); x++)
	{
		m_pD3D->EnumAdapterModes(0, x, &d3ddm);
		if(d3ddm.Width == nWidth)
		{
			if(d3ddm.Height == nHeight)
			{
				if((d3ddm.Format == D3DFMT_R5G6B5) || (d3ddm.Format == D3DFMT_X1R5G5B5) || (d3ddm.Format == D3DFMT_X4R4G4B4))
				{
					if(nDepth == 16)
					{
						return d3ddm.Format;
					}
				}
				else if((d3ddm.Format == D3DFMT_R8G8B8) || (d3ddm.Format == D3DFMT_X8R8G8B8))
				{
					if(nDepth == 32)
					{
						return d3ddm.Format;
					}
				}
			}
		}
	}

	return D3DFMT_UNKNOWN;
}

HRESULT CGame::InitialiseD3D(HWND hWnd, UINT nWidth, UINT nHeight)
{
	WriteToLog("InitialiseD3D Started...");

    //First of all, create the main D3D object. If it is created successfully we 
    //should get a pointer to an IDirect3D8 interface.
    m_pD3D = Direct3DCreate8(D3D_SDK_VERSION);
    if(m_pD3D == NULL)
    {
		WriteToLog("\tUnable to create DirectX8 interface.");
        return E_FAIL;
    }

    //Get the current display mode
    D3DDISPLAYMODE d3ddm;

	d3ddm.Format = CheckDisplayMode(nWidth, nHeight, 32);
	if(d3ddm.Format != D3DFMT_UNKNOWN)
	{
		//Width x Height x 32bit has been selected
		d3ddm.Width = nWidth;
		d3ddm.Height = nHeight;

		WriteToLog("\t%d x %d x 32bit back buffer format selected. Format = %d.", nWidth, nHeight, d3ddm.Format);
	}
	else
	{
		d3ddm.Format = CheckDisplayMode(nWidth, nHeight, 16);
		if(d3ddm.Format != D3DFMT_UNKNOWN)
		{
            //Width x Height x 16bit has been selected
			d3ddm.Width = nWidth;
			d3ddm.Height = nHeight;
			WriteToLog("\t%d x %d x 16bit back buffer format selected. Format = %d.", nWidth, nHeight, d3ddm.Format);
		}
        else
		{
			WriteToLog("\tUnable to select back buffer format for %d x %d.", nWidth, nHeight);
            return E_FAIL;
        }
	}


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

	d3dpp.Windowed = FALSE;
    d3dpp.BackBufferCount = 1;
    d3dpp.BackBufferFormat = d3ddm.Format;
    d3dpp.BackBufferWidth = d3ddm.Width;
    d3dpp.BackBufferHeight = d3ddm.Height;
    d3dpp.hDeviceWindow = hWnd;
    d3dpp.SwapEffect = D3DSWAPEFFECT_COPY_VSYNC;
	d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
    d3dpp.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_ONE;

	//Select the best depth buffer, select 32, 24 or 16 bit
    if(m_pD3D->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, d3ddm.Format, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, D3DFMT_D32) == D3D_OK)
	{
        d3dpp.AutoDepthStencilFormat = D3DFMT_D32;
        d3dpp.EnableAutoDepthStencil = TRUE;

		WriteToLog("\t32bit depth buffer selected");
    }
    else if(m_pD3D->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, d3ddm.Format, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, D3DFMT_D24X8) == D3D_OK)
    {
		d3dpp.AutoDepthStencilFormat = D3DFMT_D24X8;
        d3dpp.EnableAutoDepthStencil = TRUE;

		WriteToLog("\t24bit depth buffer selected");
	}
    else if(m_pD3D->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, d3ddm.Format, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, D3DFMT_D16) == D3D_OK)
    {
		d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
        d3dpp.EnableAutoDepthStencil = TRUE;

		WriteToLog("\t16bit depth buffer selected");
	}
    else
	{
        d3dpp.EnableAutoDepthStencil = FALSE;
		WriteToLog("\tUnable to select depth buffer.");
	}

    //Create a Direct3D device.
    if(FAILED(m_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, 
                                   D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &m_pD3DDevice)))
    {
		WriteToLog("\tUnable to create device.");
        return E_FAIL;
    }
    
	//Turn on back face culling. This is becuase we want to hide the back of our polygons
    m_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);

	//Turn off lighting becuase we are specifying that our vertices have colour
    m_pD3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);

	//Turn on Depth Buffering
    m_pD3DDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);

	WriteToLog("InitialiseD3D Finished OK");

    return S_OK;
}

LPDIRECT3DDEVICE8 CGame::GetDevice()
{
	return m_pD3DDevice;
}

void CGame::GameLoop()
{
    //Enter the game loop
    MSG msg; 
    BOOL fMessage;

    PeekMessage(&msg, NULL, 0U, 0U, PM_NOREMOVE);

	//Game started, so record time
	m_dwStartTime = timeGetTime();

    while(msg.message != WM_QUIT)
    {
        fMessage = PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE);

        if(fMessage)
        {
            //Process message
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else
        {
            //No message to process, so render the current scene
            Render();
        }

    }
}

void CGame::Render()
{
    D3DXMATRIX matRotationX, matRotationY, matRotationZ, matRotationUser1;
	D3DXMATRIX matMoveRight27, matMoveLeft27, matMoveRight9, matMoveLeft9, matMoveDown15, matMoveUp15;
	D3DXMATRIX matTransformation2, matTransformation3, matTransformation4, matTransformation5;
    D3DXMATRIX matScaleUp1p5;

	if(m_pD3DDevice == NULL)
    {
        return;
    }

    //Clear the back buffer and depth buffer
    m_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
    
    //Begin the scene
    m_pD3DDevice->BeginScene();
    
	//Setup camera and perspective
	SetupCamera();

    
	//Create the rotation transformation matrices around the x, y and z axis
	D3DXMatrixRotationX(&matRotationX, 0); // timeGetTime()/400.0f);
	D3DXMatrixRotationY(&matRotationY, 0); // timeGetTime()/400.0f);
	D3DXMatrixRotationZ(&matRotationZ, 0); // timeGetTime()/400.0f);

	//Create the rotation transformation matrices around our user defined axis
	D3DXMatrixRotationAxis(&matRotationUser1, &D3DXVECTOR3(1.0f, 1.0f, 0.0f), timeGetTime()/400.0f);

	//Create the translation (move) matrices
	//MOVE__
	D3DXMatrixTranslation(&matMoveRight27, 27.0, 0.0, 0.0);
	D3DXMatrixTranslation(&matMoveLeft27, -27.0, 0.0, 0.0);
	D3DXMatrixTranslation(&matMoveRight9, 9.0, 0.0, 0.0);
	D3DXMatrixTranslation(&matMoveLeft9, -9.0, 0.0, 0.0);
	D3DXMatrixTranslation(&matMoveDown15, 0.0, -15.0, 0.0);
	D3DXMatrixTranslation(&matMoveUp15, 0.0, 15.0, 0.0);

	//Create a scale transformation
	D3DXMatrixScaling(&matScaleUp1p5, 1.5, 1.5, 1.5);


	//Combine the matrices to form 4 transformation matrices
	D3DXMatrixMultiply(&matTransformation2, &matMoveRight9, &matRotationY);
	D3DXMatrixMultiply(&matTransformation2, &matTransformation2, &matMoveLeft9);

	D3DXMatrixMultiply(&matTransformation3, &matMoveLeft9, &matRotationZ);
	D3DXMatrixMultiply(&matTransformation3, &matTransformation3, &matMoveRight9);

	D3DXMatrixMultiply(&matTransformation4, &matMoveLeft27, &matRotationX);
	D3DXMatrixMultiply(&matTransformation4, &matTransformation4, &matMoveRight27);

	D3DXMatrixMultiply(&matTransformation5, &matMoveDown15, &matRotationY);
	D3DXMatrixMultiply(&matTransformation5, &matTransformation5, &matRotationX);
	D3DXMatrixMultiply(&matTransformation5, &matTransformation5, &matRotationZ);
	D3DXMatrixMultiply(&matTransformation5, &matTransformation5, &matMoveUp15);
	//D3DXMatrixMultiply(&matTransformation5, &matTransformation5, &matScaleUp1p5);

    //Apply the transformations and render our objects
	m_pD3DDevice->SetTransform(D3DTS_WORLD, &matRotationX);
    m_pCube1->Render_2();

	m_pD3DDevice->SetTransform(D3DTS_WORLD, &matTransformation2);
	m_pCube2->Render_2();

	m_pD3DDevice->SetTransform(D3DTS_WORLD, &matTransformation3);
	m_pCube3->Render();

	m_pD3DDevice->SetTransform(D3DTS_WORLD, &matTransformation4);
	m_pCube4->Render_2();

	m_pD3DDevice->SetTransform(D3DTS_WORLD, &matTransformation5);
	m_pCube5->Render_2();

    //End the scene
    m_pD3DDevice->EndScene();
    
    //Filp the back and front buffers so that whatever has been rendered on the back buffer
    //will now be visible on screen (front buffer).
    m_pD3DDevice->Present(NULL, NULL, NULL, NULL);

	//Count Frames
	m_dwFrames++;
}

void CGame::SetupCamera()
{
	//Here we will setup the camera.
	//The camera has three settings: "Camera Position", "Look at Position" and "Up Direction"
	//We have set the following:
	//Camera Position:	(0, 0, -100)
	//Look at Position: (0, 0, 0)
	//Up direction:		Y-Axis.
	if (PushUp)
	{
		C_UP = -0.05f;
		aa++;
		WORK_VAL = TRUE;
		
	}
	
	if (PushDown)
	{
		C_UP = 0.05f;
		aa++;
		WORK_VAL = TRUE;
	}
	if (PushLeft) 
		C_LEFT = 0.002f;
	if (PushRight)
		C_LEFT = -0.002f;



	C_ACCEL += C_UP;
	if(WORK_VAL == TRUE)
	{
		if(bb%2 == 0 )
		{
			C_WORK = -0.25f;
			
		}
		else
		{
			C_WORK = 0.25f;
			
		}

		
		WORK_VAL = FALSE;
	}
	else
		C_WORK = 0;
	
	
	if(aa > 10)
	{
		bb++;
			aa = 0;
	}
	C_ACCELX += C_LEFT;

	// 가속 저항
	if (C_ACCEL > 0) {
		if (C_UP < 0.02f) {
			C_UP = 0;
			C_ACCEL -= 0.01f;
			if (C_ACCEL < 0)
				C_ACCEL = 0;
		}
		else
		C_UP -= 0.01f;
	}
	if (C_ACCEL < 0) {
		if (C_UP > -0.02f) {
			C_UP = 0;
			C_ACCEL += 0.01f;
			if (C_ACCEL > 0)
				C_ACCEL = 0;
		}
		else
			C_UP += 0.01f;
	}

	// 가속한계
	if (C_ACCEL > 1.5) 
		C_ACCEL = 1.5;
	if (C_ACCEL < -1.5)
		C_ACCEL = -1.5;

	//C_ACCELX += C_LEFT;

	// 핸들 저항
	if (C_ACCELX > 0) {
		C_ACCELX -= 0.0005f;
		if (C_ACCELX < 0)
			C_ACCELX = 0;
		if (C_LEFT > 0)
			C_LEFT -= 0.0005f;
		if (C_LEFT < 0.0005f)
			C_LEFT = 0;
	}
	if (C_ACCELX < 0) {
		C_ACCELX += 0.0005f;
		if (C_ACCELX > 0)
			C_ACCELX = 0;
		if (C_LEFT < 0)
			C_LEFT += 0.0005f;
		if (C_LEFT > -0.0005f)
			C_LEFT = 0;
	}

	// 핸들 가속한계
	if (C_ACCELX > 0.02) 
		C_ACCELX = 0.02;
	if (C_ACCELX < -0.02)
		C_ACCELX = -0.02;

	// 속도와 회전관계
	if (C_ACCEL > 0) 
		C_ACCELX *= C_ACCEL/1.5;
	if (C_ACCEL < 0)
		C_ACCELX *= - C_ACCEL/1.5;
	D3DXMATRIX matRotationY;
	D3DXMATRIX matMove;

	D3DXMatrixRotationY(&matRotationY, C_ACCELX);
	//if (C_ACCEL != 0.0f)
		D3DXMatrixMultiply(&matView, &matView, &matRotationY);
    
	D3DXMatrixTranslation(&matMove, 0.0, C_WORK, C_ACCEL);
    D3DXMatrixMultiply(&matView, &matView, &matMove);
	m_pD3DDevice->SetTransform(D3DTS_VIEW, &matView);
	
	
	//Here we specify the field of view, aspect ration and near and far clipping planes.
    D3DXMATRIX matProj;
    D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI/4, 1.0f, 1.0f, 500.0f);
    m_pD3DDevice->SetTransform(D3DTS_PROJECTION, &matProj);
}


void CGame::WriteToLog(char *lpszText, ...)
{
	if(m_fEnableLogging)
	{
		va_list argList;
		FILE *pFile;

		//Initialize variable argument list
		va_start(argList, lpszText);

		//Open the log file for appending
		pFile = fopen("log.txt", "a+");

		//Write the text and a newline
		vfprintf(pFile, lpszText, argList);
		putc('\n', pFile);

		//Close the file
		fclose(pFile);
		va_end(argList);
	}
}

void CGame::EnableLogging()
{
	m_fEnableLogging = true;
	
	FILE* pFile;

	//Clear the file contents
	pFile = fopen("log.txt", "wb");

	//Close it up and return success
	fclose(pFile);
}
