#if defined(UNICODE) && !defined(_UNICODE)
    #define _UNICODE
#elif defined(_UNICODE) && !defined(UNICODE)
    #define UNICODE
#endif

//#include <tchar.h>
#include <windows.h>
#include <GL/glew.h>
#include <GL/wglew.h>

#include "Tscene.h"

static HINSTANCE	m_hInstance;
static HWND			m_hwnd;
static HGLRC		m_hrc;
static int			m_window_w, m_window_h;
static LPCSTR       m_title;

Tscene              scene;

// déclaration des entêtes
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void close(LPCSTR title);
bool createOpenGLContext();
bool createWindow(LPCSTR title, DWORD windowStyle, int x, int y, int width, int height);
bool createWindowFullScreen(LPCSTR title, DWORD windowStyle, int width, int height);

// boucle
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    m_title = "scene de test";

    //ECO_error_set("start");
    //chemin icone
    //m_iconName = "donnees/eco.ico";

	// --- fenêtre normale ---

	DWORD windowStyle = WS_OVERLAPPEDWINDOW;		// style standard
	//DWORD windowStyle = WS_CAPTION;				// no resize no system
	//DWORD windowStyle = WS_CAPTION | WS_SYSMENU;	// no resize
	//DWORD windowStyle = WS_POPUPWINDOW;					// plein ecran sans caption
	//DWORD windowStyle = WS_CAPTION | WS_EX_TOPMOST;	// plein ecran sans caption et pas de fenetre par dessus
	int x=0, y=0;
		//pos centrée
		//x = (GetSystemMetrics(SM_CXSCREEN) - width) / 2;
		//y = (GetSystemMetrics(SM_CYSCREEN) - height) / 2;
		// Windows decide de la  position
		//x = CW_USEDEFAULT;
		//y = CW_USEDEFAULT;

	m_window_w = 1600;
	m_window_h = 900;
	createWindow(m_title, windowStyle, x,y,m_window_w,m_window_h);
/*
	// --- ou plein ecran ---
	//En plein écran, on règle l'écran à la taille maximale du bureau
	m_window_w = GetSystemMetrics(SM_CXSCREEN);
	m_window_h = GetSystemMetrics(SM_CYSCREEN);
	createWindowFullScreen(m_title, WS_POPUPWINDOW,m_window_w,m_window_h);
*/
	//initialise les variables indispensables
	ECO_setHWND(m_hwnd);
	ECO_setViewPort(m_window_w, m_window_h);

	// boucle de programme
	scene.run();
	//close(m_title); >> quand event destroy

	return 0; //(int) msg.wParam;
}

void close(LPCSTR title)
{
    scene.close();

    ShowCursor(true);           // Montre le curseur de la souris.
    // Change les paramètres d'affichage si l'on quitte le mode plein écran.
    //if (m_fullScreen)
    //    ChangeDisplaySettings(NULL, 0);
	//DestroyIcon(m_hIcon);     //l'icone créée
    HDC hdc = GetDC(m_hwnd);
	wglMakeCurrent(hdc, 0);     // Enlever le contexte de rendu du contexte matériel
	wglDeleteContext(m_hrc);    // Supprimer le contexte de rendu
	ReleaseDC(m_hwnd, hdc);     // Libérer le contexte matériel de la fenêtre
    DestroyWindow(m_hwnd);      // Détruit la fenêtre.
    m_hwnd = NULL;
    UnregisterClass(title, m_hInstance);// Détruit l'instance de l'application.
    m_hInstance = NULL;
}

bool createOpenGLContext()
{
	HDC	hdc = GetDC(m_hwnd);

	PIXELFORMATDESCRIPTOR pfd; // Create a new PIXELFORMATDESCRIPTOR (PFD)
	memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); // Clear our  PFD
	pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); // Set the size of the PFD to the size of the class
	pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW; // Enable double buffering, opengl support and drawing to a window
	pfd.iPixelType = PFD_TYPE_RGBA; // Set our application to use RGBA pixels
	pfd.cColorBits = 32; // Give us 32 bits of color information (the higher, the more colors)
	pfd.cDepthBits = 32; // Give us 32 bits of depth information (the higher, the more depth levels)
	pfd.iLayerType = PFD_MAIN_PLANE; // Set the layer of the PFD

	int nPixelFormat = ChoosePixelFormat(hdc, &pfd); // Check if our PFD is valid and get a pixel format back
	if (nPixelFormat == 0) // If it fails
    {
        ECO_error_set("Erreur nPixelFormat >createOpenGLContext");
        return false;
    }

	bool bResult = SetPixelFormat(hdc, nPixelFormat, &pfd); // Try and set the pixel format based on our PFD
	if (!bResult)
    {
        ECO_error_set("Erreur bResult >createOpenGLContext");
        return false;
    }

	// On crée un contexte OpenGL 2.1 pour le contexte matériel
	HGLRC temp = wglCreateContext(hdc);
	wglMakeCurrent(hdc, temp);
	// puis active GLEW
	GLenum error = glewInit();
	if (error != GLEW_OK)
	{
		ECO_error_set("Erreur d'initialisation de GLEW : %s >createOpenGLContext", glewGetErrorString(error));
		return false;
	}
	int attributes[] =
	{
		WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
		WGL_CONTEXT_MINOR_VERSION_ARB, 2,
		WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, // Compatibilité ascendante
		0
	};

	// Si l'extension de création de contexte OpenGL 3.x est disponible
	if (glewIsSupported("GL_create_context") == 1)
	{
		m_hrc = wglCreateContextAttribsARB(hdc, NULL, attributes);
		wglMakeCurrent(NULL, NULL); // On supprime le contexte temporaire
		wglDeleteContext(temp); // On supprime le contexte OpenGL 2.1 temporaire
		if (wglMakeCurrent(hdc, m_hrc) == false) // On passe au contexte OpenGL 3.0
		{
			ECO_error_set("Erreur création du contexte 3.0 >createOpenGLContext");
			return false;
		}
	}
	else
	{
		m_hrc = temp;
	}
