// ================================================================
  // Filename: WorldTri.cpp
  // Description: Drawing a Triangle primitive in homogenous world
  //              coordinates
  //
  //              This source corresponds to 32Bits.co.uk DirectX
  //              Basics Series 3 part 1.
  // ================================================================


  #define WIN32_LEAN_AND_MEAN
  #include <windows.h>
  #include <d3d8.h>
  #include <d3dx8.h>
  #include <dxerr8.h>

  #include "D3DFuncs.h"

  // Function declarations
  int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);
  LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );

  HRESULT GameInit();
  HRESULT GameLoop();
  HRESULT GameShutDown();
  HRESULT Render();


  // Globals
  static char strAppname[]="Direct3D Triangle";
  LPDIRECT3D8 g_pD3D;
  LPDIRECT3DDEVICE8 g_pDevice;
  LPDIRECT3DSURFACE8 g_pBackSurface;
  HWND g_hWnd;

  D3DCURRENTSETTINGS g_D3DSettings;

  // Globals specifically for this source

  LPDIRECT3DVERTEXBUFFER8 g_pVertexBuffer;

  typedef struct _tagSimpleVertex
  {
      float   x,y,z;
      float   rhw;
  } SIMPLEVERTEX;

  #define FVF_SIMPLEVERTEX (D3DFVF_XYZRHW)


  //-----------------------------------------------------------------------------
  // Name: WinMain()
  // Desc: The application's entry point
  //-----------------------------------------------------------------------------

  int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
  {
      WNDCLASSEX wc;

      ZeroMemory(&wc, sizeof(WNDCLASSEX));
      wc.cbSize=sizeof(WNDCLASSEX);                       // size of the window struct in bytes
      wc.style=CS_HREDRAW | CS_VREDRAW | CS_OWNDC;        // window styles to use
      wc.lpfnWndProc=MsgProc;                             // function name of event handler
      wc.hInstance=hInstance;                             // handle to this apps instance
      wc.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH);// background colour of window
      wc.hIcon= LoadIcon(NULL, IDI_APPLICATION);          // icon for the app window
      wc.hIconSm=LoadIcon(NULL, IDI_APPLICATION);         // icon when minimized to taskbar
      wc.hCursor=LoadCursor(NULL, IDC_ARROW);             // cursor to use for this window
      wc.lpszClassName=strAppname;                        // name for this class

      // Register the window class
      RegisterClassEx( &wc );

      g_D3DSettings.m_nDeviceWidth=800;
      g_D3DSettings.m_nDeviceHeight=600;

      // Create the application's window
      g_hWnd = CreateWindow(strAppname, strAppname, WS_OVERLAPPEDWINDOW, 10, 10,
                            g_D3DSettings.m_nDeviceWidth, g_D3DSettings.m_nDeviceHeight,
                            NULL, NULL, wc.hInstance, NULL );

      // Show the window
      ShowWindow(g_hWnd, nCmdShow);
      UpdateWindow(g_hWnd);

      if(FAILED(GameInit()))
      {
          UnregisterClass( strAppname, wc.hInstance );
          return -1;
      }

      // Enter the message loop
      MSG msg;
      ZeroMemory( &msg, sizeof(msg) );
      int count=0;
      while( msg.message!=WM_QUIT )
      {
          if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
          {
              TranslateMessage( &msg );
              DispatchMessage( &msg );
          }
          else
          {
              GameLoop();
          }
      }

      GameShutDown();
      UnregisterClass( strAppname, wc.hInstance );
      return 0;
  }


  //-----------------------------------------------------------------------------
  // Name: MsgProc()
  // Desc: The window's message handler
  //-----------------------------------------------------------------------------
  LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
  {
      HDC hDC;
      PAINTSTRUCT PaintStruct;


      switch( msg )
      {
      case WM_PAINT:
          {
              hDC=BeginPaint(hWnd, &PaintStruct);     // Tell windows we want to update the window

              // Do GDI drawing here

              EndPaint(hWnd, &PaintStruct);
              return 0;
          }
      case WM_KEYDOWN:
          {
              switch(wParam)
              {

              case VK_SPACE:
                  {
                      PostQuitMessage( 0 );
                      return 0;
                  }
              }
          }

      case WM_DESTROY:
          {
              PostQuitMessage( 0 );
              return 0;
          }

      default:
          return DefWindowProc( hWnd, msg, wParam, lParam );
      }

  }


  // =====================================================================================
  //  High level functions for initialization, loop and shutdown
  // =====================================================================================

  HRESULT GameInit()
  {
      HRESULT rslt=0;

      g_pD3D=Direct3DCreate8(D3D_SDK_VERSION);
      if(g_pD3D==NULL)
      {
          return D3DError(E_FAIL, __LINE__, __FILE__, "Failed to create a D3D8 object.");
      }


      // Populate our struct with how we want to set up D3D...
      g_D3DSettings.m_bWindowed=TRUE;
      g_D3DSettings.m_bMultiSampling=FALSE;
      g_D3DSettings.m_D3DFormat=D3DFMT_X8R8G8B8;

      // ...and pass it to our function to create the device!
      rslt=InitDirect3DDevice(g_hWnd, g_D3DSettings, g_pD3D, &g_pDevice);
      if(FAILED(rslt))
      {
          return E_FAIL;
      }

      // ===================================================================================
      // Set up our primitive and vertex buffer
      // ===================================================================================

      // Create an array to hold our triangle's 3 vertices:
      //      v1
      //      /\
      //     /  \
      // v0 /____\ v2

      SIMPLEVERTEX Triangle[3];
      Triangle[0].x=50.0f;
      Triangle[0].y=200.0f;
      Triangle[0].z=0.0f;
      Triangle[0].rhw=1.0f;

      Triangle[1].x=150.0f;
      Triangle[1].y=50.0f;
      Triangle[1].z=0.0f;
      Triangle[1].rhw=1.0f;

      Triangle[2].x=250.0f;
      Triangle[2].y=200.0f;
      Triangle[2].z=0.0f;
      Triangle[2].rhw=1.0f;

      // Next, create our vertex buffer using the properties of our custom struct and FVF
      rslt=g_pDevice->CreateVertexBuffer(sizeof(Triangle), 0, FVF_SIMPLEVERTEX, D3DPOOL_DEFAULT,
                                         &g_pVertexBuffer);
      if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "CreateVertexBuffer() failed."); }

      // Now lock the vertex buffer...
      BYTE* pVerticeLock=0;
      rslt=g_pVertexBuffer->Lock(0, sizeof(Triangle), &pVerticeLock, 0);
      if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Failed to lock Vertex Buffer."); }

      // ...and copy our array of vertices straight into it
      CopyMemory(pVerticeLock, &Triangle, sizeof(Triangle));

      // Remember to unlock the vertex buffer once we're done.
      g_pVertexBuffer->Unlock();


      return S_OK;
  }

  HRESULT GameLoop()
  {
      return Render();
  }

  HRESULT GameShutDown()
  {
      // Don't forget to release the vertex buffer - it's a COM interface!
      if(g_pVertexBuffer)
          g_pVertexBuffer->Release();

      if(g_pBackSurface)
          g_pBackSurface->Release();
      if(g_pDevice)
          g_pDevice->Release();
      if(g_pD3D)
          g_pD3D->Release();
      return S_OK;
  }



  // =====================================================================================
  // Main render function to perform D3D drawing
  // =====================================================================================

  HRESULT Render()
  {
      HRESULT rslt=NULL;


      // ====================================================================================
      // - Do all the usual checks to make sure we have the right pointers, etc...
      // ====================================================================================

      // Make sure we have a valid D3D Device
      if(!g_pDevice) { return E_FAIL; }

      // Return if the device is not ready
      rslt=ValidateDevice(g_pDevice, g_pBackSurface, g_D3DSettings);
      if(FAILED(rslt)) { return rslt; }

      // Clear the back buffer
      g_pDevice->Clear(0,0, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,55), 1.0f, 0);

      // Get a pointer to the back buffer (remember, page flipping has taken place)
      rslt=g_pDevice->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &g_pBackSurface);
      if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Failed to get the back buffer."); }



      rslt=g_pDevice->BeginScene();
      if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "BeginScene() failed."); }

      // ====================================================================================
      // - Do our drawing operations
      // ====================================================================================


      // Tell D3D we want to use our vertex buffer as the "source stream"
      g_pDevice->SetStreamSource(0, g_pVertexBuffer, sizeof(SIMPLEVERTEX));

      // Tell D3D we want to use our custom FVF define
      g_pDevice->SetVertexShader(FVF_SIMPLEVERTEX);

      // And finally tell D3D to draw our triangle!
      g_pDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);


      // ====================================================================================
      // - Clean up and present the back buffer to be page flipped
      // ====================================================================================

      g_pDevice->EndScene();
      g_pBackSurface->Release();

      // Present the back buffer to the display adapter to be drawn
      g_pDevice->Present(NULL, NULL, NULL, NULL);


      return S_OK;
  }