//=================================================
//HighTide 
//Gos2 Test App
//
//Written by Matt Bronder
//Ported to GOS2 by Bob Crocco 8/2000
//=================================================

#include <Gos2\Gos.h>
#include "d3dx8.h"
#include "HighTide.h"
#include "Helper.h"

HighTideApp HTApp;

void HighTideApp::PreferredDisplay( int &Windowed, unsigned int &Width, unsigned int &Height, D3DFORMAT &Format )
{
#if 0
	Windowed = false;
#else
	Windowed = true;
	Width = 640;
	Height = 480;
#endif

}


//Initialize: Initialize the application's state
bool HighTideApp::Initialize( _TCHAR *pCommandLine )
{
	UINT i;



	SPEW_ENABLE(_T("GROUP_MEMORY"));

	//create a heap
	GosHeap* pDefHeap = GosHeap::Create(_T("DefHeap"),64000000);
	GosHeap::PushCurrent(pDefHeap);

	_TCHAR * testptr1 = (_TCHAR *) GosMalloc(32, DBGNAME("TestPtr1"));
	wcscpy(testptr1, _T("Leak Me 1"));
	_TCHAR * testptr2 = (_TCHAR *) new( pDefHeap ) _TCHAR[32];
	wcscpy(testptr2, _T("Leak Me 2"));
	_TCHAR * testptr3 = (_TCHAR *) new( DBGNAME("TestPtr3") ) _TCHAR[64];
	wcscpy(testptr3, _T("Leak Me 3"));

	m_pOneFont = GosFont3D::CreateFromFile( _T("arial black.ttf"), 0 );
	m_pTwoFont = GosFont3D::CreateFromFile( _T("Comic Sans MS.ttf"), 0 );
	m_pRedFont = GosFont3D::CreateFromFile( _T("Courier New.ttf"), 0 );
	m_pBluFont = GosFont3D::CreateFromFile( _T("Monotype Corsiva.ttf"), 0 );
	m_pComicFont = GosFont3D::CreateFromFile( _T("assets\\Graphics\\comic.d3f"), 0 );


	//BUGBUG: invalid backbuffer format
	//Gos3D::SetDisplay( 800, 600, D3DFMT_A8R8G8B8, false );

	//Set up a material
	
		// White material
	SetMaterial(&m_matWhite, RGB_MAKE(255, 255, 255),RGB_MAKE(16, 16, 16), RGB_MAKE(128, 128, 128),RGB_MAKE(0, 0, 0), (float)(10.0f));
 
	//Set Up the Background
	m_ptBgStars = GosTexture::CreateFromFile( _T("assets\\Graphics\\stars.bmp") );
	m_prBackground[0] = TLVERTEX(D3DXVECTOR3(0.0f, (float)(Gos3D::Height()), 0.9999f), 0.01f, RGBA_MAKE(255,255,255,255), RGB_MAKE(0,0,0), 0.0f, 1.0f);
    m_prBackground[1] = TLVERTEX(D3DXVECTOR3(0.0f, 0.0f, 0.9999f), 0.01f, RGBA_MAKE(255,255,255,255), RGB_MAKE(0,0,0), 0.0f, 0.0f);
    m_prBackground[2] = TLVERTEX(D3DXVECTOR3((float)(Gos3D::Width()), 0.0f, 0.9999f), 0.01f, RGBA_MAKE(255,255,255,255), RGB_MAKE(0,0,0), 1.0f, 0.0f);
    m_prBackground[3] = TLVERTEX(D3DXVECTOR3((float)(Gos3D::Width()), (float)(Gos3D::Height()), 0.9999f), 0.01f, RGBA_MAKE(255,255,255,255), RGB_MAKE(0,0,0), 1.0f, 1.0f);

	//Set Up the Beach
	m_ptBeach = GosTexture::CreateFromFile( _T("assets\\Graphics\\beach.bmp") );
	HTApp.SetupBeach();
	//Set Up the Cliffs
	HTApp.SetupCliff();
	//Set Up Lighthouse
	HTApp.SetupLighthouse();
	//Set Up Waves
	HTApp.SetupWaves();

	//Set Up lighthouse light
	D3DXVECTOR3 vLightDir;
	m_fRotationDelta = LIGHT_ROTATION_DELTA1;
    m_fElevationDelta = LIGHT_ELEVATION_DELTA;
    m_bRotateLight = TRUE;

	//BUGBUG: no create revolution with only 5 params, fudged the rest of the data
	CreateRevolution(&m_rvLightCone, 20.0f, 1, 32, LightConeRadius,NULL,0.001f,TRUE,2.0f,4.0f);
        
    for (i = 0; i < m_rvLightCone.uCenterVertices; i++)
	{
        m_rvLightCone.prCenter[i].vPosition.y -= 20.0f;
    }

	 // Rotate the light cone: angle it toward the water 
    m_fLightElevation = LIGHT_CONE_ANGLE;
    m_fLightRotation = LIGHT_ROTATION_DELTA1;

	InitMatrix(&m_mLightCone,
        (float)( 1.0f), (float)( 0.0f), (float)( 0.0f), (float)( 0.0f),
        (float)( 0.0f), (float)( cos(m_fLightElevation)), 
        (float)( sin(m_fLightElevation)), (float)( 0.0f),
        (float)( 0.0f), (float)(-sin(m_fLightElevation)), 
        (float)( cos(m_fLightElevation)), (float)( 0.0f),
        (float)( 0.0f), (float)( 0.0f), (float)( 0.0f), (float)( 1.0f)
    );

    InitMatrix(&m_mConeRotation,
        (float)( cos(m_fLightRotation)), (float)( 0.0f), 
        (float)(-sin(m_fLightRotation)), (float)( 0.0f),
        (float)( 0.0f), (float)( 1.0f), (float)( 0.0f), (float)( 0.0f),
        (float)( sin(m_fLightRotation)), (float)( 0.0f), 
        (float)( cos(m_fLightRotation)), (float)( 0.0f),
        (float)( 0.0f), (float)( 0.0f), (float)( 0.0f), (float)( 1.0f)
    );

	InitMatrix(&m_mConePosition,
        (float)( 1.0f), (float)( 0.0f), (float)( 0.0f), (float)( 0.0f),
        (float)( 0.0f), (float)( 1.0f), (float)( 0.0f), (float)( 0.0f),
        (float)( 0.0f), (float)( 0.0f), (float)( 1.0f), (float)( 0.0f),
        (float)(-1.0f), (float)( 2.85f), (float)( 0.0f), (float)( 1.0f)
    );

    SetMaterial(&m_matLightCone, RGB_MAKE(255, 255, 255), 
                                    RGB_MAKE(0, 0, 0), RGB_MAKE(196, 196, 196), 
                                    RGB_MAKE(255, 255, 255), 10.0f);

    SetMaterialTranslucency(&m_matLightCone, 0.15f);

	// Create the lighthouse spotlight
    vLightDir = D3DXVECTOR3(0.0f, -1.0f, 0.0f);
    D3DXVec3TransformCoord(&vLightDir, &vLightDir, &m_mLightCone);
    SetSpotLight(&m_ConeLight, D3DXVECTOR3(-1.0f, 2.85f, 0.0f), 
                                     vLightDir, RGB_MAKE(255, 255, 255), 
                                     D3DLIGHT_RANGE_MAX, 1.0f, 1.0f, 0.0f, 0.0f, 
                                     M_PI / 22.0f, M_PI / 19.0f);
    m_fMaxIntensity = -vLightDir.z;


	//Set Up the overhead light 
	SetLight(&m_DefLight,D3DLIGHT_DIRECTIONAL,D3DXVECTOR3(0.0f,0.0f,0.0f),D3DXVECTOR3(-0.1f,-1.0f,1.0f),RGB_MAKE(128, 128, 128),RGB_MAKE(128, 128, 128),RGB_MAKE(128, 128, 128),D3DLIGHT_RANGE_MAX,1.0f,1.0f,0.0f,0.0f,0.0f,0.0f);
	
	//Set Background color to black
	m_bgColor = 0xff000000;

	//Set up viewpoint
	// Initialize matrices ( m_WorldMatrix is set each frame )
	D3DXMatrixLookAtLH( &m_ViewMatrix, &D3DXVECTOR3(-4.8f, 3.0f, 15.0f),&D3DXVECTOR3(-2.5f, 0.5f, 5.0f), &D3DXVECTOR3( 0.f, 1.f, 0.f ) );
	//D3DXMatrixLookAtLH( &m_ViewMatrix, &D3DXVECTOR3(0.0f, 3.0f, 2.0f),&D3DXVECTOR3(-1.0f,3.0f, 0.0f), &D3DXVECTOR3( 0.f, 1.f, 0.f ) );
	D3DXMatrixPerspectiveFovLH( &m_ProjectionMatrix, D3DX_PI/4, 1.f, .1f, 1000.f );

	return true;
}

