#include <math.h>

#include <d3dx8.h>

#include <algorithm>

#include "Timer.h"

#include ".\LD.h"
#include "GSMenu.h"
#include "GSGame.h"
#include "resource.h"

#include <ctime>


#pragma comment( lib, "d3d8.lib" )
#pragma comment( lib, "d3dx8.lib" )
#pragma comment( lib, "dsound.lib" )


LDApp       theApp;



LDApp::LDApp() :
  m_pd3d( NULL ),
  m_pd3dDevice( NULL ),
  m_pTextureFont( NULL ),
  m_pTextureMetal( NULL ),
  m_pCurrentGameState( NULL ),
  m_pNextState( NULL ),
  m_pFont( NULL ),
  m_pDirectSound( NULL ),
  m_fWavyTextTimeCount( 0.0f ),
  m_hIcon( NULL ),
  m_FullScreen( false )
{

  memset( &m_d3dPP, 0, sizeof( m_d3dPP ) );

  for ( int i = 0; i < 256; ++i )
  {
    m_bKeyPressed[i] = false;
    m_bKeyReleased[i] = true;
  }

  m_hIcon = LoadIcon( GetModuleHandle( NULL ), MAKEINTRESOURCE( IDI_ICON_BOMB ) );

  srand( (unsigned int)std::time( NULL ) );
}



LDApp::~LDApp()
{

  if ( m_hIcon )
  {
    DestroyIcon( m_hIcon );
  }

}



LRESULT LDApp::StaticWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{

  return theApp.WindowProc( hWnd, uMsg, wParam, lParam );

}



void LDApp::ToggleWindowMode()
{

  m_FullScreen          = !m_FullScreen;
  m_d3dPP.Windowed      = !m_FullScreen;

  DWORD       NewStyle    = WS_VISIBLE;
  DWORD       NewStyleEx  = 0;
  if ( m_FullScreen )
  {
    NewStyle |= WS_POPUP;
    NewStyleEx |= WS_EX_TOPMOST;

    // store previous position
    GetWindowPlacement( m_hWnd, &m_WPLWindowed );
    RECT    rc;

    GetWindowRect( m_hWnd, &rc );

    m_WindowPosWindowed.x = rc.left;
    m_WindowPosWindowed.y = rc.top;
  }
  else
  {
    NewStyle |= WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_OVERLAPPED;
    NewStyleEx = WS_EX_WINDOWEDGE;
  }
  // First volatile resources must be re-acquired
  SetWindowLong( m_hWnd, GWL_STYLE, NewStyle );
  SetWindowLong( m_hWnd, GWL_EXSTYLE, NewStyleEx );

  // Direct3D device can then be reset properly
  Unload3dResources();
  m_pd3dDevice->Reset( &m_d3dPP );

  if ( !m_FullScreen )
  {
    SetWindowPlacement( m_hWnd, &m_WPLWindowed );
    SetWindowPos( m_hWnd, HWND_NOTOPMOST, m_WindowPosWindowed.x, m_WindowPosWindowed.y,
                  0, 0, SWP_NOSIZE | SWP_FRAMECHANGED );
  }
  else
  {
    SetWindowPos( m_hWnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED );
  }
  // Once the main device is reset, the subdevices can also be reset
  Load3dResources();

}



LRESULT LDApp::WindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{

  switch ( uMsg )
  {
    case WM_KEYDOWN:
      m_bKeyPressed[wParam] = true;
      if ( m_pCurrentGameState )
      {
        m_pCurrentGameState->OnKeyDown( (int)wParam );
      }
      break;
    case WM_KEYUP:
      m_bKeyPressed[wParam] = false;
      m_bKeyReleased[wParam] = true;
      break;
    case WM_CHAR:
      if ( m_pCurrentGameState )
      {
        m_pCurrentGameState->OnChar( (int)wParam );
      }
      break;
    case WM_SYSKEYUP:
      if ( wParam == VK_RETURN )
      {
        // Toggle Windowed/Fullscreen mode
        ToggleWindowMode();
        return 0;
      }
      break;
    case WM_MOUSEMOVE:
    case WM_LBUTTONDOWN:
    case WM_LBUTTONUP:
    case WM_LBUTTONDBLCLK:
    case WM_RBUTTONDOWN:
    case WM_RBUTTONUP:
    case WM_RBUTTONDBLCLK:
      m_MouseX = (int)(short)LOWORD( lParam );
      m_MouseY = (int)(short)HIWORD( lParam );
      m_MouseButtons = (int)wParam;
      if ( m_pCurrentGameState )
      {
        m_pCurrentGameState->OnMouse( (int)(short)LOWORD( lParam ), (int)(short)HIWORD( lParam ), (int)wParam );
      }
      break;
    //case WM_MOUSEWHEEL:
    case 0x020a:
      // WM_MOUSEWHEEL
      if ( m_pCurrentGameState )
      {
        int   Delta = ( (short)HIWORD( wParam ) );

        UINT  Ticks = 0;

        SystemParametersInfo( 104, 0, &Ticks, 0 );

        while ( Delta >= 120 )
        {
          for ( UINT i = 0; i < Ticks; ++i )
          {
            m_pCurrentGameState->OnMouseWheel( 1 );
          }
          Delta -= 120;
        }
        while ( Delta <= -120 )
        {
          for ( UINT i = 0; i < Ticks; ++i )
          {
            m_pCurrentGameState->OnMouseWheel( -1 );
          }
          Delta += 120;
        }
      }
      break;
    case WM_DESTROY:
      PostQuitMessage( 0 );
      return TRUE;
  }
  return DefWindowProc( hWnd, uMsg, wParam, lParam );

}



void LDApp::RenderFrame()
{

  m_pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE );
  m_pd3dDevice->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL );
  m_pd3dDevice->SetRenderState( D3DRS_ALPHAREF, 8 );

  m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE );
  m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
  m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );

  m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE );
  m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
  m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );

  m_pd3dDevice->SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
  m_pd3dDevice->SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
  m_pd3dDevice->SetTextureStageState( 0, D3DTSS_MIPFILTER, D3DTEXF_LINEAR );

  //m_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xff757E85, 1.0f, 0 );
  m_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xff757E85, 1.0f, 0 );

  if ( m_pCurrentGameState )
  {
    m_pCurrentGameState->Display( m_pd3dDevice );
  }

  m_pd3dDevice->SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_POINT );
  m_pd3dDevice->SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_POINT );
  m_pd3dDevice->SetTextureStageState( 0, D3DTSS_MIPFILTER, D3DTEXF_POINT );

}



void LDApp::Print( int iX, int iY, const char* szText, DWORD dwColor )
{

  if ( m_pFont == NULL )
  {
    return;
  }

  if ( SUCCEEDED( m_pFont->Begin() ) )
  {
    RECT    rcText;

    SetRect( &rcText, iX, iY, 640, 480 );
    m_pFont->DrawTextA( szText, (int)strlen( szText ), &rcText, 0, dwColor );
    m_pFont->End();
  }

}



void LDApp::PrintNice( int iX, int iY, const char* szText, DWORD dwColor )
{

  while ( *szText )
  {
    tMapLetters::iterator   itLetter( m_mapLetters.find( *szText ) );
    if ( itLetter != m_mapLetters.end() )
    {
      tTextureSection& tsLetter = itLetter->second;

      RenderTextureSection2d( iX, iY, tsLetter, dwColor );

      iX += tsLetter.m_iWidth;
    }

    ++szText;
  }

}



void LDApp::PrintNiceCentered( int iX, int iY, const char* szText, DWORD dwColor, bool bWavy )
{

  if ( bWavy )
  {
    PrintNiceCenteredWavy( iX, iY, szText, dwColor );
    return;
  }

  int   iLength = 0;

  const char*   szDummy = szText;

  while ( *szDummy )
  {
    tMapLetters::iterator   itLetter( m_mapLetters.find( *szDummy ) );
    if ( itLetter != m_mapLetters.end() )
    {
      tTextureSection& tsLetter = itLetter->second;

      iLength += tsLetter.m_iWidth;
    }

    ++szDummy;
  }

  // center about the x value
  iX -= iLength / 2;
  while ( *szText )
  {
    tMapLetters::iterator   itLetter( m_mapLetters.find( *szText ) );
    if ( itLetter != m_mapLetters.end() )
    {
      tTextureSection& tsLetter = itLetter->second;

      RenderTextureSection2d( iX, iY, tsLetter, dwColor );

      iX += tsLetter.m_iWidth;
    }

    ++szText;
  }

}



