#pragma once

#include <windows.h>

#include <d3d8.h>
#include <d3dx8.h>
#include <dsound.h>

#include <map>
#include <set>
#include <vector>

#include "TextureSection.h"
#include "VertexBuffer.h"
#include "GameState.h"
#include "LDTypes.h"
#include "OggDSound.h"


struct Vertex
{
  D3DXVECTOR3   Pos;
  D3DXVECTOR3   Normal;
  DWORD         Diffuse;
  float         TU;
  float         TV;

  Vertex() :
    Diffuse( 0xffffffff ),
    TU( 0.0f ),
    TV( 0.0f )
  {
  }
};



struct Mesh
{
  std::vector<Vertex>       VertexList;

  void AddTriangle( float X1, float Y1, float Z1, DWORD Color1,
                    float X2, float Y2, float Z2, DWORD Color2,
                    float X3, float Y3, float Z3, DWORD Color3 )
  {
    VertexList.resize( VertexList.size() + 3 );

    VertexList[VertexList.size() - 3].Pos     = D3DXVECTOR3( X1, Y1, Z1 );
    VertexList[VertexList.size() - 3].Diffuse = Color1;

    VertexList[VertexList.size() - 2].Pos     = D3DXVECTOR3( X2, Y2, Z2 );
    VertexList[VertexList.size() - 2].Diffuse = Color2;

    VertexList[VertexList.size() - 1].Pos     = D3DXVECTOR3( X3, Y3, Z3 );
    VertexList[VertexList.size() - 1].Diffuse = Color3;
  }



  void AddQuad( float X1, float Y1, float Z1, DWORD Color1,
                float X2, float Y2, float Z2, DWORD Color2,
                float X3, float Y3, float Z3, DWORD Color3,
                float X4, float Y4, float Z4, DWORD Color4 )
  {
    VertexList.resize( VertexList.size() + 6 );

    VertexList[VertexList.size() - 6].Pos     = D3DXVECTOR3( X1, Y1, Z1 );
    VertexList[VertexList.size() - 6].Diffuse = Color1;

    VertexList[VertexList.size() - 5].Pos     = D3DXVECTOR3( X2, Y2, Z2 );
    VertexList[VertexList.size() - 5].Diffuse = Color2;

    VertexList[VertexList.size() - 4].Pos     = D3DXVECTOR3( X3, Y3, Z3 );
    VertexList[VertexList.size() - 4].Diffuse = Color3;

    VertexList[VertexList.size() - 3].Pos     = D3DXVECTOR3( X3, Y3, Z3 );
    VertexList[VertexList.size() - 3].Diffuse = Color3;

    VertexList[VertexList.size() - 2].Pos     = D3DXVECTOR3( X2, Y2, Z2 );
    VertexList[VertexList.size() - 2].Diffuse = Color2;

    VertexList[VertexList.size() - 1].Pos     = D3DXVECTOR3( X4, Y4, Z4 );
    VertexList[VertexList.size() - 1].Diffuse = Color4;
  }
};



class LDApp
{

  public:

    typedef std::map<char,tTextureSection>    tMapLetters;


    IDirect3D8*           m_pd3d;

    IDirect3DDevice8*     m_pd3dDevice;

    IDirect3DTexture8*    m_pTextureFont;
    IDirect3DTexture8*    m_pTextureFX;
    IDirect3DTexture8*    m_pTextureUnits;
    IDirect3DTexture8*    m_pTextureTiles;

    ID3DXFont*            m_pFont;

    IDirectSound*         m_pDirectSound;

    IDirectSoundBuffer*   m_pSound[LD::SOUND_LAST_ENTRY];

    D3DPRESENT_PARAMETERS m_d3dPP;

    std::set<CVertexBuffer*>      m_setVBs;

    CVertexBuffer*        CreateVertexBuffer( Mesh& mesh );

    CVertexBuffer*        CreateVertexBuffer( DWORD dwPrimitiveCount, 
                                              DWORD dwPrimitiveSize, 
                                              DWORD dwStride,
                                              D3DPRIMITIVETYPE dwPrimitiveType, 
                                              DWORD dwUsage, 
                                              DWORD dwFVF, 
                                              D3DPOOL d3dPool );
    void                  RemoveVertexBuffer( CVertexBuffer *pVB );


    tMapLetters           m_mapLetters;

    HWND                  m_hWnd;

    HICON                 m_hIcon;

    DWORD                 m_LastTicks;

    tTextureSection       m_TexSec[LD::TEX_LAST_ENTRY];

    GameState*           m_pCurrentGameState;

    GameState*           m_pNextState;

    float                 m_FixedFrameDelay;

    bool                  m_KeyPressed[256];
    bool                  m_KeyReleased[256];

    float                 m_WavyTextTimeCount;

    int                   m_MouseX;
    int                   m_MouseY;
    int                   m_MouseButtons;

    bool                  m_FullScreen;

    WINDOWPLACEMENT       m_WPLWindowed;
    POINT                 m_WindowPosWindowed;

    CVertexBuffer*        m_pTestVB;

    CDirectSoundOgg       m_Music;


