/** 
 * \file GameUtils.cpp
 * \brief Collection of usefull functions called by Game.cpp.
 * 
 * \author Petar Bajic, MPE (C) All Rights Reserved, Homepage: www.mystic-peanut.com
 * \date January, 16 2007.
 */

#include "GameUtils.h"

WINDOWPLACEMENT pwp;
bool bIsTopmost = true;
DWORD dwStyle;

/**
 * \brief This function creates DirectX 3D Device
 * \param hWnd handle to the application window
 * \param ppd3dDevice pointer to d3d device to be created
 * \param ppd3dpp directX presentation parameters
 * \param fullscreen boolean telling if application is started in fullscreen or windowed mode
 * \author Petar Bajic 
 * \date January, 16 2007.
 */
bool InitD3D(HWND hWnd, LPDIRECT3DDEVICE9* ppd3dDevice, LPDIRECT3D9* ppD3D, D3DPRESENT_PARAMETERS** ppd3dpp, bool fullscreen) 
{
    if( NULL == ( *ppD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
        return false;
	
	*ppd3dpp = (D3DPRESENT_PARAMETERS*) malloc(sizeof(D3DPRESENT_PARAMETERS));

    // Set up the structure used to create the D3DDevice
    ZeroMemory( *ppd3dpp, sizeof(D3DPRESENT_PARAMETERS) );
    (**ppd3dpp).Windowed = TRUE;
    (**ppd3dpp).SwapEffect = D3DSWAPEFFECT_DISCARD;
    (**ppd3dpp).BackBufferFormat = D3DFMT_UNKNOWN; //D3DFMT_UNKNOWN works with windowed, but must be defined for fullscreen
	(**ppd3dpp).EnableAutoDepthStencil = TRUE;
	(**ppd3dpp).AutoDepthStencilFormat = D3DFMT_D16;
	RECT rcCurrentClient;
	GetClientRect( hWnd, &rcCurrentClient );
	(**ppd3dpp).BackBufferWidth = (UINT)rcCurrentClient.right;
	(**ppd3dpp).BackBufferHeight = (UINT)rcCurrentClient.bottom;

	if (fullscreen)
	{
		//Find out desktop resolution:
		D3DDISPLAYMODE DisplayMode;
		(*ppD3D)->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &DisplayMode);
		//Override fullscreen parameters
		(**ppd3dpp).BackBufferFormat = D3DFMT_A8R8G8B8;
		(**ppd3dpp).Windowed = FALSE;
		(**ppd3dpp).BackBufferWidth = DisplayMode.Width;
		(**ppd3dpp).BackBufferHeight = DisplayMode.Height;
	}

    // Create the D3DDevice
    if( FAILED( (*ppD3D)->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
                                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                      *ppd3dpp, ppd3dDevice ) ) )
    {
        return false;
    }

    // Turn off culling, so we see the front and back of the triangle
    (*ppd3dDevice)->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
	// Turn on Z Buffer
	(*ppd3dDevice)->SetRenderState( D3DRS_ZENABLE, D3DZB_TRUE );

	return true;
}

bool InitMusic()
{
	//Each game module initializes its own music
	return true;
}

/**
 * \brief This function creates lights
 * \param light1 pointer to light to be created
 * \author Petar Bajic 
 * \date January, 16 2007.
 */