void LDApp::PrintNiceRightAligned( int iX, int iY, const char* szText, DWORD dwColor )
{

  int   iLength = 0;

  const char*   szDummy = szText;

  while ( *szDummy )
  {
    tMapLetters::iterator   itLetter( m_mapLetters.find( *szDummy ) );
    if ( itLetter != m_mapLetters.end() )
    {
      tTextureSection& tsLetter = itLetter->second;

      iLength += tsLetter.m_iWidth;
    }

    ++szDummy;
  }

  // center about the x value
  iX -= iLength;
  while ( *szText )
  {
    tMapLetters::iterator   itLetter( m_mapLetters.find( *szText ) );
    if ( itLetter != m_mapLetters.end() )
    {
      tTextureSection& tsLetter = itLetter->second;

      RenderTextureSection2d( iX, iY, tsLetter, dwColor );

      iX += tsLetter.m_iWidth;
    }

    ++szText;
  }

}



void LDApp::PrintNiceCenteredWavy( int iX, int iY, const char* szText, DWORD dwColor )
{

  int   iLength = 0;

  const char*   szDummy = szText;

  while ( *szDummy )
  {
    tMapLetters::iterator   itLetter( m_mapLetters.find( *szDummy ) );
    if ( itLetter != m_mapLetters.end() )
    {
      tTextureSection& tsLetter = itLetter->second;

      iLength += tsLetter.m_iWidth;
    }

    ++szDummy;
  }

  // center about the x value
  iX -= iLength / 2;

  int   iLetter = 0;

  while ( *szText )
  {
    tMapLetters::iterator   itLetter( m_mapLetters.find( *szText ) );
    if ( itLetter != m_mapLetters.end() )
    {
      tTextureSection& tsLetter = itLetter->second;

      // make waves, different side every odd letter
      RenderTextureSection2d( iX, iY + ( 1 - ( iLetter % 2 ) * 2 ) * (int)( 4.0f * cosf( m_fWavyTextTimeCount * 3.1415926f / 180.0f ) ), tsLetter, dwColor );

      iX += tsLetter.m_iWidth;
      ++iLetter;
    }

    ++szText;
  }

}



void LDApp::RenderTextureSection2d( int iX, int iY,
                                          const tTextureSection& TexSection,
                                          DWORD dwColor,
                                          DWORD dwColor2,
                                          DWORD dwColor3,
                                          DWORD dwColor4,
                                          int iWidth, int iHeight,
                                          DWORD dwAlternativeFlags )
{

  m_pd3dDevice->SetTexture( 0, TexSection.m_pTexture );

  if ( iWidth == -1 )
  {
    iWidth  = TexSection.m_iWidth;
  }
  if ( iHeight == -1 )
  {
    iHeight = TexSection.m_iHeight;
  }

  if ( dwAlternativeFlags == 0 )
  {
    dwAlternativeFlags = TexSection.m_Flags;
  }

  if ( ( ( dwAlternativeFlags & tTextureSection::TSF_ROTATE_270 ) == tTextureSection::TSF_ROTATE_270 )
  ||   ( ( dwAlternativeFlags & tTextureSection::TSF_ROTATE_270 ) == tTextureSection::TSF_ROTATE_90 ) )
  {
    std::swap( iWidth, iHeight );
  }

  if ( dwAlternativeFlags & tTextureSection::TSF_HCENTER )
  {
    iX -= iWidth / 2;
  }
  if ( dwAlternativeFlags & tTextureSection::TSF_VCENTER )
  {
    iY -= iHeight / 2;
  }
  if ( dwAlternativeFlags & tTextureSection::TSF_ALIGN_BOTTOM )
  {
    iY -= iHeight - 1;
  }
  if ( dwAlternativeFlags & tTextureSection::TSF_ALIGN_RIGHT )
  {
    iX -= iWidth - 1;
  }

  float   fTU[2][2] = { { TexSection.m_fTU[0], TexSection.m_fTU[0] }, { TexSection.m_fTU[1], TexSection.m_fTU[1] } },
          fTV[2][2] = { { TexSection.m_fTV[0], TexSection.m_fTV[1] }, { TexSection.m_fTV[0], TexSection.m_fTV[1] } };


  if ( dwAlternativeFlags & tTextureSection::TSF_H_MIRROR )
  {
    std::swap( fTU[0][0], fTU[1][0] );
    std::swap( fTU[0][1], fTU[1][1] );
  }
  if ( dwAlternativeFlags & tTextureSection::TSF_V_MIRROR )
  {
    std::swap( fTV[0][0], fTV[0][1] );
    std::swap( fTV[1][0], fTV[1][1] );
  }

  if ( ( dwAlternativeFlags & tTextureSection::TSF_ROTATE_270 ) == tTextureSection::TSF_ROTATE_270 )
  {
    float   fTempTU = fTU[0][0];
    float   fTempTV = fTV[0][0];

    fTU[0][0] = fTU[0][1];
    fTV[0][0] = fTV[0][1];
    fTU[0][1] = fTU[1][1];
    fTV[0][1] = fTV[1][1];
    fTU[1][1] = fTU[1][0];
    fTV[1][1] = fTV[1][0];
    fTU[1][0] = fTempTU;
    fTV[1][0] = fTempTV;
  }
  else if ( dwAlternativeFlags & tTextureSection::TSF_ROTATE_90 )
  {
    float   fTempTU = fTU[0][0];
    float   fTempTV = fTV[0][0];

    fTU[0][0] = fTU[1][0];
    fTV[0][0] = fTV[1][0];
    fTU[1][0] = fTU[1][1];
    fTV[1][0] = fTV[1][1];
    fTU[1][1] = fTU[0][1];
    fTV[1][1] = fTV[0][1];
    fTU[0][1] = fTempTU;
    fTV[0][1] = fTempTV;
  }
  else if ( dwAlternativeFlags & tTextureSection::TSF_ROTATE_180 )
  {
    std::swap( fTU[0][0], fTU[1][1] );
    std::swap( fTV[0][0], fTV[1][1] );
    std::swap( fTU[1][0], fTU[0][1] );
    std::swap( fTV[1][0], fTV[0][1] );
  }

  RenderQuad2d( iX, iY,
              iWidth, iHeight,
              fTU[0][0], fTV[0][0],
              fTU[1][0], fTV[1][0],
              fTU[0][1], fTV[0][1],
              fTU[1][1], fTV[1][1],
              dwColor,
              dwColor2, dwColor3, dwColor4 );

}



void LDApp::RenderTextureSectionAsTile2d( int iX, int iY,
                                          const tTextureSection& TexSection,
                                          DWORD dwColor )
{

  RenderTextureSection2d( iX, iY, TexSection, dwColor, dwColor, dwColor, dwColor, 32, 32 );

}