//Render: Render a single frame
void HighTideApp::Render()
{
	D3DXMATRIX  m;
	D3DXVECTOR3 r;

	// Clear the background to blue and begin the scene
	Gos3D::Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, m_bgColor, 1.0f, 0L );

	Gos3D::BeginScene();

	D3DXMatrixIdentity( &m_WorldMatrix );
	Gos3D::SetTransform( D3DTS_VIEW, &m_ViewMatrix );
	Gos3D::SetTransform( D3DTS_WORLD, &m_WorldMatrix );
	Gos3D::SetTransform( D3DTS_PROJECTION, &m_ProjectionMatrix );

	//Light
	Gos3D::SetLight( 0, &m_DefLight );
	Gos3D::LightEnable( 0, true );
	Gos3D::SetRenderState(D3DRS_SPECULARENABLE, TRUE);
	Gos3D::SetRenderState( D3DRS_LIGHTING, true );

	//Background
	Gos3D::SetFVFShader(FVF_TLVERTEX);
	Gos3D::SetTexture(0, m_ptBgStars);
	Gos3D::DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, m_prBackground, sizeof(TLVERTEX));
	
	//Beach
	Gos3D::SetMaterial( &m_matWhite);
	Gos3D::SetTexture( 0, m_ptBeach );
	Gos3D::SetFVFShader(FVF_HHVERTEX);
	Gos3D::DrawIndexedPrimitiveUP(D3DPT_TRIANGLESTRIP,0,m_uNumBeachVertices,m_uNumBeachIndices - 2,&m_pwBeach[0], D3DFMT_INDEX16, &m_prBeach[0], sizeof(HHVERTEX));

	//Cliffs
	Gos3D::DrawIndexedPrimitiveUP(D3DPT_TRIANGLESTRIP,0,m_uNumCliffVertices, m_uNumCliffIndices - 2,&m_pwCliff[0], D3DFMT_INDEX16, &m_prCliff[0], sizeof(HHVERTEX));
	
	//LightHouse
	RenderLighthouse();
	
	//Waves
	 // Render the waves
    D3DXMatrixIdentity( &m_WorldMatrix );
	Gos3D::SetTransform( D3DTS_WORLD, &m_WorldMatrix );
	Gos3D::SetMaterial(&m_matWave);
	Gos3D::SetTexture(0, NULL);
	Gos3D::SetRenderState(D3DRS_SPECULARENABLE, TRUE);
	Gos3D::DrawIndexedPrimitiveUP(D3DPT_TRIANGLESTRIP, 0, 
            m_uNumWaveVertices, m_uNumWaveIndices - 2, 
            &m_pwWave[0], D3DFMT_INDEX16, &m_prWave[0], sizeof(HHVERTEX));
	Gos3D::SetRenderState(D3DRS_SPECULARENABLE, FALSE);
	//Lighthouse Cone
	  // Update the spotlight position
    D3DXVec3TransformCoord((D3DXVECTOR3*)&m_ConeLight.Direction, D3DXVec3TransformCoord(&r, &D3DXVECTOR3(0.0f, -1.0f, 0.0f), &m_mLightCone), 
                            &m_mConeRotation);
	D3DXMatrixMultiply(&m_mLightDirection, &m_mLightCone, &m_mConeRotation);
    D3DXMatrixMultiply(&m, &m_mLightDirection, &m_mConePosition);
	Gos3D::SetTransform(D3DTS_WORLD, &m);
	Gos3D::SetMaterial(&m_matLightCone);

	Gos3D::SetRenderState(D3DRS_SRCBLEND,(DWORD)D3DBLEND_SRCALPHA);
	Gos3D::SetRenderState(D3DRS_DESTBLEND,(DWORD)D3DBLEND_INVSRCALPHA);

	Gos3D::SetRenderState(D3DRS_SPECULARENABLE, FALSE);
	Gos3D::SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
	Gos3D::DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, 
            m_rvLightCone.uCenterVertices, m_rvLightCone.uCenterIndices / 3, 
            m_rvLightCone.pwCenter, D3DFMT_INDEX16, m_rvLightCone.prCenter, sizeof(HHVERTEX));
	Gos3D::SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
	Gos3D::SetRenderState(D3DRS_SPECULARENABLE, TRUE);

	//cone light
	Gos3D::SetLight( 1, &m_ConeLight );
	Gos3D::LightEnable( 1, true );
	Gos3D::SetRenderState(D3DRS_SPECULARENABLE, TRUE);
	Gos3D::SetRenderState( D3DRS_LIGHTING, true );

	DWORD height, width;
	DWORD left, top;
	static DWORD count = 0;


	top = 100;
	left = 12;
	GosText::SetAttributes( m_pOneFont, 0xffffffff, -24, false, false, true, false, 0);
	GosText::Set(GosText::Position, left, top );
	GosText::Draw(_T("One Fish"));
	GosText::StringLength( &width , &height , _T("One Fish"));
	left = width+12;
	GosText::Set(GosText::Font, m_pTwoFont );
	GosText::Set(GosText::Position, left, top );
	GosText::Set(GosText::ForegroundColor, 0xffffff00 );
	GosText::Draw(_T("Two Fish"));
	GosText::StringLength( &width , &height , _T("Two Fish"));
	left += width+12;
	GosText::Set(GosText::Font, m_pRedFont );
	GosText::Set(GosText::Position, left, top );
	GosText::Set(GosText::ForegroundColor, 0xffff0000 );
	GosText::Draw(_T("Red Fish"));
	GosText::StringLength( &width , &height , _T("Red Fish"));
	left += width+12;
	GosText::Set(GosText::Font, m_pBluFont );
	GosText::Set(GosText::Position, left, top );
	GosText::Set(GosText::ForegroundColor, 0xff0000ff );
	GosText::Draw(_T("Blue Fish"));
	GosText::StringLength( &width , &height , _T("Red Fish"));
	left = 12;
	top += height;

	if( count & ~0xff )
	{
		static frames = 0xff;

		if(frames--)
		{
			GosText::SetAttributes( m_pComicFont, 0xffffffff, 1.f, true, true, true, false, 0);
			GosText::Set(GosText::Position, left, top );
			GosText::Set(GosText::ForegroundColor, 0xff00ffff );
			GosText::Draw(_T("Some random string\nto mess up the !%&$! cache!!\nAnother random string\nto mess up the !%&$! cache!!"));
			GosText::StringLength( &width , &height , _T("Some random string\nto mess up the !%&$! cache!!\nnAnother random string\nto mess up the !%&$! cache!!"));
			left = 12;
			top += height;
		}
		else
		{
			count = 0;
			frames = 0xff;
			GosText::Flush();
		}
		
	}
	else
		count++;

	GosText::SetAttributes( m_pTwoFont, 0xffff00ff, -24, false, false, true, false, 0);
	GosText::Set(GosText::Position, left, top );
	GosText::Draw(_T("x = %.4f"), 0.025);
	GosText::StringLength( &width , &height , _T("x = %.4f"), 0.025);
	top += height;
	GosText::Set(GosText::Position, left, top );
	GosText::Draw(_T("One Fish - Two Fish\n - Red Fish - Blue Fish"));

	Gos3D::EndScene();

}
	