bool InitLight (LPDIRECT3DDEVICE9 pd3dDevice, D3DLIGHT9* light1, D3DLIGHT9* light2, D3DLIGHT9* light3)
{
	// Fill in a light structure defining our light
	ZeroMemory( light1, sizeof(D3DLIGHT9) );
	light1->Type    = D3DLIGHT_DIRECTIONAL;
	light1->Diffuse.r = 1.0f;
	light1->Diffuse.g = 1.0f;
	light1->Diffuse.b = 1.0f;
	light1->Diffuse.a = 1.0f;
	light1->Range   = 1000.0f;

	// Create a direction for our light - it must be normalized 
	D3DXVECTOR3 vecDir;
	vecDir = D3DXVECTOR3(0.5f,-0.3f,0.5);
	D3DXVec3Normalize( (D3DXVECTOR3*)&(light1->Direction), &vecDir );

	ZeroMemory( light2, sizeof(D3DLIGHT9) );
	light2->Type    = D3DLIGHT_DIRECTIONAL;
	light2->Diffuse.r = 1.0f;
	light2->Diffuse.g = 1.0f;
	light2->Diffuse.b = 1.0f;
	light2->Diffuse.a = 1.0f;
	light2->Range   = 1000.0f;

	// Create a direction for our light - it must be normalized 
	vecDir = D3DXVECTOR3(-0.5f,-0.3f,-0.5f);
	D3DXVec3Normalize( (D3DXVECTOR3*)&(light2->Direction), &vecDir );

	ZeroMemory( light3, sizeof(D3DLIGHT9) );
	light3->Type    = D3DLIGHT_POINT;
	light3->Diffuse.r = 1.0f;
	light3->Diffuse.g = 1.0f;
	light3->Diffuse.b = 1.0f;
	light3->Diffuse.a = 1.0f;
	light3->Range   = 1000.0f;
	light3->Position = D3DXVECTOR3(0.0f,0.0f,0.0f);
	light3->Attenuation0 = 1.0f;
	light3->Range = 1000.0f;
	// Create a direction for our light - it must be normalized 
	//vecDir = D3DXVECTOR3(0.0f,1.0f,0.0f);
	//D3DXVec3Normalize( (D3DXVECTOR3*)&(light3->Direction), &vecDir );

	// Tell the device about the lights and turn it on
	pd3dDevice->SetLight( 0, light1 );
	pd3dDevice->LightEnable( 0, TRUE ); 
	pd3dDevice->SetLight( 1, light2 );
	pd3dDevice->LightEnable( 1, TRUE ); 
	pd3dDevice->SetLight( 2, light3 );
	

	return true;
}

/**
 * \brief This function creates DirectX sound device
 * \param ppDS sound device to be created
 * \author Petar Bajic 
 * \date January, 16 2007.
 */
bool InitSound(HWND hWnd, IDirectSound8** ppDS)
{
	HRESULT hr;

	// Create IDirectSound using the primary sound device
    if( FAILED( hr = DirectSoundCreate8( NULL, ppDS, NULL ) ) )
        return false;

    // Set DirectSound coop level
    if( FAILED( hr = (*ppDS)->SetCooperativeLevel( hWnd, DSSCL_PRIORITY ) ) )
        return false;

	return true;
}

/**
 * \brief Captures current window parameters, to restore them later
 * \param hWnd handle to application window
 * \author Petar Bajic 
 * \date January, 16 2007.
 */
void CaptureWindowParameters(HWND hWnd)
{
	ZeroMemory( &pwp, sizeof(WINDOWPLACEMENT) );
	pwp.length = sizeof(WINDOWPLACEMENT);
	GetWindowPlacement( hWnd, &pwp );
	bIsTopmost = ( (GetWindowLong(hWnd,GWL_EXSTYLE) & WS_EX_TOPMOST) != 0); 
	dwStyle = GetWindowLong( hWnd, GWL_STYLE );
	dwStyle &= ~WS_MAXIMIZE & ~WS_MINIMIZE; // remove minimize/maximize style
}

/**
 * \brief Toggles fullscreen/windowed mode.
 * \param oldScreenWidth screen width of previous application window state
 * \author Petar Bajic 
 * \date January, 16 2007.
 */