void LDApp::RenderTextureSectionRotated2d( int iX, int iY,
                                                 const tTextureSection& TexSection,
                                                 float fAngle,
                                                 DWORD dwColor,
                                                 DWORD dwColor2,
                                                 DWORD dwColor3,
                                                 DWORD dwColor4,
                                                 int iWidth, int iHeight,
                                                 DWORD dwAlternativeFlags )
{

  m_pd3dDevice->SetTexture( 0, TexSection.m_pTexture );

  if ( iWidth == -1 )
  {
    iWidth  = TexSection.m_iWidth;
  }
  if ( iHeight == -1 )
  {
    iHeight = TexSection.m_iHeight;
  }

  if ( dwAlternativeFlags == 0 )
  {
    dwAlternativeFlags = TexSection.m_Flags;
  }

  if ( ( ( dwAlternativeFlags & tTextureSection::TSF_ROTATE_270 ) == tTextureSection::TSF_ROTATE_270 )
  ||   ( ( dwAlternativeFlags & tTextureSection::TSF_ROTATE_270 ) == tTextureSection::TSF_ROTATE_90 ) )
  {
    std::swap( iWidth, iHeight );
  }

  if ( dwAlternativeFlags & tTextureSection::TSF_HCENTER )
  {
    iX -= iWidth / 2;
  }
  if ( dwAlternativeFlags & tTextureSection::TSF_VCENTER )
  {
    iY -= iHeight / 2;
  }
  if ( dwAlternativeFlags & tTextureSection::TSF_ALIGN_BOTTOM )
  {
    iY -= iHeight - 1;
  }
  if ( dwAlternativeFlags & tTextureSection::TSF_ALIGN_RIGHT )
  {
    iX -= iWidth - 1;
  }

  float   fTU[2][2] = { { TexSection.m_fTU[0], TexSection.m_fTU[0] }, { TexSection.m_fTU[1], TexSection.m_fTU[1] } },
          fTV[2][2] = { { TexSection.m_fTV[0], TexSection.m_fTV[1] }, { TexSection.m_fTV[0], TexSection.m_fTV[1] } };


  if ( dwAlternativeFlags & tTextureSection::TSF_H_MIRROR )
  {
    std::swap( fTU[0][0], fTU[1][0] );
    std::swap( fTU[0][1], fTU[1][1] );
  }
  if ( dwAlternativeFlags & tTextureSection::TSF_V_MIRROR )
  {
    std::swap( fTV[0][0], fTV[0][1] );
    std::swap( fTV[1][0], fTV[1][1] );
  }

  if ( ( dwAlternativeFlags & tTextureSection::TSF_ROTATE_270 ) == tTextureSection::TSF_ROTATE_270 )
  {
    float   fTempTU = fTU[0][0];
    float   fTempTV = fTV[0][0];

    fTU[0][0] = fTU[0][1];
    fTV[0][0] = fTV[0][1];
    fTU[0][1] = fTU[1][1];
    fTV[0][1] = fTV[1][1];
    fTU[1][1] = fTU[1][0];
    fTV[1][1] = fTV[1][0];
    fTU[1][0] = fTempTU;
    fTV[1][0] = fTempTV;
  }
  else if ( dwAlternativeFlags & tTextureSection::TSF_ROTATE_90 )
  {
    float   fTempTU = fTU[0][0];
    float   fTempTV = fTV[0][0];

    fTU[0][0] = fTU[1][0];
    fTV[0][0] = fTV[1][0];
    fTU[1][0] = fTU[1][1];
    fTV[1][0] = fTV[1][1];
    fTU[1][1] = fTU[0][1];
    fTV[1][1] = fTV[0][1];
    fTU[0][1] = fTempTU;
    fTV[0][1] = fTempTV;
  }
  else if ( dwAlternativeFlags & tTextureSection::TSF_ROTATE_180 )
  {
    std::swap( fTU[0][0], fTU[1][1] );
    std::swap( fTV[0][0], fTV[1][1] );
    std::swap( fTU[1][0], fTU[0][1] );
    std::swap( fTV[1][0], fTV[0][1] );
  }

  RenderQuadRotated2d( iX, iY,
                     iWidth, iHeight,
                     fTU[0][0], fTV[0][0],
                     fTU[1][0], fTV[1][0],
                     fTU[0][1], fTV[0][1],
                     fTU[1][1], fTV[1][1],
                     fAngle,
                     dwColor,
                     dwColor2, dwColor3, dwColor4 );

}



void LDApp::RenderQuad( const D3DXVECTOR3& V1,
                          const D3DXVECTOR3& V2,
                          const D3DXVECTOR3& V3,
                          const D3DXVECTOR3& V4,
                          float fTU1, float fTV1,
                          float fTU2, float fTV2,
                          float fTU3, float fTV3,
                          float fTU4, float fTV4,
                          DWORD dwColor,
                          DWORD dwColor2,
                          DWORD dwColor3,
                          DWORD dwColor4 )
{

  if ( ( dwColor2 == dwColor3 )
  &&   ( dwColor3 == dwColor4 )
  &&   ( dwColor4 == 0 ) )
  {
    dwColor2 = dwColor3 = dwColor4 = dwColor;
  }

  struct CUSTOMVERTEX
  {
    D3DXVECTOR3     position; // The position
    D3DXVECTOR3     normal; // The position
    D3DCOLOR        color;    // The color
    float           fTU,
                    fTV;
  };

  CUSTOMVERTEX          vertData[4];


  D3DXVECTOR3       normal;

  D3DXVec3Cross( &normal, &( V2 - V1 ), &( V3 - V1 ) );
  D3DXVec3Normalize( &normal, &normal );

  m_pd3dDevice->SetVertexShader( D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1 | D3DFVF_NORMAL );

  vertData[0].position    = V1;
  vertData[0].color       = dwColor;
  vertData[0].normal      = normal;
  vertData[0].fTU         = fTU1;
  vertData[0].fTV         = fTV1;

  vertData[1].position    = V2;
  vertData[1].color       = dwColor2;
  vertData[1].normal      = normal;
  vertData[1].fTU         = fTU2;
  vertData[1].fTV         = fTV2;

  vertData[2].position    = V3;
  vertData[2].color       = dwColor3;
  vertData[2].normal      = normal;
  vertData[2].fTU         = fTU3;
  vertData[2].fTV         = fTV3;

  vertData[3].position    = V4;
  vertData[3].color       = dwColor4;
  vertData[3].normal      = normal;
  vertData[3].fTU         = fTU4;
  vertData[3].fTV         = fTV4;

  m_pd3dDevice->DrawPrimitiveUP(
                  D3DPT_TRIANGLESTRIP,
                  2,
                  &vertData,
                  sizeof( vertData[0] ) );

}



void LDApp::RenderTextureSection( const D3DXVECTOR3& V1,
                                                const D3DXVECTOR3& V2,
                                                const D3DXVECTOR3& V3,
                                                const D3DXVECTOR3& V4,
                                                const tTextureSection& TexSection,
                                                DWORD dwColor,
                                                DWORD dwColor2,
                                                DWORD dwColor3,
                                                DWORD dwColor4 )
{

  m_pd3dDevice->SetTexture( 0, TexSection.m_pTexture );

  float   fTU[2][2] = { { TexSection.m_fTU[0], TexSection.m_fTU[0] }, { TexSection.m_fTU[1], TexSection.m_fTU[1] } },
          fTV[2][2] = { { TexSection.m_fTV[0], TexSection.m_fTV[1] }, { TexSection.m_fTV[0], TexSection.m_fTV[1] } };


  if ( TexSection.m_Flags & tTextureSection::TSF_H_MIRROR )
  {
    std::swap( fTU[0][0], fTU[1][0] );
    std::swap( fTU[0][1], fTU[1][1] );
  }
  if ( TexSection.m_Flags & tTextureSection::TSF_V_MIRROR )
  {
    std::swap( fTV[0][0], fTV[0][1] );
    std::swap( fTV[1][0], fTV[1][1] );
  }

  if ( ( TexSection.m_Flags & tTextureSection::TSF_ROTATE_270 ) == tTextureSection::TSF_ROTATE_270 )
  {
    float   fTempTU = fTU[0][0];
    float   fTempTV = fTV[0][0];

    fTU[0][0] = fTU[0][1];
    fTV[0][0] = fTV[0][1];
    fTU[0][1] = fTU[1][1];
    fTV[0][1] = fTV[1][1];
    fTU[1][1] = fTU[1][0];
    fTV[1][1] = fTV[1][0];
    fTU[1][0] = fTempTU;
    fTV[1][0] = fTempTV;
  }
  else if ( TexSection.m_Flags & tTextureSection::TSF_ROTATE_90 )
  {
    float   fTempTU = fTU[0][0];
    float   fTempTV = fTV[0][0];

    fTU[0][0] = fTU[1][0];
    fTV[0][0] = fTV[1][0];
    fTU[1][0] = fTU[1][1];
    fTV[1][0] = fTV[1][1];
    fTU[1][1] = fTU[0][1];
    fTV[1][1] = fTV[0][1];
    fTU[0][1] = fTempTU;
    fTV[0][1] = fTempTV;
  }
  else if ( TexSection.m_Flags & tTextureSection::TSF_ROTATE_180 )
  {
    std::swap( fTU[0][0], fTU[1][1] );
    std::swap( fTV[0][0], fTV[1][1] );
    std::swap( fTU[1][0], fTU[0][1] );
    std::swap( fTV[1][0], fTV[0][1] );
  }

  RenderQuad( V1, V2, V3, V4,
              fTU[0][0], fTV[0][0],
              fTU[1][0], fTV[1][0],
              fTU[0][1], fTV[0][1],
              fTU[1][1], fTV[1][1],
              dwColor, dwColor2, dwColor3, dwColor4 );


}