//Step: Move the scene forward one frame			
bool HighTideApp::Step()
{
	UINT x,y,i;
	float       fAngle;
    float       fHeight;

	//if waves not set up yet
	if(!m_pfBaseAngle)
	   return true;

	// Update the position of the waves
   for (y = 0; y < WAVE_GRID_DIM_Y; y++)
   {
        for (x = 0; x < WAVE_GRID_DIM_X; x++)
		{

            fHeight = 0.0f;

            for (i = 0; i < NUM_WAVE_SOURCES; i++)
			{
                fAngle = (m_pfBaseAngle[y * WAVE_GRID_DIM_X * NUM_WAVE_SOURCES + x * NUM_WAVE_SOURCES + i] + m_pws[i].fPhase);
                fHeight += m_pws[i].fAmplitude * m_pfSinLookup[(UINT)((fAngle < 0.0f? (fAngle+M_2PI):fAngle) / M_2PI * 1023.0)];
            }

            m_prWave[y * WAVE_GRID_DIM_X + x].y = fHeight;
        }
    }

    // Update wave normals
    GenerateWaveNormals(&m_prWave[0], m_pfSqrtLookup);

    // Update phases
    for (i = 0; i < NUM_WAVE_SOURCES; i++) {

        m_pws[i].fPhase -= m_pws[i].fFrequency;

        if (m_pws[i].fPhase < -M_2PI) {
            m_pws[i].fPhase += M_2PI;
        }
    }

	//rotate lighthouse
	GosUser::Poll();
	GosUser::GetStatus( &m_jButtons, &m_jX, &m_jY );

	//toggle lighthouse direction
	if(m_jButtons & 2) //large left pistol grip button
	{
		if(m_fRotationDelta == LIGHT_ROTATION_DELTA2)
			m_fRotationDelta = -LIGHT_ROTATION_DELTA2;
		else
			m_fRotationDelta = LIGHT_ROTATION_DELTA2;
	}

	m_fLightRotation += m_fRotationDelta;
    if (m_fLightRotation > M_2PI) 
        m_fLightRotation -= M_2PI;
    else if (m_fLightRotation < 0) 
        m_fLightRotation += M_2PI;


	//adjust lighthouse elevation
	if(m_jButtons & 4)							//small top right pistol grip button
		m_fLightElevation += m_fElevationDelta;	//up
	else if(m_jButtons & 8)						//small bottom right pistol grop button
		m_fLightElevation -= m_fElevationDelta; //down
	
	if (m_fLightElevation < M_PIDIV4)
	    m_fLightElevation = M_PIDIV4;
	else if (m_fLightElevation > M_3PIDIV4)
		m_fLightElevation = M_3PIDIV4;

    //else if (m_fLightElevation > 1.208305f)
     //   m_fLightElevation = 1.208305f;

	m_mLightCone._22 = (float)(cos(m_fLightElevation));
    m_mLightCone._23 = (float)(sin(m_fLightElevation));
    m_mLightCone._32 = -m_mLightCone._23;
    m_mLightCone._33 = m_mLightCone._22;

    m_mConeRotation._11 = (float)(cos(m_fLightRotation));
    m_mConeRotation._13 = (float)(-sin(m_fLightRotation));
    m_mConeRotation._31 = -m_mConeRotation._13;
    m_mConeRotation._33 = m_mConeRotation._11;

	return true;
}