void ToggleFullScreen(HWND hWnd, LPDIRECT3DDEVICE9 pd3dDevice, D3DPRESENT_PARAMETERS** ppPP, LPDIRECT3D9 pD3D, 
					  int* oldScreenWidth, int* oldScreenHeight, int* screenWidth, int* screenHeight)
{
	if ((*ppPP)->Windowed)
	{
		//Go to fullscreen
		(**ppPP).Windowed = false;
		//Find out desktop resolution:
		D3DDISPLAYMODE DisplayMode;
		pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &DisplayMode);
		//Set fullscreen parameters
		(**ppPP).BackBufferFormat = D3DFMT_A8R8G8B8;
		(**ppPP).BackBufferWidth = DisplayMode.Width;
		(**ppPP).BackBufferHeight = DisplayMode.Height;
		*oldScreenWidth = *screenWidth;
		*oldScreenHeight = *screenHeight;
		*screenWidth = (**ppPP).BackBufferWidth;
		*screenHeight = (**ppPP).BackBufferHeight;
		//Save windowed mode style
		ZeroMemory( &pwp, sizeof(WINDOWPLACEMENT) );
		pwp.length = sizeof(WINDOWPLACEMENT);
		GetWindowPlacement( hWnd, &pwp );
		bIsTopmost = ( (GetWindowLong(hWnd,GWL_EXSTYLE) & WS_EX_TOPMOST) != 0); 
		dwStyle = GetWindowLong( hWnd, GWL_STYLE );
		dwStyle &= ~WS_MAXIMIZE & ~WS_MINIMIZE; // remove minimize/maximize style
	}
	else
	{
		//Go to windowed mode
		(**ppPP).Windowed = true;
		//Find out desktop format:
		D3DDISPLAYMODE DisplayMode;
		pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &DisplayMode);
		//Set Windowed parameters
		(**ppPP).BackBufferFormat = DisplayMode.Format;
		(**ppPP).BackBufferWidth = *oldScreenWidth;
		(**ppPP).BackBufferHeight = *oldScreenHeight;
		*oldScreenWidth = *screenWidth;
		*oldScreenHeight = *screenHeight;		
		*screenWidth = (**ppPP).BackBufferWidth;
		*screenHeight = (**ppPP).BackBufferHeight;
        // Also restore the z-order, style, and position of window to previous state
        HWND hWndInsertAfter = bIsTopmost ? HWND_TOPMOST : HWND_NOTOPMOST;
		SetWindowLong(hWnd, GWL_STYLE, dwStyle);
		SetWindowPos(hWnd, hWndInsertAfter, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_FRAMECHANGED);
		SetWindowPlacement(hWnd, &pwp );
	}

	pd3dDevice->Reset(*ppPP);
}

/**
 * \brief This function resets D3D device.
 * \param pPP new presentation parameters
 * \author Petar Bajic 
 * \date January, 16 2007.
 */
void Reset3D(LPDIRECT3DDEVICE9 pd3dDevice, D3DPRESENT_PARAMETERS* pPP)
{
	HRESULT hr;
	if (FAILED(hr = pd3dDevice->Reset(pPP)))
		hr = E_FAIL;
}

/**
 * \brief World, View and Projection matrices are set with passed function parameters.
 * \param eye position of the camera
 * \param lookAt position where camera is pointed to.
 * \author Petar Bajic 
 * \date January, 16 2007.
 */
void SetupMatrices(LPDIRECT3DDEVICE9* ppd3dDevice, D3DXVECTOR3* eye, D3DXVECTOR3* lookAt, D3DXVECTOR3* upVec, float aspectRatio)
{
    D3DXMATRIXA16 matWorld;
	D3DXMatrixIdentity(&matWorld);
    (*ppd3dDevice)->SetTransform( D3DTS_WORLD, &matWorld );
    D3DXMATRIXA16 matView;
    D3DXMatrixLookAtLH( &matView, eye, lookAt, upVec );
    (*ppd3dDevice)->SetTransform( D3DTS_VIEW, &matView );
    D3DXMATRIXA16 matProj;
    D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, aspectRatio, 1.0f, 1000.0f );
    (*ppd3dDevice)->SetTransform( D3DTS_PROJECTION, &matProj );
}