void LDApp::RenderQuad2d( int iX, int iY, int iWidth, int iHeight,
                                float fTU1, float fTV1,
                                float fTU2, float fTV2,
                                float fTU3, float fTV3,
                                float fTU4, float fTV4,
                                DWORD dwColor,
                                DWORD dwColor2,
                                DWORD dwColor3,
                                DWORD dwColor4 )
{

  struct CUSTOMVERTEX
  {
      D3DXVECTOR3   position; // The position
      float         fRHW;
      D3DCOLOR      color;    // The color
      float         fTU,
                    fTV;
  };

  if ( dwColor2 == 0 )
  {
    dwColor4 = dwColor3 = dwColor2 = dwColor;
  }

  CUSTOMVERTEX          vertData[4];

  float   fDelta = -0.5f;

  float   fRHW = 1.0f;

  D3DXVECTOR3   ptPos( (float)iX, (float)iY, 0.0f );
  D3DXVECTOR3   ptSize( (float)iWidth, (float)iHeight, 0.0f );

  m_pd3dDevice->SetVertexShader( D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1 );

  vertData[0].position.x  = ptPos.x + fDelta;
  vertData[0].position.y  = ptPos.y + fDelta;
  vertData[0].position.z  = (float)ptPos.z;
  vertData[0].fRHW        = fRHW;
  vertData[0].color       = dwColor;
  vertData[0].fTU         = fTU1;
  vertData[0].fTV         = fTV1;

  vertData[1].position.x  = ptPos.x + ptSize.x + fDelta;
  vertData[1].position.y  = ptPos.y + fDelta;
  vertData[1].position.z  = (float)ptPos.z;
  vertData[1].fRHW        = fRHW;
  vertData[1].color       = dwColor2;
  vertData[1].fTU         = fTU2;
  vertData[1].fTV         = fTV2;

  vertData[2].position.x  = ptPos.x + fDelta;
  vertData[2].position.y  = ptPos.y + ptSize.y + fDelta;
  vertData[2].position.z  = (float)ptPos.z;
  vertData[2].fRHW        = fRHW;
  vertData[2].color       = dwColor3;
  vertData[2].fTU         = fTU3;
  vertData[2].fTV         = fTV3;

  vertData[3].position.x  = ptPos.x + ptSize.x + fDelta;
  vertData[3].position.y  = ptPos.y + ptSize.y + fDelta;
  vertData[3].position.z  = (float)ptPos.z;
  vertData[3].fRHW        = fRHW;
  vertData[3].color       = dwColor4;
  vertData[3].fTU         = fTU4;
  vertData[3].fTV         = fTV4;

  m_pd3dDevice->DrawPrimitiveUP(
                  D3DPT_TRIANGLESTRIP,
                  2,
                  &vertData,
                  sizeof( vertData[0] ) );

}




void LDApp::RenderQuad2d( int iX, int iY, int iWidth, int iHeight,
                                DWORD dwColor,
                                DWORD dwColor2,
                                DWORD dwColor3,
                                DWORD dwColor4 )
{

  struct CUSTOMVERTEX
  {
      D3DXVECTOR3   position; // The position
      float         fRHW;
      D3DCOLOR      color;    // The color
  };

  if ( dwColor2 == 0 )
  {
    dwColor4 = dwColor3 = dwColor2 = dwColor;
  }

  CUSTOMVERTEX          vertData[4];

  float   fDelta = -0.5f;

  float   fRHW = 1.0f;

  D3DXVECTOR3   ptPos( (float)iX, (float)iY, 0.0f );
  D3DXVECTOR3   ptSize( (float)iWidth, (float)iHeight, 0.0f );

  m_pd3dDevice->SetVertexShader( D3DFVF_XYZRHW | D3DFVF_DIFFUSE );
  m_pd3dDevice->SetTexture( 0, NULL );

  vertData[0].position.x  = ptPos.x + fDelta;
  vertData[0].position.y  = ptPos.y + fDelta;
  vertData[0].position.z  = (float)ptPos.z;
  vertData[0].fRHW        = fRHW;
  vertData[0].color       = dwColor;

  vertData[1].position.x  = ptPos.x + ptSize.x + fDelta;
  vertData[1].position.y  = ptPos.y + fDelta;
  vertData[1].position.z  = (float)ptPos.z;
  vertData[1].fRHW        = fRHW;
  vertData[1].color       = dwColor2;

  vertData[2].position.x  = ptPos.x + fDelta;
  vertData[2].position.y  = ptPos.y + ptSize.y + fDelta;
  vertData[2].position.z  = (float)ptPos.z;
  vertData[2].fRHW        = fRHW;
  vertData[2].color       = dwColor3;

  vertData[3].position.x  = ptPos.x + ptSize.x + fDelta;
  vertData[3].position.y  = ptPos.y + ptSize.y + fDelta;
  vertData[3].position.z  = (float)ptPos.z;
  vertData[3].fRHW        = fRHW;
  vertData[3].color       = dwColor4;

  m_pd3dDevice->DrawPrimitiveUP(
                  D3DPT_TRIANGLESTRIP,
                  2,
                  &vertData,
                  sizeof( vertData[0] ) );

}




void LDApp::RenderQuadRotated2d( int iX, int iY, int iWidth, int iHeight,
                                       float fTU1, float fTV1,
                                       float fTU2, float fTV2,
                                       float fTU3, float fTV3,
                                       float fTU4, float fTV4,
                                       float fAngle,
                                       DWORD dwColor,
                                       DWORD dwColor2,
                                       DWORD dwColor3,
                                       DWORD dwColor4 )
{

  if ( dwColor2 == 0 )
  {
    dwColor4 = dwColor3 = dwColor2 = dwColor;
  }

  struct CUSTOMVERTEX
  {
    D3DXVECTOR3   vectPos;
    float         fRHW;
    DWORD         color;    // The color
    float         fTU,fTV;
  };

  CUSTOMVERTEX          vertData[4];


  m_pd3dDevice->SetVertexShader( D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1 );

  vertData[0].fRHW        = 1.0f;
  vertData[0].color       = dwColor;
  vertData[0].fTU         = fTU1;
  vertData[0].fTV         = fTV1;

  vertData[1].fRHW        = 1.0f;
  vertData[1].color       = dwColor2;
  vertData[1].fTU         = fTU2;
  vertData[1].fTV         = fTV2;

  vertData[2].fRHW        = 1.0f;
  vertData[2].color       = dwColor3;
  vertData[2].fTU         = fTU3;
  vertData[2].fTV         = fTV3;

  vertData[3].fRHW        = 1.0f;
  vertData[3].color       = dwColor4;
  vertData[3].fTU         = fTU4;
  vertData[3].fTV         = fTV4;

  // this is wrong in so many ways...
  D3DXVECTOR3   vQuarter1( -iWidth * 0.5f, -iHeight * 0.5f, 0.0f );
  D3DXVECTOR3   vQuarter2( iWidth * 0.5f, -iHeight * 0.5f, 0.0f );
  D3DXVECTOR3   vQuarter3( -iWidth * 0.5f, iHeight * 0.5f, 0.0f );
  D3DXVECTOR3   vQuarter4( iWidth * 0.5f, iHeight * 0.5f, 0.0f );

  D3DXVECTOR3   vQuarterRot1;
  D3DXVECTOR3   vQuarterRot2;
  D3DXVECTOR3   vQuarterRot3;
  D3DXVECTOR3   vQuarterRot4;

  float     fRad = -3.1415926f * fAngle / 180.0f;
  vQuarterRot1.x = vQuarter1.x * cos( fRad ) - vQuarter1.y * sin( fRad );
  vQuarterRot1.y = vQuarter1.x * sin( fRad ) + vQuarter1.y * cos( fRad );
  vQuarterRot1.z = 0.0f;
  vQuarterRot2.x = vQuarter2.x * cos( fRad ) - vQuarter2.y * sin( fRad );
  vQuarterRot2.y = vQuarter2.x * sin( fRad ) + vQuarter2.y * cos( fRad );
  vQuarterRot2.z = 0.0f;
  vQuarterRot3.x = vQuarter3.x * cos( fRad ) - vQuarter3.y * sin( fRad );
  vQuarterRot3.y = vQuarter3.x * sin( fRad ) + vQuarter3.y * cos( fRad );
  vQuarterRot3.z = 0.0f;
  vQuarterRot4.x = vQuarter4.x * cos( fRad ) - vQuarter4.y * sin( fRad );
  vQuarterRot4.y = vQuarter4.x * sin( fRad ) + vQuarter4.y * cos( fRad );
  vQuarterRot4.z = 0.0f;

  vertData[0].vectPos = D3DXVECTOR3( (float)iX, (float)iY, 0 ) + vQuarterRot1;
  vertData[1].vectPos = D3DXVECTOR3( (float)iX, (float)iY, 0 ) + vQuarterRot2;
  vertData[2].vectPos = D3DXVECTOR3( (float)iX, (float)iY, 0 ) + vQuarterRot3;
  vertData[3].vectPos = D3DXVECTOR3( (float)iX, (float)iY, 0 ) + vQuarterRot4;

  m_pd3dDevice->DrawPrimitiveUP(
                  D3DPT_TRIANGLESTRIP,
                  2,
                  &vertData,
                  sizeof( vertData[0] ) );

}