//Shutdown: Called at game shutdown time
void HighTideApp::Shutdown()
{
	//BUGBUG: ???: when Matt frees m_prBeach he casts it to an LPBYTE first...?
	delete m_ptBeach;
	delete m_ptBgStars;

	GosFree(m_prBeach);
	GosFree(m_pwBeach);
	
	GosFree(m_prCliff);
    GosFree(m_pwCliff);

	ReleaseLighthouse();

	if(m_pOneFont)
		delete m_pOneFont;
	if(m_pTwoFont)
		delete m_pTwoFont;
	if(m_pRedFont)
		delete m_pRedFont;
	if(m_pBluFont)
		delete m_pBluFont;
	if(m_pComicFont)
		delete m_pComicFont;

	// we should probably have client applications free their heap...
	// not just let them assume we'll do it for them!
}		

//Name:	Name of the game
TCHAR* HighTideApp::Name()
{
	return _T("High Tide");
}


//-----------------------------------------
//Application Functions
//-----------------------------------------
void HighTideApp::SetupBeach()
{
	
	UINT x,y;

	//set up beach vertices
	m_uNumBeachVertices = BEACH_GRID_DIM_Y * BEACH_GRID_DIM_X;
	m_prBeach = (PHHVERTEX) GosMalloc(m_uNumBeachVertices * sizeof(HHVERTEX),DBGNAME("Beach Vertices"));
	
	for (y = 0; y < BEACH_GRID_DIM_Y; y++) 
	{
        for (x = 0; x < BEACH_GRID_DIM_X; x++) 
		{

          m_prBeach[y * BEACH_GRID_DIM_X + x] = HHVERTEX(D3DXVECTOR3(3.5f 
                            + (float)(x) / (float)(BEACH_GRID_DIM_X) 
                           * BEACH_GRID_WIDTH - (BEACH_GRID_WIDTH / 2.0f), 
                           ((float)(y) / (float)(BEACH_GRID_DIM_Y)
                           * BEACH_GRID_DEPTH) - BEACH_GRID_OFFSET / 2.0f 
                           + RND() * BEACH_GRID_OFFSET, 
                           0.5f - ((float)(y) / (float)(BEACH_GRID_DIM_Y)
                           * BEACH_GRID_HEIGHT)), 
                           D3DXVECTOR3(0.0f, 1.0f, 0.0f), 
                           (float)(x) / (float)(BEACH_GRID_DIM_X),
                           (float)(y) / (float)(BEACH_GRID_DIM_Y));
        }
    }

    GenerateBeachNormals(&m_prBeach[0]);

	//set up beach indices
	m_uNumBeachIndices = (BEACH_GRID_DIM_Y - 1) * (BEACH_GRID_DIM_X + 1) * 2 - 2;
	m_pwBeach = (LPWORD) GosMalloc(((BEACH_GRID_DIM_Y - 1) * (BEACH_GRID_DIM_X + 1) * 2) * sizeof(WORD),
		DBGNAME("Beach Indices"));
	
    for (y = 0; y < BEACH_GRID_DIM_Y - 1; y++) {

        for (x = 0; x < BEACH_GRID_DIM_X; x++) {

            m_pwBeach[y * (BEACH_GRID_DIM_X + 1) * 2 + x * 2] = 
                            (y + 1) * BEACH_GRID_DIM_X + x;
            m_pwBeach[y * (BEACH_GRID_DIM_X + 1) * 2 + x * 2 + 1] = 
                            y * BEACH_GRID_DIM_X + x;
        }

        m_pwBeach[y * (BEACH_GRID_DIM_X + 1) * 2 + BEACH_GRID_DIM_X * 2] = 
                            y * BEACH_GRID_DIM_X + BEACH_GRID_DIM_X - 1;
        m_pwBeach[y * (BEACH_GRID_DIM_X + 1) * 2 + BEACH_GRID_DIM_X * 2 + 1] = 
                            (y + 2) * BEACH_GRID_DIM_X;
    }
		
}