    LDApp();
    ~LDApp();


    void                  RenderTextureSection2d( int X, int Y,
                                                const tTextureSection& TexSection,
                                                DWORD Color = 0xffffffff,
                                                DWORD Color2 = 0,
                                                DWORD Color3 = 0,
                                                DWORD Color4 = 0,
                                                int Width = -1, int Height = -1,
                                                DWORD AlternativeFlags = 0 );
    void                  RenderTextureSectionAsTile2d( int X, int Y,
                                                      const tTextureSection& TexSection,
                                                      DWORD Color = 0xffffffff );
    void                  RenderTextureSectionRotated2d( int X, int Y,
                                                       const tTextureSection& TexSection,
                                                       float Angle,
                                                       DWORD Color,
                                                       DWORD Color2 = 0xffffffff,
                                                       DWORD Color3 = 0xffffffff,
                                                       DWORD Color4 = 0xffffffff,
                                                       int Width = -1, int Height = -1,
                                                       DWORD AlternativeFlags = 0 );
    void                  RenderTextureSectionRotated2d( int X, int Y,
                                                         const tTextureSection& TexSection,
                                                         float Angle,
                                                         float ZoomFactor,
                                                         DWORD Color = 0xffffffff,
                                                         DWORD Color2 = 0,
                                                         DWORD Color3 = 0,
                                                         DWORD Color4 = 0,
                                                         int Width = -1, int Height = -1,
                                                         DWORD AlternativeFlags = 0 );
    void                  RenderQuad2d( int X, int Y, int Width, int Height,
                                      float TU1, float TV1,
                                      float TU2, float TV2,
                                      float TU3, float TV3,
                                      float TU4, float TV4,
                                      DWORD Color,
                                      DWORD Color2 = 0,
                                      DWORD Color3 = 0,
                                      DWORD Color4 = 0 );
    void                  RenderQuad2d( int X, int Y, int Width, int Height,
                                      DWORD Color,
                                      DWORD Color2 = 0,
                                      DWORD Color3 = 0,
                                      DWORD Color4 = 0 );
    void                  RenderQuadRotated2d( int X, int Y, int Width, int Height,
                                             float TU1, float TV1,
                                             float TU2, float TV2,
                                             float TU3, float TV3,
                                             float TU4, float TV4,
                                             float Angle,
                                             DWORD Color,
                                             DWORD Color2 = 0,
                                             DWORD Color3 = 0,
                                             DWORD Color4 = 0 );
    void                  RenderQuadRotated2d( int X, int Y, int Width, int Height,
                                               float TU1, float TV1,
                                               float TU2, float TV2,
                                               float TU3, float TV3,
                                               float TU4, float TV4,
                                               float Angle,
                                               float ZoomFactor,
                                               DWORD Color,
                                               DWORD Color2 = 0,
                                               DWORD Color3 = 0,
                                               DWORD Color4 = 0 );

    void                  RenderQuad( const D3DXVECTOR3& V1,
                                      const D3DXVECTOR3& V2,
                                      const D3DXVECTOR3& V3,
                                      const D3DXVECTOR3& V4,
                                      float TU1, float TV1,
                                      float TU2, float TV2,
                                      float TU3, float TV3,
                                      float TU4, float TV4,
                                      DWORD Color,
                                      DWORD Color2 = 0,
                                      DWORD Color3 = 0,
                                      DWORD Color4 = 0 );
    void                  RenderTextureSection( const D3DXVECTOR3& V1,
                                                const D3DXVECTOR3& V2,
                                                const D3DXVECTOR3& V3,
                                                const D3DXVECTOR3& V4,
                                                const tTextureSection& TexSection,
                                                DWORD Color,
                                                DWORD Color2 = 0,
                                                DWORD Color3 = 0,
                                                DWORD Color4 = 0 );

    void                  Print( int X, int Y, const char* szText, DWORD Color = 0xffffffff );
    void                  PrintNice( int X, int Y, const char* szText, DWORD Color = 0xffffffff );
    void                  PrintNiceCentered( int X, int Y, const char* szText, DWORD Color = 0xffffffff, bool bWavy = false );
    void                  PrintNiceRightAligned( int X, int Y, const char* szText, DWORD Color = 0xffffffff );
    void                  PrintNiceCenteredWavy( int X, int Y, const char* szText, DWORD Color = 0xffffffff );

    IDirect3DTexture8*    LoadTexture( const char* szFileName, DWORD ColorKey = 0 );
    IDirectSoundBuffer*   LoadWave( const char* szFileName );

    static LRESULT        StaticWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
    LRESULT               WindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );

    void                  RenderFrame();
    void                  UpdateFrame( const float fElapsedTime );

    void                  NextState( GameState* pNewState );

    void                  Run();

    void                  LoadResources();
    void                  UnloadResources();
    void                  Load3dResources();
    void                  Unload3dResources();

    void                  PlaySound( LD::SoundTypes Sound );
    void                  LoopSound( LD::SoundTypes Sound );
    void                  StopSound( LD::SoundTypes Sound );

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

    void                  ToggleWindowMode();

};


extern LDApp    theApp;