IDirectSoundBuffer* LDApp::LoadWave( const char* szFileName )
{

  WAVEFORMATEX        wfDummy;

  DSBUFFERDESC1       dsBufferDesc;

  HRESULT             hResult;

  char                *pPointer1,
                      *pPointer2;

  unsigned char       ucBuffer[16];

  DWORD               dwChunkSize,
                      dwSize1,
                      dwSize2;

  if ( m_pDirectSound == NULL )
  {
    return NULL;
  }

  // Wave einlesen
  FILE*   fileSound = fopen( szFileName, "rb" );
  if ( fileSound == NULL )
  {
    return NULL;
  }

  fread( ucBuffer, 8, 1, fileSound );
  if ( ( ucBuffer[0] != 82 )
  &&   ( ucBuffer[1] != 73 )
  &&   ( ucBuffer[2] != 70 )
  &&   ( ucBuffer[3] != 70 ) )
  {
    // kein RIFF
    fclose( fileSound );
    return NULL;
  }
  fread( ucBuffer, 8, 1, fileSound );
  if ( ( ucBuffer[0] != 87 )
  &&   ( ucBuffer[1] != 65 )
  &&   ( ucBuffer[2] != 86 )
  &&   ( ucBuffer[3] != 69 )
  &&   ( ucBuffer[4] != 102 )
  &&   ( ucBuffer[5] != 109 )
  &&   ( ucBuffer[6] != 116 )
  &&   ( ucBuffer[7] != 32 ) )
  {
    // kein WAVEfmt_
    fclose( fileSound );
    return NULL;
  }
  fread( &dwChunkSize, 4, 1, fileSound );
  if ( dwChunkSize != 16 )
  {
    // Nicht Standardgröße, ich bin verwörrt
    fclose( fileSound );
    return NULL;
  }

  // Da ist jetzt das wichtige Zeugs drin
  fread( ucBuffer, 16, 1, fileSound );

  fseek( fileSound, dwChunkSize - 16, SEEK_CUR );

  // Set up wave format structure.
  memset( &wfDummy, 0, sizeof( WAVEFORMATEX ) );
  wfDummy.wFormatTag = WAVE_FORMAT_PCM;    // Immer
  wfDummy.nChannels = ucBuffer[2] + 256 * ucBuffer[3];
  wfDummy.nSamplesPerSec = ucBuffer[4] + 256 * ucBuffer[5] + 65536 * ucBuffer[6] + 16777217 * ucBuffer[7];
  wfDummy.nBlockAlign = ucBuffer[12] + 256 * ucBuffer[13];
  wfDummy.nAvgBytesPerSec = wfDummy.nSamplesPerSec * wfDummy.nBlockAlign;
  wfDummy.wBitsPerSample = ucBuffer[14] + 256 * ucBuffer[15];

  fread( ucBuffer, 4, 1, fileSound );
  if ( ( ucBuffer[0] != 100 )
  &&   ( ucBuffer[1] != 97 )
  &&   ( ucBuffer[2] != 116 )
  &&   ( ucBuffer[3] != 97 ) )
  {
    // kein data
    fclose( fileSound );
    return NULL;
  }
  fread( &dwChunkSize, 4, 1, fileSound );

  // DSBUFFERDESC1 füllen
  memset( &dsBufferDesc, 0, sizeof( DSBUFFERDESC1 ) );            // Zero it out.
  dsBufferDesc.dwSize = sizeof( DSBUFFERDESC1 );                  // Immer
  dsBufferDesc.dwFlags = DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY;
    //DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY | DSBCAPS_STATIC | DSBCAPS_GETCURRENTPOSITION2;// | DSBCAPS_STICKYFOCUS;
  //  DSBCAPS_CTRLDEFAULT | DSBCAPS_STATIC | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_STICKYFOCUS;
  dsBufferDesc.dwBufferBytes = dwChunkSize;
  dsBufferDesc.lpwfxFormat = (LPWAVEFORMATEX)&wfDummy;

  // DirectSoundBuffer anlegen
  IDirectSoundBuffer*   pSoundBuffer = NULL;

  hResult = m_pDirectSound->CreateSoundBuffer( (LPDSBUFFERDESC)&dsBufferDesc,
                                               &pSoundBuffer,
                                               NULL );
  if ( hResult == DS_OK )
  {
    hResult = pSoundBuffer->Lock( 0, dwChunkSize, (void **)&pPointer1, (LPDWORD)&dwSize1, (void **)&pPointer2, (LPDWORD)&dwSize2, 0 );
    if ( hResult != DS_OK )
    {
      // Konnte Buffer nicht "locken"
      fclose( fileSound );
      pSoundBuffer->Release();
      return NULL;
    }
    fread( pPointer1, dwSize1, 1, fileSound );
    if ( ( dwSize2 != 0 )
    &&   ( pPointer2 != NULL ) )
    {
      // Zweiten Teil nur bei Wrap einladen
      fread( pPointer2, dwSize2, 1, fileSound );
    }
    else
    {
      dwSize2 = 0;
    }
    fclose( fileSound );
    hResult = pSoundBuffer->Unlock( pPointer1, dwSize1, pPointer2, dwSize2 );
    if ( hResult != DS_OK )
    {
      // Konnte Buffer nicht "unlocken"
      fclose( fileSound );
      pSoundBuffer->Release();
      return NULL;
    }
    fclose( fileSound );

    return pSoundBuffer;
  }
  // Failed
  fclose( fileSound );

  return NULL;

}



IDirect3DTexture8* LDApp::LoadTexture( const char* szFileName, DWORD dwColorKey )
{
  IDirect3DTexture8*    pNewTexture = NULL;

  if ( dwColorKey )
  {
    if ( FAILED( D3DXCreateTextureFromFileEx( m_pd3dDevice, szFileName,
                                              D3DX_DEFAULT, D3DX_DEFAULT,
                                              1,
                                              0,
                                              D3DFMT_UNKNOWN,
                                              D3DPOOL_MANAGED,
                                              D3DX_FILTER_NONE,
                                              D3DX_FILTER_NONE,
                                              dwColorKey,
                                              NULL,
                                              NULL,
                                              &pNewTexture ) ) )
    {
      return NULL;
    }
    m_LoadedTextures.insert( pNewTexture );
    return pNewTexture;
  }
  if ( FAILED( D3DXCreateTextureFromFile( m_pd3dDevice, szFileName, &pNewTexture ) ) )
  {
    return NULL;
  }
  m_LoadedTextures.insert( pNewTexture );

  return pNewTexture;
}