//SetupCliff
void HighTideApp::SetupCliff()
{
	UINT x,y;

	//set up cliff vertices
	m_uNumCliffVertices = CLIFF_GRID_DIM_Y * CLIFF_GRID_DIM_X;
	m_prCliff = (PHHVERTEX) GosMalloc(m_uNumCliffVertices * sizeof(HHVERTEX),DBGNAME("Cliff Vertices"));

	for (y = 0; y < CLIFF_GRID_DIM_Y; y++) {

        for (x = 0; x < CLIFF_GRID_DIM_X; x++) {

            m_prCliff[y * CLIFF_GRID_DIM_X + x] = HHVERTEX(D3DXVECTOR3(3.5f 
                            + (float)(x) / (float)(CLIFF_GRID_DIM_X) 
                           * CLIFF_GRID_WIDTH - (CLIFF_GRID_WIDTH / 2.0f), 
                           (CLIFF_GRID_HEIGHT - 
                           ((float)(y) / (float)(CLIFF_GRID_DIM_Y)
                           * CLIFF_GRID_HEIGHT))  - CLIFF_GRID_OFFSET / 2.0f 
                           + RND() * CLIFF_GRID_OFFSET, 
                           0.5f - BEACH_GRID_HEIGHT - CLIFF_GRID_OFFSET / 2.0f 
                           + RND() * CLIFF_GRID_OFFSET), 
                           D3DXVECTOR3(0.0f, 0.0f, -1.0f), 
                           (float)(x) / (float)(CLIFF_GRID_DIM_X),
                           (float)(y) / (float)(CLIFF_GRID_DIM_Y));
        }
    }

	GenerateCliffNormals(&m_prCliff[0]);

	//set up cliff indices
	m_uNumCliffIndices = (CLIFF_GRID_DIM_Y - 1) * (CLIFF_GRID_DIM_X + 1) * 2 - 2;
	m_pwCliff = (LPWORD) GosMalloc(((CLIFF_GRID_DIM_Y - 1) * (CLIFF_GRID_DIM_X + 1) * 2) * sizeof(WORD),
		DBGNAME("Cliff Indices"));

	for (y = 0; y < CLIFF_GRID_DIM_Y - 1; y++)
	{
        for (x = 0; x < CLIFF_GRID_DIM_X; x++)
		{

            m_pwCliff[y * (CLIFF_GRID_DIM_X + 1) * 2 + x * 2 + 1] = 
                            (y + 1) * CLIFF_GRID_DIM_X + x;
            m_pwCliff[y * (CLIFF_GRID_DIM_X + 1) * 2 + x * 2] = 
                            y * CLIFF_GRID_DIM_X + x;
        }

        m_pwCliff[y * (CLIFF_GRID_DIM_X + 1) * 2 + CLIFF_GRID_DIM_X * 2] = 
                            y * CLIFF_GRID_DIM_X + CLIFF_GRID_DIM_X - 1;
        m_pwCliff[y * (CLIFF_GRID_DIM_X + 1) * 2 + CLIFF_GRID_DIM_X * 2 + 1] = 
                            (y + 2) * CLIFF_GRID_DIM_X;
    }


}