/*
	int glVersion[2] = { -1, -1 }; // Set some default values for the version
	glGetIntegerv(GL_MAJOR_VERSION, &glVersion[0]); // Get back the OpenGL MAJOR version we are using
	glGetIntegerv(GL_MINOR_VERSION, &glVersion[1]); // Get back the OpenGL MAJOR version we are using
	ECO_error_set("Using OpenGL: %d %d", glVersion[0], glVersion[1]);
*/
	return true;
}

bool createWindow(LPCSTR title, DWORD windowStyle, int x, int y, int width, int height)
{
    WNDCLASSEX windowClass;
	DWORD windowExtendedStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;

	m_hInstance = GetModuleHandle(NULL);    // Obtention de l'instance de cette application.

    windowClass.hInstance = m_hInstance;
	windowClass.lpszClassName = title;
	windowClass.lpfnWndProc = WndProc;	// lien vers les messages
	windowClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	windowClass.cbSize = sizeof(WNDCLASSEX);
	windowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	windowClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
	windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	windowClass.lpszMenuName = NULL;	// No menu
	windowClass.cbClsExtra = 0;         // No extra bytes after the window class
	windowClass.cbWndExtra = 0;         // structure or the window instance
	windowClass.hbrBackground = NULL;

	if (!RegisterClassEx(&windowClass))
    {
        ECO_error_set("échec RegisterClass");
        return false;
    }

    // pour obtenir la taille en tenant compte (ajoutant) les bordures
	RECT windowRect = { 0,0,width,height };
	AdjustWindowRectEx(&windowRect, windowStyle, false, windowExtendedStyle);

	m_hwnd = CreateWindowEx(
		windowExtendedStyle,
		title,
		title,
		windowStyle,
		x,y,
		windowRect.right - windowRect.left,
		windowRect.bottom - windowRect.top,
		NULL,
		NULL,
		m_hInstance,
		NULL);

	// ajout d'une icone sans passer par un fichier ressource / DestroyIcon dans le destructeur
	/*
	m_hIcon = (HICON)LoadImage(NULL, m_iconName, IMAGE_ICON, 32, 32, LR_LOADFROMFILE);
	if(m_hIcon)
	{
		SendMessage(m_hwnd, WM_SETICON, ICON_BIG, (LPARAM)m_hIcon);
		SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, (LPARAM)m_hIcon);
	}
	*/

	createOpenGLContext();

	ShowWindow(m_hwnd, SW_SHOW);
	UpdateWindow(m_hwnd);

	return true;
}

bool createWindowFullScreen(LPCSTR title, DWORD windowStyle, int width, int height)
{
	WNDCLASSEX windowClass;
	DWORD windowExtendedStyle = WS_EX_APPWINDOW; // |= WS_EX_TOPMOST; //aucune fenêtre ne vient par dessus

	m_hInstance = GetModuleHandle(NULL);

	windowClass.hInstance = m_hInstance;
	windowClass.lpszClassName = title;
	windowClass.lpfnWndProc = WndProc;	// lien vers les messages
	windowClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	windowClass.cbSize = sizeof(WNDCLASSEX);
	windowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	windowClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
	windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	windowClass.lpszMenuName = NULL;	// No menu
	windowClass.cbClsExtra = 0;         // No extra bytes after the window class
	windowClass.cbWndExtra = 0;         // structure or the window instance
	windowClass.hbrBackground = NULL;

	if (!RegisterClassEx(&windowClass))
	{
		return false;
	}

	DEVMODE dmScreenSettings;
	memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
	dmScreenSettings.dmSize = sizeof(dmScreenSettings);
	dmScreenSettings.dmPelsWidth = width;
	dmScreenSettings.dmPelsHeight = height;
	dmScreenSettings.dmBitsPerPel = 32;
	dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
	if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
	{
		ECO_error_set("échec passage en plein écran");
		return false;
	}

	// pour obtenir la taille en tenant compte (ajoutant) les bordures
	RECT windowRect = { 0,0,width,height };
	AdjustWindowRectEx(&windowRect, windowStyle, false, windowExtendedStyle);

	m_hwnd = CreateWindowEx(
		windowExtendedStyle,
		title,
		title,
		windowStyle,
		0, 0,
		windowRect.right - windowRect.left,
		windowRect.bottom - windowRect.top,
		NULL,
		NULL,
		m_hInstance,
		NULL);

	createOpenGLContext();

	ShowWindow(m_hwnd, SW_SHOW);
	UpdateWindow(m_hwnd);

	return true;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_SIZE:
		{
			int width_old = m_window_w;
			int height_old = m_window_h;
			m_window_w = LOWORD(lParam);
			m_window_h = HIWORD(lParam);
			ECO_setViewPort(m_window_w, m_window_h);
			scene.resize(width_old, height_old, m_window_w, m_window_h);
		}
		break;
	case WM_DESTROY:
	    close(m_title);
		PostQuitMessage(0);
		break;
	}

	return DefWindowProc(hWnd, message, wParam, lParam);
}

