//===================================================================
// NOM : main.cpp
// AUT : Jérôme Fuselier 
// MEL : jerome.fuselier@free.fr
// WEB : http://jerome.fuselier.free.fr
// ROL : Point d'entrée dans le programme
//===================================================================


//===================================================================
// Fichiers include
//===================================================================


// Indique au compilateur de charger la librairie dans le projet
// vous pouvez la rajouter à la main dans les options du projet
// mais c'est le meilleur moyen de l'oublier à mon avis
#pragma comment(lib, "d3d8.lib")

#include "windows.h"
#include "resource.h"
#include <d3d8.h>


//===================================================================
// Variables globales
//===================================================================


// La fenètre windows
HWND  g_hwnd;
LPCSTR   g_nom_classe = "dx_tut";
LPCSTR  g_titre_fenetre = "DirectX - Tutoriel n°1";
int      g_largeur = 800;
int      g_hauteur = 600;

// Pour Direct3D
LPDIRECT3D8 g_d3d = NULL;
LPDIRECT3DDEVICE8 g_device = NULL;



//===================================================================
// Signatures des fonctions
//===================================================================

HWND creer_fenetre(HINSTANCE hInstance);
void detruire_fenetre(HWND hwnd, HINSTANCE hInstance);

HWND creer_fenetre(HINSTANCE hInstance);
void detruire_d3d();
void render();

LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam,
                  LPARAM lParam);
INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,
               LPSTR lpCmdLine, INT nCmdShow);


//===================================================================
// ROL : Créer une fenètre et sa classe
//===================================================================
HWND creer_fenetre(HINSTANCE hInstance)
{
   // Création de la classe de la fenètre

   // Une structure qui sauvegarde les informations de la classe
   WNDCLASS wc;
   wc.style = 0;
   wc.lpfnWndProc = WndProc;
   wc.cbClsExtra = 0;
   wc.cbWndExtra = 0;
   wc.hInstance = hInstance;
   wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON));
   wc.hCursor = LoadCursor(NULL, IDC_ARROW);
   wc.hbrBackground = NULL;//(HBRUSH)GetStockObject(WHITE_BRUSH);
   wc.lpszMenuName = NULL;
   wc.lpszClassName = g_nom_classe;

   // Enregistrement de la classe de la fenètre
   RegisterClass(&wc);

   // On veut que la partie cliente de la fenètre soit de la bonne 
   // taille
   // => on calcule la taille la fenètre en conséquence
   // en effet, lors de la création de la fenètre, on fournit la 
   // taille de la fenètre (partie cliente + barre de titre, ...)

   DWORD style_fenetre = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU |
                        WS_THICKFRAME | WS_MINIMIZEBOX | WS_VISIBLE;
   // Le rectangle rc va contenir la taille effective de la fenètre
   RECT rc;
   SetRect(&rc, 0, 0, g_largeur, g_hauteur);
   AdjustWindowRect(&rc, style_fenetre, TRUE);

   //AdjustWindowRect
   HWND hwnd = CreateWindow(g_nom_classe, g_titre_fenetre,
                      style_fenetre, CW_USEDEFAULT,
                      CW_USEDEFAULT, rc.right-rc.left,
                      rc.bottom-rc.top, GetDesktopWindow(),
                      NULL, hInstance, NULL);

   // Affichage de la fenètre
   ShowWindow(hwnd, SW_SHOW);
   UpdateWindow(hwnd);

   return hwnd;
}


//===================================================================
// ROL : Détruire une fenètre et sa classe
//===================================================================
void detruire_fenetre(HWND hwnd, HINSTANCE hInstance)
{
   // Destruction de la fenètre
   DestroyWindow(hwnd);
   // Désenregistrement de la classe de la fenètre
   UnregisterClass(g_nom_classe, hInstance);
}


//===================================================================
// ROL : Initialise d3d et le device
//===================================================================
HRESULT init_d3d(HWND hwnd)
{
   HRESULT hr;

   // Création de l'objet d3d
    g_d3d = Direct3DCreate8(D3D_SDK_VERSION);
   if (g_d3d == NULL)
      return E_FAIL;

    // On récupère la résolution du bureau. On récupère ainsi un 
   // format supporté par la carte pour le backbuffer
    D3DDISPLAYMODE d3ddm;
    hr = g_d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);
   if (FAILED(hr))
        return E_FAIL;

    // On définit une structure qui va permettre de paramétrer le 
   // device
    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = d3ddm.Format;

    // Création du device. C'est l'objet le plus important de 
   // direct3D. C'est par lui que l'on accède à la carte graphique 
   // pour rendre la scène
    hr = g_d3d->CreateDevice(D3DADAPTER_DEFAULT,
                      D3DDEVTYPE_HAL,
                      hwnd,
                             D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                             &d3dpp,
                      &g_device);
        return E_FAIL;

    return S_OK;
}


//===================================================================
// ROL : Détruire les objets Direct3D
//===================================================================
void detruire_d3d()
{
   // Libération de la mémoire allouée pour le device
   if (g_device != NULL)
      g_device->Release();

   // Libération de la mémoire allouée pour l'interface Direct3D
   if (g_d3d != NULL)
      g_d3d->Release();
}


//===================================================================
// ROL : Effectue le rednu de la scène
//===================================================================
void render()
{
   // On efface le device
   g_device->Clear(0, NULL, D3DCLEAR_TARGET, 0xffffff00, 1.0f, 0);
   // Début de la scène
   g_device->BeginScene();

   // C'est à cet endroit que l'on doit effectuer le rendu de 
   // la scène

   // Fin de la scène
   g_device->EndScene();
   // Affichage du rendu
   g_device->Present(NULL, NULL, NULL, NULL);
}



//===================================================================
// ROL : handler des messages de la fenètre principale
//===================================================================
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam,
                  LPARAM lParam)
{
   switch (msg)
    {
      // Gestion des messages

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

   // Traitement par défaut des messages non traités
    return DefWindowProc(hWnd, msg, wParam, lParam);
}



//===================================================================
// ROL : Point d'entrée dans le programme
//===================================================================
INT WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrevInst,
               LPSTR lpCmdLine, INT nCmdShow)
{
   // Création de la fenètre windows
   g_hwnd = creer_fenetre(hInst);

   init_d3d(g_hwnd);

   // La boucle de messages
   MSG msg;
   ZeroMemory(&msg, sizeof(msg));
    while (msg.message != WM_QUIT)
    {
      // Si on récupère un message à traiter dans la file
        if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
      // S'il n'y a pas de messages à traiter, on effectue le 
      // rendu de la scène
        else
            render();
    }

   // destruction des objets Direct3D
   detruire_d3d();

   // destruction de la fenètre windows
   detruire_fenetre(g_hwnd, hInst);

   return msg.wParam;
}