//SetupLighthouse
void HighTideApp::SetupLighthouse()
{
	PLIGHTHOUSE plh = &m_lhLighthouse;

    UINT i;

    // Clear out the lighthouse structure
    memset(plh, 0, sizeof(LIGHTHOUSE));

    InitMatrix(&plh->mWorld,
        (float)( 1.0f), (float)( 0.0f), (float)( 0.0f), (float)( 0.0f),
        (float)( 0.0f), (float)( 1.0f), (float)( 0.0f), (float)( 0.0f),
        (float)( 0.0f), (float)( 0.0f), (float)( 1.0f), (float)( 0.0f),
        (float)(-1.0f), (float)( 0.0f), (float)( 0.0f), (float)( 1.0f)
    );

    // Initialize the lighthouse base
   CreateRevolution(&plh->rvBase, 0.21f, 1, 6, BaseRadius, NULL, 0.001f, 
                            TRUE, 2.0f, 2.0f); 
  

    // Initialize the lighthouse trunk
    CreateRevolution(&plh->rvTrunk, 2.4f, 1, 16, TrunkRadius, NULL, 0.001f,
                            TRUE, 2.0f, 8.0f); 
   
    for (i = 0; i < plh->rvTrunk.uCenterVertices; i++) {
        plh->rvTrunk.prCenter[i].vPosition.y += 0.2f;
    }

    // Initialize the lighthouse balcony
    CreateRevolution(&plh->rvBalcony, 0.10f, 1, 6, BalconyRadius, NULL, 
                            0.001f, TRUE, 1.0f, 0.25f); 
   
    for (i = 0; i < plh->rvBalcony.uCenterVertices; i++) {
        plh->rvBalcony.prCenter[i].vPosition.y += 2.6f;
    }

    // Initialize the lighthouse portal
    CreateRevolution(&plh->rvPortal, 0.20f, 1, 6, PortalRadius, NULL, 
                            0.001f, TRUE, 1.0f, 0.25f); 
    
    plh->prPortal = (PLVERTEX)GosMalloc(plh->rvPortal.uCenterVertices * sizeof(LVERTEX),DBGNAME("Lighthouse Portal"));
   
    for (i = 0; i < plh->rvPortal.uCenterVertices; i++) {
        plh->rvPortal.prCenter[i].vPosition.y += 2.7f;
        plh->prPortal[i].vPosition = plh->rvPortal.prCenter[i].vPosition;
        plh->prPortal[i].cDiffuse = RGB_MAKE(64, 64, 64);
    }

    // Initialize the lighthouse roof
    CreateRevolution(&plh->rvRoof, 0.25f, 1, 6, RoofRadius, NULL, 0.001f, 
                            TRUE, 0.5f, 0.25f);
   
    for (i = 0; i < plh->rvRoof.uCenterVertices; i++) {
        plh->rvRoof.prCenter[i].vPosition.y += 2.9f;
    }

    // Initialize the material and textures
    SetMaterial(&plh->material, RGB_MAKE(255, 255, 255),
                            RGB_MAKE(16, 16, 16), RGB_MAKE(128, 128, 128), 
                            RGB_MAKE(128, 128, 128), (float)(10.0f));

    plh->pd3dtTrunk = GosTexture::CreateFromFile( _T("assets\\Graphics\\Rockwall.bmp") );
    plh->pd3dtRoof  = GosTexture::CreateFromFile( _T("assets\\Graphics\\RedBrick.bmp") );
}