void LDApp::UpdateFrame( const float fElapsedTime )
{
  m_fWavyTextTimeCount += 250.0f * fElapsedTime;
  if ( m_pCurrentGameState )
  {
    m_pCurrentGameState->UpdateFrame( fElapsedTime );
  }
}



void LDApp::NextState( CGameState* pNewState )
{

  if ( pNewState )
  {
    m_pNextState = pNewState;
  }

}



void LDApp::Run()
{

  if ( FAILED( DirectSoundCreate( NULL, &m_pDirectSound, NULL ) ) )
  {
    MessageBox( NULL, "Couldn't create DirectSound object", "Error", 0 );
    return;
  }
  m_pd3d = Direct3DCreate8( D3D_SDK_VERSION );
  if ( m_pd3d == NULL )
  {
    MessageBox( NULL, "Couldn't create Direct3D8", "Error", 0 );
    return;
  }

  //m_FullScreen = ( MessageBox( NULL, "Do you want to run in fullscreen mode?", "LD#15: Caverns of Venus", MB_YESNO ) == IDYES );
  m_FullScreen = false;

  DWORD   dwStyles = WS_VISIBLE | WS_POPUP;
  RECT    rcWindow;

  SetRect( &rcWindow, 0, 0, 640, 480 );
  if ( !m_FullScreen )
  {
    dwStyles |= WS_CAPTION | WS_SYSMENU | WS_SIZEBOX | WS_BORDER | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_MAXIMIZE;
    AdjustWindowRect( &rcWindow, dwStyles, FALSE );

    OffsetRect( &rcWindow, rcWindow.left, rcWindow.top );
    OffsetRect( &rcWindow,
                ( GetSystemMetrics( SM_CXSCREEN ) - ( rcWindow.right - rcWindow.left ) ) / 2,
                ( GetSystemMetrics( SM_CYSCREEN ) - ( rcWindow.bottom - rcWindow.top ) ) / 2 );
  }

  m_hWnd = CreateWindow( "STATIC", "LD#38 - Advanced Mitosis", dwStyles,
                         rcWindow.left, rcWindow.top,
                         rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top,
                         NULL, 0, GetModuleHandle( NULL ), 0 );

  SetWindowLongPtr( m_hWnd, GWL_WNDPROC, (LONG)(LONG_PTR)StaticWindowProc );

  SendMessage( m_hWnd, WM_SETICON, ICON_BIG, (LPARAM)m_hIcon );
  SendMessage( m_hWnd, WM_SETICON, ICON_SMALL, (LPARAM)m_hIcon );

  m_pDirectSound->SetCooperativeLevel( m_hWnd, DSSCL_EXCLUSIVE );

  m_d3dPP.BackBufferWidth   = 640;
  m_d3dPP.BackBufferHeight  = 480;
  m_d3dPP.BackBufferFormat  = D3DFMT_R5G6B5;
  m_d3dPP.EnableAutoDepthStencil  = TRUE;
  // TODO - try different formats before failing!
  m_d3dPP.AutoDepthStencilFormat  = D3DFMT_D16;
  if ( !m_FullScreen )
  {
    m_d3dPP.Windowed          = TRUE;
    m_d3dPP.BackBufferCount   = 1;
    m_d3dPP.SwapEffect        = D3DSWAPEFFECT_FLIP;
    m_d3dPP.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
  }
  else
  {
    m_d3dPP.Windowed          = FALSE;
    m_d3dPP.BackBufferCount   = 0;
    m_d3dPP.SwapEffect        = D3DSWAPEFFECT_DISCARD;
    m_d3dPP.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
  }

  m_d3dPP.hDeviceWindow     = m_hWnd;

  if ( FAILED( m_pd3d->CreateDevice( D3DADAPTER_DEFAULT,
                                        D3DDEVTYPE_HAL,
                                        m_hWnd,
                                        D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                        &m_d3dPP,
                                        &m_pd3dDevice ) ) )
  {
    // fall"back" to 32 bit if 16 bit won't work
    m_d3dPP.BackBufferFormat  = D3DFMT_X8R8G8B8;
    if ( FAILED( m_pd3d->CreateDevice( D3DADAPTER_DEFAULT,
                                          D3DDEVTYPE_HAL,
                                          m_hWnd,
                                          D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                          &m_d3dPP,
                                          &m_pd3dDevice ) ) )
    {
      // fall"back" to 24 bit if 16 bit won't work (crap card ahoy)
      m_d3dPP.BackBufferFormat  = D3DFMT_R8G8B8;
      if ( FAILED( m_pd3d->CreateDevice( D3DADAPTER_DEFAULT,
                                            D3DDEVTYPE_HAL,
                                            m_hWnd,
                                            D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                            &m_d3dPP,
                                            &m_pd3dDevice ) ) )
      {
        MessageBox( m_hWnd, "Cannot create D3D Device, tried R5G6B5, R8G8B8 and X8R8G8B8.", "Oh the noes!", 0 );
        DestroyWindow( m_hWnd );
        return;
      }
    }
  }

  LoadResources();

  m_Music.Initialize( m_hWnd );
  m_Music.SetVolume( 50 );

  theApp.m_Music.LoadMusic( "sounds/music.ogg" );
  theApp.m_Music.Play();

  m_pd3dDevice->SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
  m_pd3dDevice->SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
  m_pd3dDevice->SetTextureStageState( 0, D3DTSS_MIPFILTER, D3DTEXF_LINEAR );

  CTimer::Time( CTimer::TF_GETELAPSEDTIME );

  m_pCurrentGameState = new CGSMenu();
  //m_pCurrentGameState = new CGSGame();

  ShowWindow( m_hWnd, SW_MAXIMIZE );

  MSG   msg;

  while ( true )
  {
    if ( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )
    {
      if ( !GetMessage( &msg, NULL, 0, 0 ) )
      {
        break;
      }
      TranslateMessage( &msg );
      DispatchMessage( &msg );
    }
    else
    {
      HRESULT   hRes = S_OK;

      if ( FAILED( hRes = m_pd3dDevice->TestCooperativeLevel() ) )
      {
        // If the device was lost, do not render until we get it back
        if ( D3DERR_DEVICELOST != hRes )
        {
          // Check if the device needs to be resized.
          if ( D3DERR_DEVICENOTRESET == hRes )
          {
            Unload3dResources();

            if ( SUCCEEDED( m_pd3dDevice->Reset( &m_d3dPP ) ) )
            {
              Load3dResources();
            }
          }
        }
      }
      else
      {
        // display loop. now WITH BeginScene/EndScene
        HRESULT   hRes = m_pd3dDevice->BeginScene();
        if ( SUCCEEDED( hRes ) )
        {
          RenderFrame();

          m_pd3dDevice->EndScene();

          hRes = m_pd3dDevice->Present( NULL, NULL, NULL, NULL );
        }
      }

      float   fElapsedTime = ( GetTickCount() - m_dwLastTicks ) * 0.001f;

      m_dwLastTicks = GetTickCount();

      fElapsedTime = CTimer::Time( CTimer::TF_GETELAPSEDTIME );

      UpdateFrame( fElapsedTime );

      if ( m_pNextState )
      {
        delete m_pCurrentGameState;
        m_pCurrentGameState = m_pNextState;
        m_pNextState = NULL;
      }
    }
  }

  if ( m_pCurrentGameState )
  {
    delete m_pCurrentGameState;
  }

  UnloadResources();

  if ( m_pDirectSound != NULL )
  {
    m_pDirectSound->Release();
    m_pDirectSound = NULL;
  }

  if ( m_pd3dDevice )
  {
    m_pd3dDevice->Release();
  }
  if ( m_pd3d )
  {
    m_pd3d->Release();
  }

  DestroyWindow( m_hWnd );

}