//SetupWaves
void HighTideApp::SetupWaves()
{
	UINT x,y,i;
	D3DXVECTOR3 vDistance, vPoint;
	float       fDistance;

	//setup wave sources
	m_pws = (PWAVESOURCE)GosMalloc(NUM_WAVE_SOURCES * sizeof(WAVESOURCE),DBGNAME("Wave Sources"));
    InitWaveSources(m_pws);
	
	//setup vertices
	m_uNumWaveVertices = WAVE_GRID_DIM_Y * WAVE_GRID_DIM_X;
    m_prWave = (PHHVERTEX)GosMalloc(m_uNumWaveVertices * sizeof(HHVERTEX),DBGNAME("Wave Vertices"));
   
	for (y = 0; y < WAVE_GRID_DIM_Y; y++) {

        for (x = 0; x < WAVE_GRID_DIM_X; x++) {

            m_prWave[y * WAVE_GRID_DIM_X + x] = HHVERTEX(D3DXVECTOR3((float)(x) 
                            / (float)(WAVE_GRID_DIM_X) 
                           * WAVE_GRID_WIDTH - (WAVE_GRID_WIDTH / 2.0f), 
                           0.0f, 
                           WAVE_GRID_HEIGHT 
                           - ((float)(y) / (float)(WAVE_GRID_DIM_Y)
                           * WAVE_GRID_HEIGHT)), 
                           D3DXVECTOR3(0.0f, 1.0f, 0.0f), 0.0f, 0.0f);
        }
    }

	//setup indicies
	m_uNumWaveIndices = (WAVE_GRID_DIM_Y - 1) * (WAVE_GRID_DIM_X + 1) * 2 - 2;
    m_pwWave = (LPWORD)GosMalloc(((WAVE_GRID_DIM_Y - 1) * (WAVE_GRID_DIM_X + 1) * 2) * sizeof(WORD),DBGNAME("Wave Indices"));

	for (y = 0; y < WAVE_GRID_DIM_Y - 1; y++) {

        for (x = 0; x < WAVE_GRID_DIM_X; x++) {

            m_pwWave[y * (WAVE_GRID_DIM_X + 1) * 2 + x * 2] = 
                            (y + 1) * WAVE_GRID_DIM_X + x;
            m_pwWave[y * (WAVE_GRID_DIM_X + 1) * 2 + x * 2 + 1] = 
                            y * WAVE_GRID_DIM_X + x;
        }

        m_pwWave[y * (WAVE_GRID_DIM_X + 1) * 2 + WAVE_GRID_DIM_X * 2] = 
                            y * WAVE_GRID_DIM_X + WAVE_GRID_DIM_X - 1;
        m_pwWave[y * (WAVE_GRID_DIM_X + 1) * 2 + WAVE_GRID_DIM_X * 2 + 1] = 
                            (y + 2) * WAVE_GRID_DIM_X;
    }

	//setup wave base angles
    m_pfBaseAngle = (float*)GosMalloc((WAVE_GRID_DIM_Y * WAVE_GRID_DIM_X * NUM_WAVE_SOURCES) * sizeof(float),DBGNAME("Wave Base Angles"));
	for (y = 0; y < WAVE_GRID_DIM_Y; y++) {

        for (x = 0; x < WAVE_GRID_DIM_X; x++) {

            for (i = 0; i < NUM_WAVE_SOURCES; i++) {
                
                vPoint = D3DXVECTOR3(m_prWave[y * WAVE_GRID_DIM_X + x].x, 0.0f, 
                            m_prWave[y * WAVE_GRID_DIM_X + x].z);
                vDistance = vPoint - m_pws[i].vPos;
                fDistance = D3DXVec3Length(&vDistance);
                while (fDistance > m_pws[i].fWavelength) {
                    fDistance -= m_pws[i].fWavelength;
                }
                m_pfBaseAngle[y * WAVE_GRID_DIM_X * NUM_WAVE_SOURCES 
                            + x * NUM_WAVE_SOURCES + i] = 
                            M_2PI * (fDistance / m_pws[i].fWavelength);
            }
        }
    }

	// Create a wave material
    SetMaterial(&m_matWave, RGB_MAKE(0, 0, 128), 
                                RGB_MAKE(0, 0, 16), RGB_MAKE(255, 255, 255), 
                                RGB_MAKE(0, 0, 0), (float)(40.0f));
	
	// Initialize the sin lookup table
    for (i = 0; i < 1023; i++) {
        m_pfSinLookup[i] = (float)(sin((float)(i) / 1023.0f * M_2PI));
    }
    m_pfSinLookup[1023] = m_pfSinLookup[0];

	// Intialize the sqrt lookup table
    // NOTE: This table only works for sqrt parameter values 
    // between 0.000368 - 0.0005
    for (i = 0; i < SQRT_TABLE_SIZE; i++) {
        m_pfSqrtLookup[i] = (float)(sqrt((float)(i) / (float)(SQRT_TABLE_SIZE) 
                                    * 0.000132 + 0.000368));
	}

}