void LDApp::LoadResources()
{
  m_pSound[LD::SOUND_SLIME]   = LoadWave( "sounds\\slime.wav" );
  m_pSound[LD::SOUND_INJECT]  = LoadWave( "sounds\\inject.wav" );
  m_pSound[LD::SOUND_EAT]     = LoadWave( "sounds\\eat.wav" );
  m_pSound[LD::SOUND_DIE]     = LoadWave( "sounds\\died.wav" );

  Load3dResources();
}



void LDApp::Load3dResources()
{
  m_pTextureFX      = LoadTexture( "images/fx.png", 0xff2CFF07 );
  m_pTextureFont    = LoadTexture( "images/font.bmp", 0xff000000 );
  m_pTextureMetal   = LoadTexture( "images/metal.png" );
  m_pTextureUnits   = LoadTexture( "images/units.png", 0xffff00ff );
  m_pTexturePipette = LoadTexture( "images/pipette.png", 0xff000000 );
  m_pTextureTitle   = LoadTexture( "images/title.png" );

  m_TexSec[LD::TEX_METAL_BACKGROUND] = tTextureSection( m_pTextureMetal, 0, 0, 256, 256 );
  m_TexSec[LD::TEX_PLAYER_1] = tTextureSection( m_pTextureUnits, 1, 1, 30, 30 );
  m_TexSec[LD::TEX_PLAYER_2] = tTextureSection( m_pTextureUnits, 33, 1, 30, 30 );
  m_TexSec[LD::TEX_PLANKTON] = tTextureSection( m_pTextureUnits, 4, 36, 7, 7 );
  m_TexSec[LD::TEX_HOMING_ENEMY_1] = tTextureSection( m_pTextureUnits, 65, 1, 30, 30 );
  m_TexSec[LD::TEX_HOMING_ENEMY_2] = tTextureSection( m_pTextureUnits, 97, 1, 30, 30 );
  m_TexSec[LD::TEX_SITTING_ENEMY_1] = tTextureSection( m_pTextureUnits, 129, 1, 30, 30 );
  m_TexSec[LD::TEX_SITTING_ENEMY_2] = tTextureSection( m_pTextureUnits, 161, 1, 30, 30 );
  m_TexSec[LD::TEX_BORDER_EDGE] = tTextureSection( m_pTextureUnits, 17, 33, 10, 10 );

  m_TexSec[LD::TEX_PIPETTE_1] = tTextureSection( m_pTexturePipette, 1, 0, 46, 256 );
  m_TexSec[LD::TEX_PIPETTE_2] = tTextureSection( m_pTexturePipette, 48 + 1, 0, 46, 256 );
  m_TexSec[LD::TEX_PIPETTE_3] = tTextureSection( m_pTexturePipette, 2 * 48 + 1, 0, 46, 256 );
  m_TexSec[LD::TEX_PIPETTE_4] = tTextureSection( m_pTexturePipette, 3 * 48 + 1, 0, 46, 256 );
  m_TexSec[LD::TEX_PIPETTE_5] = tTextureSection( m_pTexturePipette, 4 * 48 + 1, 0, 46, 256 );

  D3DSURFACE_DESC   ddsc;

  if ( SUCCEEDED( m_pTextureTitle->GetLevelDesc( 0, &ddsc ) ) )
  {
    m_TexSec[LD::TEX_TITLE] = tTextureSection( m_pTextureTitle, 0, 0, 640, 480, 0, ddsc.Width, ddsc.Height );
  }

  // Font info from Generator
  m_mapLetters[32] = tTextureSection( m_pTextureFont, 0, 0, 7, 33 );
  m_mapLetters[33] = tTextureSection( m_pTextureFont, 0, 33, 7, 33 );
  m_mapLetters[38] = tTextureSection( m_pTextureFont, 0, 66, 0, 33 );
  m_mapLetters[39] = tTextureSection( m_pTextureFont, 0, 66, 6, 33 );
  m_mapLetters[44] = tTextureSection( m_pTextureFont, 0, 99, 6, 33 );
  m_mapLetters[46] = tTextureSection( m_pTextureFont, 0, 132, 6, 33 );
  m_mapLetters[58] = tTextureSection( m_pTextureFont, 0, 165, 6, 33 );
  m_mapLetters[59] = tTextureSection( m_pTextureFont, 0, 198, 6, 33 );
  m_mapLetters[34] = tTextureSection( m_pTextureFont, 7, 0, 11, 33 );
  m_mapLetters[36] = tTextureSection( m_pTextureFont, 7, 33, 11, 33 );
  m_mapLetters[40] = tTextureSection( m_pTextureFont, 7, 66, 11, 33 );
  m_mapLetters[41] = tTextureSection( m_pTextureFont, 7, 99, 10, 33 );
  m_mapLetters[43] = tTextureSection( m_pTextureFont, 7, 132, 11, 33 );
  m_mapLetters[45] = tTextureSection( m_pTextureFont, 7, 165, 11, 33 );
  m_mapLetters[48] = tTextureSection( m_pTextureFont, 7, 198, 10, 33 );
  m_mapLetters[35] = tTextureSection( m_pTextureFont, 18, 0, 17, 33 );
  m_mapLetters[37] = tTextureSection( m_pTextureFont, 35, 0, 19, 33 );
  m_mapLetters[42] = tTextureSection( m_pTextureFont, 54, 0, 12, 33 );
  m_mapLetters[47] = tTextureSection( m_pTextureFont, 66, 0, 12, 33 );
  m_mapLetters[49] = tTextureSection( m_pTextureFont, 78, 0, 8, 33 );
  m_mapLetters[50] = tTextureSection( m_pTextureFont, 86, 0, 10, 33 );
  m_mapLetters[51] = tTextureSection( m_pTextureFont, 96, 0, 10, 33 );
  m_mapLetters[52] = tTextureSection( m_pTextureFont, 106, 0, 10, 33 );
  m_mapLetters[53] = tTextureSection( m_pTextureFont, 116, 0, 10, 33 );
  m_mapLetters[54] = tTextureSection( m_pTextureFont, 126, 0, 10, 33 );
  m_mapLetters[55] = tTextureSection( m_pTextureFont, 136, 0, 10, 33 );
  m_mapLetters[56] = tTextureSection( m_pTextureFont, 146, 0, 10, 33 );
  m_mapLetters[57] = tTextureSection( m_pTextureFont, 156, 0, 10, 33 );
  m_mapLetters[61] = tTextureSection( m_pTextureFont, 166, 0, 11, 33 );
  m_mapLetters[63] = tTextureSection( m_pTextureFont, 177, 0, 11, 33 );
  m_mapLetters[64] = tTextureSection( m_pTextureFont, 188, 0, 19, 33 );
  m_mapLetters[65] = tTextureSection( m_pTextureFont, 207, 0, 11, 33 );
  m_mapLetters[66] = tTextureSection( m_pTextureFont, 218, 0, 10, 33 );
  m_mapLetters[67] = tTextureSection( m_pTextureFont, 228, 0, 10, 33 );
  m_mapLetters[68] = tTextureSection( m_pTextureFont, 238, 0, 10, 33 );
  m_mapLetters[73] = tTextureSection( m_pTextureFont, 248, 0, 6, 33 );
  m_mapLetters[69] = tTextureSection( m_pTextureFont, 18, 33, 10, 33 );
  m_mapLetters[70] = tTextureSection( m_pTextureFont, 18, 66, 10, 33 );
  m_mapLetters[71] = tTextureSection( m_pTextureFont, 18, 99, 10, 33 );
  m_mapLetters[74] = tTextureSection( m_pTextureFont, 18, 132, 10, 33 );
  m_mapLetters[79] = tTextureSection( m_pTextureFont, 18, 165, 10, 33 );
  m_mapLetters[80] = tTextureSection( m_pTextureFont, 18, 198, 10, 33 );
  m_mapLetters[72] = tTextureSection( m_pTextureFont, 28, 33, 11, 33 );
  m_mapLetters[75] = tTextureSection( m_pTextureFont, 28, 66, 11, 33 );
  m_mapLetters[76] = tTextureSection( m_pTextureFont, 28, 99, 11, 33 );
  m_mapLetters[78] = tTextureSection( m_pTextureFont, 28, 132, 11, 33 );
  m_mapLetters[81] = tTextureSection( m_pTextureFont, 28, 165, 10, 33 );
  m_mapLetters[82] = tTextureSection( m_pTextureFont, 28, 198, 10, 33 );
  m_mapLetters[77] = tTextureSection( m_pTextureFont, 39, 33, 15, 33 );
  m_mapLetters[83] = tTextureSection( m_pTextureFont, 39, 66, 11, 33 );
  m_mapLetters[84] = tTextureSection( m_pTextureFont, 39, 99, 10, 33 );
  m_mapLetters[85] = tTextureSection( m_pTextureFont, 39, 132, 10, 33 );
  m_mapLetters[86] = tTextureSection( m_pTextureFont, 39, 165, 11, 33 );
  m_mapLetters[87] = tTextureSection( m_pTextureFont, 39, 198, 14, 33 );
  m_mapLetters[88] = tTextureSection( m_pTextureFont, 54, 33, 12, 33 );
  m_mapLetters[89] = tTextureSection( m_pTextureFont, 66, 33, 10, 33 );
  m_mapLetters[90] = tTextureSection( m_pTextureFont, 76, 33, 10, 33 );
  m_mapLetters[91] = tTextureSection( m_pTextureFont, 86, 33, 10, 33 );
  m_mapLetters[92] = tTextureSection( m_pTextureFont, 96, 33, 15, 33 );
  m_mapLetters[93] = tTextureSection( m_pTextureFont, 111, 33, 10, 33 );
  m_mapLetters[95] = tTextureSection( m_pTextureFont, 121, 33, 10, 33 );
  m_mapLetters[97] = tTextureSection( m_pTextureFont, 131, 33, 11, 33 );
  m_mapLetters[98] = tTextureSection( m_pTextureFont, 142, 33, 10, 33 );
  m_mapLetters[99] = tTextureSection( m_pTextureFont, 152, 33, 10, 33 );
  m_mapLetters[100] = tTextureSection( m_pTextureFont, 162, 33, 10, 33 );
  m_mapLetters[101] = tTextureSection( m_pTextureFont, 172, 33, 10, 33 );
  m_mapLetters[102] = tTextureSection( m_pTextureFont, 182, 33, 10, 33 );
  m_mapLetters[103] = tTextureSection( m_pTextureFont, 192, 33, 10, 33 );
  m_mapLetters[104] = tTextureSection( m_pTextureFont, 202, 33, 11, 33 );
  m_mapLetters[105] = tTextureSection( m_pTextureFont, 213, 33, 6, 33 );
  m_mapLetters[106] = tTextureSection( m_pTextureFont, 219, 33, 10, 33 );
  m_mapLetters[107] = tTextureSection( m_pTextureFont, 229, 33, 11, 33 );
  m_mapLetters[108] = tTextureSection( m_pTextureFont, 240, 33, 11, 33 );
  m_mapLetters[109] = tTextureSection( m_pTextureFont, 54, 66, 15, 33 );
  m_mapLetters[110] = tTextureSection( m_pTextureFont, 54, 99, 11, 33 );
  m_mapLetters[111] = tTextureSection( m_pTextureFont, 54, 132, 10, 33 );
  m_mapLetters[112] = tTextureSection( m_pTextureFont, 54, 165, 10, 33 );
  m_mapLetters[113] = tTextureSection( m_pTextureFont, 54, 198, 10, 33 );
  m_mapLetters[114] = tTextureSection( m_pTextureFont, 69, 66, 10, 33 );
  m_mapLetters[116] = tTextureSection( m_pTextureFont, 69, 99, 10, 33 );
  m_mapLetters[117] = tTextureSection( m_pTextureFont, 69, 132, 10, 33 );
  m_mapLetters[121] = tTextureSection( m_pTextureFont, 69, 165, 10, 33 );
  m_mapLetters[122] = tTextureSection( m_pTextureFont, 69, 198, 10, 33 );
  m_mapLetters[115] = tTextureSection( m_pTextureFont, 79, 66, 11, 33 );
  m_mapLetters[118] = tTextureSection( m_pTextureFont, 79, 99, 11, 33 );
  m_mapLetters[119] = tTextureSection( m_pTextureFont, 90, 66, 14, 33 );
  m_mapLetters[120] = tTextureSection( m_pTextureFont, 104, 66, 12, 33 );

  HFONT   hTempFont = CreateFont( 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Arial" );
  D3DXCreateFont( m_pd3dDevice,
                  hTempFont,
                  &m_pFont );
  DeleteObject( hTempFont );

}



void LDApp::UnloadResources()
{
  for ( int i = 0; i < LD::SOUND_LAST_ENTRY; ++i )
  {
    if ( m_pSound[i] )
    {
      m_pSound[i]->Release();
    }
  }
  Unload3dResources();
}



void LDApp::Unload3dResources()
{
  if ( m_pFont )
  {
    m_pFont->Release();
    m_pFont = NULL;
  }
  std::set<IDirect3DTexture8*>::iterator    itT( m_LoadedTextures.begin() );
  while ( itT != m_LoadedTextures.end() )
  {
    IDirect3DTexture8*    pTex = *itT;

    if ( pTex )
    {
      pTex->Release();
    }
    ++itT;
  }
  m_LoadedTextures.clear();
  m_mapLetters.clear();
}



void LDApp::PlaySound( LD::SoundTypes Sound )
{
  IDirectSoundBuffer*   pBuffer = m_pSound[Sound];

  DWORD   Status;

  if ( pBuffer == NULL )
  {
    return;
  }

  if ( SUCCEEDED( pBuffer->GetStatus( &Status ) ) )
  {
    if ( ( Status & DSBSTATUS_PLAYING )
    ||   ( Status & DSBSTATUS_LOOPING ) )
    {
      pBuffer->Stop();
      pBuffer->SetCurrentPosition( 0 );
    }
  }

  pBuffer->SetCurrentPosition( 0 );
  pBuffer->Play( 0, 0, 0 );
}



void LDApp::LoopSound( LD::SoundTypes Sound )
{

  IDirectSoundBuffer*   pBuffer = m_pSound[Sound];

  DWORD   Status;

  if ( SUCCEEDED( pBuffer->GetStatus( &Status ) ) )
  {
    if ( ( Status & DSBSTATUS_PLAYING )
    ||   ( Status & DSBSTATUS_LOOPING ) )
    {
      pBuffer->Stop();
      pBuffer->SetCurrentPosition( 0 );
    }
  }

  pBuffer->Play( 0, 0, DSBPLAY_LOOPING );

}



void LDApp::StopSound( LD::SoundTypes Sound )
{

  IDirectSoundBuffer*   pBuffer = m_pSound[Sound];

  DWORD   Status;

  if ( SUCCEEDED( pBuffer->GetStatus( &Status ) ) )
  {
    if ( ( Status & DSBSTATUS_PLAYING )
    ||   ( Status & DSBSTATUS_LOOPING ) )
    {
      pBuffer->Stop();
    }
  }

}



void LDApp::RenderButton( int X, int Y, int W, int H, LD::TextureTypes Tex1, LD::TextureTypes Tex2, LD::TextureTypes Tex3 )
{

  if ( ( m_MouseX >= X )
  &&   ( m_MouseX < X + W )
  &&   ( m_MouseY >= Y )
  &&   ( m_MouseY < Y + H ) )
  {
    if ( m_MouseButtons & 1 )
    {
      theApp.RenderTextureSection2d( X, Y, theApp.m_TexSec[Tex3] );
    }
    else
    {
      theApp.RenderTextureSection2d( X, Y, theApp.m_TexSec[Tex2] );
    }
  }
  else
  {
    theApp.RenderTextureSection2d( X, Y, theApp.m_TexSec[Tex1] );
  }

}



int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{

  theApp.Run();

  return 0;

}