//RenderLighthouse
void HighTideApp::RenderLighthouse() 
{
    BYTE        color;
    UINT        i;
  
	PLIGHTHOUSE plh = &m_lhLighthouse;

    // Render the base of the lighthouse
	Gos3D::SetTransform( D3DTS_WORLD, &plh->mWorld);

	Gos3D::SetMaterial( &plh->material);
	Gos3D::SetTexture( 0, plh->pd3dtTrunk );
	Gos3D::DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, 
            plh->rvBase.uCenterVertices, plh->rvBase.uCenterIndices / 3, 
            plh->rvBase.pwCenter, D3DFMT_INDEX16, plh->rvBase.prCenter,
            sizeof(HHVERTEX));

	Gos3D::DrawPrimitiveUP(D3DPT_TRIANGLEFAN, plh->rvBase.uTopVertices - 2, 
            plh->rvBase.prTop, sizeof(VERTEX));

    // Render the trunk of the lighthouse
	Gos3D::DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, 
            plh->rvTrunk.uCenterVertices, plh->rvTrunk.uCenterIndices / 3, 
            plh->rvTrunk.pwCenter, D3DFMT_INDEX16, plh->rvTrunk.prCenter,
            sizeof(HHVERTEX));
    
    // Render the portal of the lighthouse
	Gos3D::SetTexture(0, NULL);

	m_fIntensity = m_ConeLight.Direction.z;
    if (m_fIntensity > 0.0f) {
        m_fIntensity = m_fIntensity / m_fMaxIntensity;
    }
    else {
        m_fIntensity = 0.0f;
    }

    color = (BYTE)(m_fIntensity * 200.0f);
    for (i = 0; i < plh->rvPortal.uCenterVertices; i++) {
        plh->prPortal[i].cSpecular = RGB_MAKE(color, color, color);
    }

	Gos3D::SetFVFShader(FVF_LVERTEX);
	Gos3D::SetRenderState(D3DRS_LIGHTING, FALSE);
	Gos3D::SetRenderState(D3DRS_SPECULARENABLE, TRUE);

	Gos3D::DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, 
            plh->rvPortal.uCenterVertices, plh->rvPortal.uCenterIndices / 3, 
            plh->rvPortal.pwCenter, D3DFMT_INDEX16, plh->prPortal,
            sizeof(LVERTEX));

	Gos3D::SetRenderState(D3DRS_SPECULARENABLE, FALSE);
    Gos3D::SetRenderState(D3DRS_LIGHTING, TRUE);

    // Render the roof of the lighthouse
	Gos3D::SetTexture(0, plh->pd3dtRoof);
    Gos3D::SetFVFShader(FVF_VERTEX);

	Gos3D::DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, 
            plh->rvBalcony.uCenterVertices, plh->rvBalcony.uCenterIndices / 3, 
            plh->rvBalcony.pwCenter, D3DFMT_INDEX16, plh->rvBalcony.prCenter,
            sizeof(HHVERTEX));

	Gos3D::DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, 
            plh->rvRoof.uCenterVertices, plh->rvRoof.uCenterIndices / 3, 
            plh->rvRoof.pwCenter, D3DFMT_INDEX16, plh->rvRoof.prCenter,
            sizeof(HHVERTEX));
}

void HighTideApp::ReleaseLighthouse() 
{

	PLIGHTHOUSE plh = &m_lhLighthouse;

    GosFree(plh->prPortal);
	//BUGBUG: deleting textures properly?
	delete plh->pd3dtRoof;
	delete plh->pd3dtTrunk;
	ReleaseRevolution(&m_rvLightCone);
    ReleaseRevolution(&plh->rvBase);
    ReleaseRevolution(&plh->rvTrunk);
    ReleaseRevolution(&plh->rvBalcony);
    ReleaseRevolution(&plh->rvPortal);
    ReleaseRevolution(&plh->rvRoof);

	// release wave information)
	GosFree(m_pws);
	GosFree(m_prWave);
	GosFree(m_pwWave);
	GosFree(m_pfBaseAngle);
}
