#ifndef GP_NO_PLATFORM
#ifdef WIN32

#include "Base.h"
#include "Platform.h"
#include "FileSystem.h"
#include "Game.h"
#include "Form.h"
#include "Vector2.h"
#include "ScriptController.h"
#if defined(GP_USE_ANGLE)
#include <EGL/egl.h>
#include <EGL/eglext.h>
#else
#include <GL/wglew.h>
#endif
#include <windowsx.h>
#include <Commdlg.h>
#include <shellapi.h>
#ifdef GP_USE_GAMEPAD
#include <XInput.h>
#endif

using gameplay::print;

// Window defaults
#define DEFAULT_RESOLUTION_X 1024
#define DEFAULT_RESOLUTION_Y 768
#define DEFAULT_COLOR_BUFFER_SIZE 32
#define DEFAULT_DEPTH_BUFFER_SIZE 24
#define DEFAULT_STENCIL_BUFFER_SIZE 8

static double __timeTicksPerMillis;
static double __timeStart;
static double __timeAbsolute;
static bool __vsync = WINDOW_VSYNC;
static HINSTANCE __hinstance = 0;
static HWND __attachToWindow = 0;
static HWND __hwnd = 0;
static HDC __hdc = 0;
static HGLRC __hrc = 0;
static bool __mouseCaptured = false;
static POINT __mouseCapturePoint = { 0, 0 };
static bool __multiSampling = false;
static bool __cursorVisible = true;
static unsigned int __gamepadsConnected = 0;


#ifdef GP_USE_GAMEPAD
static const unsigned int XINPUT_BUTTON_COUNT = 14;
static const unsigned int XINPUT_JOYSTICK_COUNT = 2;
static const unsigned int XINPUT_TRIGGER_COUNT = 2;

static XINPUT_STATE __xInputState;
static bool __connectedXInput[4];
static float normalizeXInputJoystickAxis(int axisValue, int deadZone)
{
	int absAxisValue = abs(axisValue);

	if (absAxisValue < deadZone)
	{
		return 0.0f;
	}
	else
	{
		int value = axisValue;
		int maxVal;
		if (value < 0)
		{
			value = -1;
			maxVal = 32768;
		}
		else if (value > 0)
		{
			value = 1;
			maxVal = 32767;
		}
		else
		{
			return 0;
		}

		return value * (absAxisValue - deadZone) / (float)(maxVal - deadZone);
	}
}
#endif

#if defined(GP_USE_ANGLE)
static EGLDisplay __eglDisplay = EGL_NO_DISPLAY;
static EGLContext __eglContext = EGL_NO_CONTEXT;
static EGLSurface __eglSurface = EGL_NO_SURFACE;
static EGLConfig __eglConfig = 0;
static const char* __glExtensions;
PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray = NULL;
PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays = NULL;
PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays = NULL;
PFNGLISVERTEXARRAYOESPROC glIsVertexArray = NULL;

static EGLenum checkErrorEGL(const char* msg)
{
	GP_ASSERT(msg);
	static const char* errmsg[] =
	{
		"EGL function succeeded",
		"EGL is not initialized, or could not be initialized, for the specified display",
		"EGL cannot access a requested resource",
		"EGL failed to allocate resources for the requested operation",
		"EGL fail to access an unrecognized attribute or attribute value was passed in an attribute list",
		"EGLConfig argument does not name a valid EGLConfig",
		"EGLContext argument does not name a valid EGLContext",
		"EGL current surface of the calling thread is no longer valid",
		"EGLDisplay argument does not name a valid EGLDisplay",
		"EGL arguments are inconsistent",
		"EGLNativePixmapType argument does not refer to a valid native pixmap",
		"EGLNativeWindowType argument does not refer to a valid native window",
		"EGL one or more argument values are invalid",
		"EGLSurface argument does not name a valid surface configured for rendering",
		"EGL power management event has occurred",
	};
	EGLenum error = eglGetError();
print("%s: %s.", msg, errmsg[error - EGL_SUCCESS]);
	return error;
}
#endif


static gameplay::Keyboard::Key getKey(WPARAM win32KeyCode, bool shiftDown)
{
    switch (win32KeyCode)
    {
    case VK_PAUSE:
        return gameplay::Keyboard::KEY_PAUSE;
    case VK_SCROLL:
        return gameplay::Keyboard::KEY_SCROLL_LOCK;
    case VK_PRINT:
        return gameplay::Keyboard::KEY_PRINT;
    case VK_ESCAPE:
        return gameplay::Keyboard::KEY_ESCAPE;
    case VK_BACK:
    case VK_F16: // generated by CTRL + BACKSPACE
        return gameplay::Keyboard::KEY_BACKSPACE;
    case VK_TAB:
        return shiftDown ? gameplay::Keyboard::KEY_BACK_TAB : gameplay::Keyboard::KEY_TAB;
    case VK_RETURN:
        return gameplay::Keyboard::KEY_RETURN;
    case VK_CAPITAL:
        return gameplay::Keyboard::KEY_CAPS_LOCK;
    case VK_SHIFT:
        return gameplay::Keyboard::KEY_SHIFT;
    case VK_CONTROL:
        return gameplay::Keyboard::KEY_CTRL;
    case VK_MENU:
        return gameplay::Keyboard::KEY_ALT;
    case VK_APPS:
        return gameplay::Keyboard::KEY_MENU;
    case VK_LSHIFT:
        return gameplay::Keyboard::KEY_SHIFT;
    case VK_RSHIFT:
        return gameplay::Keyboard::KEY_SHIFT;
    case VK_LCONTROL:
        return gameplay::Keyboard::KEY_CTRL;
    case VK_RCONTROL:
        return gameplay::Keyboard::KEY_CTRL;
    case VK_LMENU:
        return gameplay::Keyboard::KEY_ALT;
    case VK_RMENU:
        return gameplay::Keyboard::KEY_ALT;
    case VK_LWIN:
    case VK_RWIN:
        return gameplay::Keyboard::KEY_HYPER;
    case VK_BROWSER_SEARCH:
        return gameplay::Keyboard::KEY_SEARCH;
    case VK_INSERT:
        return gameplay::Keyboard::KEY_INSERT;
    case VK_HOME:
        return gameplay::Keyboard::KEY_HOME;
    case VK_PRIOR:
        return gameplay::Keyboard::KEY_PG_UP;
    case VK_DELETE:
        return gameplay::Keyboard::KEY_DELETE;
    case VK_END:
        return gameplay::Keyboard::KEY_END;
    case VK_NEXT:
        return gameplay::Keyboard::KEY_PG_DOWN;
    case VK_LEFT:
        return gameplay::Keyboard::KEY_LEFT_ARROW;
    case VK_RIGHT:
        return gameplay::Keyboard::KEY_RIGHT_ARROW;
    case VK_UP:
        return gameplay::Keyboard::KEY_UP_ARROW;
    case VK_DOWN:
        return gameplay::Keyboard::KEY_DOWN_ARROW;
    case VK_NUMLOCK:
        return gameplay::Keyboard::KEY_NUM_LOCK;
    case VK_ADD:
        return gameplay::Keyboard::KEY_KP_PLUS;
    case VK_SUBTRACT:
        return gameplay::Keyboard::KEY_KP_MINUS;
    case VK_MULTIPLY:
        return gameplay::Keyboard::KEY_KP_MULTIPLY;
    case VK_DIVIDE:
        return gameplay::Keyboard::KEY_KP_DIVIDE;
    case VK_NUMPAD7:
        return gameplay::Keyboard::KEY_KP_HOME;
    case VK_NUMPAD8:
        return gameplay::Keyboard::KEY_KP_UP;
    case VK_NUMPAD9:
        return gameplay::Keyboard::KEY_KP_PG_UP;
    case VK_NUMPAD4:
        return gameplay::Keyboard::KEY_KP_LEFT;
    case VK_NUMPAD5:
        return gameplay::Keyboard::KEY_KP_FIVE;
    case VK_NUMPAD6:
        return gameplay::Keyboard::KEY_KP_RIGHT;
    case VK_NUMPAD1:
        return gameplay::Keyboard::KEY_KP_END;
    case VK_NUMPAD2:
        return gameplay::Keyboard::KEY_KP_DOWN;
    case VK_NUMPAD3:
        return gameplay::Keyboard::KEY_KP_PG_DOWN;
    case VK_NUMPAD0:
        return gameplay::Keyboard::KEY_KP_INSERT;
    case VK_DECIMAL:
        return gameplay::Keyboard::KEY_KP_DELETE;
    case VK_F1:
        return gameplay::Keyboard::KEY_F1;
    case VK_F2:
        return gameplay::Keyboard::KEY_F2;
    case VK_F3:
        return gameplay::Keyboard::KEY_F3;
    case VK_F4:
        return gameplay::Keyboard::KEY_F4;
    case VK_F5:
        return gameplay::Keyboard::KEY_F5;
    case VK_F6:
        return gameplay::Keyboard::KEY_F6;
    case VK_F7:
        return gameplay::Keyboard::KEY_F7;
    case VK_F8:
        return gameplay::Keyboard::KEY_F8;
    case VK_F9:
        return gameplay::Keyboard::KEY_F9;
    case VK_F10:
        return gameplay::Keyboard::KEY_F10;
    case VK_F11:
        return gameplay::Keyboard::KEY_F11;
    case VK_F12:
        return gameplay::Keyboard::KEY_F12;
    case VK_SPACE:
        return gameplay::Keyboard::KEY_SPACE;
    case 0x30:
        return shiftDown ? gameplay::Keyboard::KEY_RIGHT_PARENTHESIS : gameplay::Keyboard::KEY_ZERO;
    case 0x31:
        return shiftDown ? gameplay::Keyboard::KEY_EXCLAM : gameplay::Keyboard::KEY_ONE;
    case 0x32:
        return shiftDown ? gameplay::Keyboard::KEY_AT : gameplay::Keyboard::KEY_TWO;
    case 0x33:
        return shiftDown ? gameplay::Keyboard::KEY_NUMBER : gameplay::Keyboard::KEY_THREE;
    case 0x34:
        return shiftDown ? gameplay::Keyboard::KEY_DOLLAR : gameplay::Keyboard::KEY_FOUR;
    case 0x35:
        return shiftDown ? gameplay::Keyboard::KEY_PERCENT : gameplay::Keyboard::KEY_FIVE;
    case 0x36:
        return shiftDown ? gameplay::Keyboard::KEY_CIRCUMFLEX : gameplay::Keyboard::KEY_SIX;
    case 0x37:
        return shiftDown ? gameplay::Keyboard::KEY_AMPERSAND : gameplay::Keyboard::KEY_SEVEN;
    case 0x38:
        return shiftDown ? gameplay::Keyboard::KEY_ASTERISK : gameplay::Keyboard::KEY_EIGHT;
    case 0x39:
        return shiftDown ? gameplay::Keyboard::KEY_LEFT_PARENTHESIS : gameplay::Keyboard::KEY_NINE;
    case VK_OEM_PLUS:
        return shiftDown ? gameplay::Keyboard::KEY_EQUAL : gameplay::Keyboard::KEY_PLUS;
    case VK_OEM_COMMA:
        return shiftDown ? gameplay::Keyboard::KEY_LESS_THAN : gameplay::Keyboard::KEY_COMMA;
    case VK_OEM_MINUS:
        return shiftDown ? gameplay::Keyboard::KEY_UNDERSCORE : gameplay::Keyboard::KEY_MINUS;
    case VK_OEM_PERIOD:
        return shiftDown ? gameplay::Keyboard::KEY_GREATER_THAN : gameplay::Keyboard::KEY_PERIOD;
    case VK_OEM_1:
        return shiftDown ? gameplay::Keyboard::KEY_COLON : gameplay::Keyboard::KEY_SEMICOLON;
    case VK_OEM_2:
        return shiftDown ? gameplay::Keyboard::KEY_QUESTION : gameplay::Keyboard::KEY_SLASH;
    case VK_OEM_3:
        return shiftDown ? gameplay::Keyboard::KEY_TILDE : gameplay::Keyboard::KEY_GRAVE;
    case VK_OEM_4:
        return shiftDown ? gameplay::Keyboard::KEY_LEFT_BRACE : gameplay::Keyboard::KEY_LEFT_BRACKET;
    case VK_OEM_5:
        return shiftDown ? gameplay::Keyboard::KEY_BAR : gameplay::Keyboard::KEY_BACK_SLASH;
    case VK_OEM_6:
        return shiftDown ? gameplay::Keyboard::KEY_RIGHT_BRACE : gameplay::Keyboard::KEY_RIGHT_BRACKET;
    case VK_OEM_7:
        return shiftDown ? gameplay::Keyboard::KEY_QUOTE : gameplay::Keyboard::KEY_APOSTROPHE;
    case 0x41:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_A : gameplay::Keyboard::KEY_A;
    case 0x42:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_B : gameplay::Keyboard::KEY_B;
    case 0x43:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_C : gameplay::Keyboard::KEY_C;
    case 0x44:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_D : gameplay::Keyboard::KEY_D;
    case 0x45:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_E : gameplay::Keyboard::KEY_E;
    case 0x46:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_F : gameplay::Keyboard::KEY_F;
    case 0x47:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_G : gameplay::Keyboard::KEY_G;
    case 0x48:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_H : gameplay::Keyboard::KEY_H;
    case 0x49:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_I : gameplay::Keyboard::KEY_I;
    case 0x4A:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_J : gameplay::Keyboard::KEY_J;
    case 0x4B:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_K : gameplay::Keyboard::KEY_K;
    case 0x4C:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_L : gameplay::Keyboard::KEY_L;
    case 0x4D:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_M : gameplay::Keyboard::KEY_M;
    case 0x4E:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_N : gameplay::Keyboard::KEY_N;
    case 0x4F:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_O : gameplay::Keyboard::KEY_O;
    case 0x50:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_P : gameplay::Keyboard::KEY_P;
    case 0x51:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_Q : gameplay::Keyboard::KEY_Q;
    case 0x52:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_R : gameplay::Keyboard::KEY_R;
    case 0x53:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_S : gameplay::Keyboard::KEY_S;
    case 0x54:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_T : gameplay::Keyboard::KEY_T;
    case 0x55:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_U : gameplay::Keyboard::KEY_U;
    case 0x56:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_V : gameplay::Keyboard::KEY_V;
    case 0x57:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_W : gameplay::Keyboard::KEY_W;
    case 0x58:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_X : gameplay::Keyboard::KEY_X;
    case 0x59:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_Y : gameplay::Keyboard::KEY_Y;
    case 0x5A:
        return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_Z : gameplay::Keyboard::KEY_Z;
    default:
        return gameplay::Keyboard::KEY_NONE;
    }
}

static void UpdateCapture(LPARAM lParam)
{
    if ((lParam & MK_LBUTTON) || (lParam & MK_MBUTTON) || (lParam & MK_RBUTTON))
        SetCapture(__hwnd);
    else
        ReleaseCapture();
}

static void WarpMouse(int clientX, int clientY)
{
    POINT p = { clientX, clientY };
    ClientToScreen(__hwnd, &p);
    SetCursorPos(p.x, p.y);
}


/**
 * Gets the width and height of the screen in pixels.
 */
static void getDesktopResolution(int& width, int& height)
{
   RECT desktop;
   const HWND hDesktop = GetDesktopWindow();
   // Get the size of screen to the variable desktop
   GetWindowRect(hDesktop, &desktop);
   width = desktop.right;
   height = desktop.bottom;
}

LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    static gameplay::Game* game = gameplay::Game::getInstance();

    if (!game->isInitialized() || hwnd != __hwnd)
    {
        // Ignore messages that are not for our game window.
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }

    static bool shiftDown = false;
    static bool capsOn = false;

    switch (msg)
    {
    case WM_CLOSE:
#ifdef GP_USE_MEM_LEAK_DETECTION
		DestroyWindow(__hwnd);
#else
        exit(0);
#endif
        return 0;

    case WM_DESTROY:
        gameplay::Platform::shutdownInternal();
        PostQuitMessage(0);
        return 0;

    case WM_LBUTTONDOWN:
    {
        int x = GET_X_LPARAM(lParam);
        int y = GET_Y_LPARAM(lParam);

        UpdateCapture(wParam);
        if (!gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_PRESS_LEFT_BUTTON, x, y, 0))
        {
            gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_PRESS, x, y, 0, true);
        }
        return 0;
    }
    case WM_LBUTTONUP:
    {
        int x = GET_X_LPARAM(lParam);
        int y = GET_Y_LPARAM(lParam);

        if (!gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_RELEASE_LEFT_BUTTON, x, y, 0))
        {
            gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_RELEASE, x, y, 0, true);
        }
        UpdateCapture(wParam);
        return 0;
    }
    case WM_RBUTTONDOWN:
        UpdateCapture(wParam);
        gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_PRESS_RIGHT_BUTTON, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
        break;

    case WM_RBUTTONUP:
        gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_RELEASE_RIGHT_BUTTON, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
        UpdateCapture(wParam);
        break;

    case WM_MBUTTONDOWN:
        UpdateCapture(wParam);
        gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_PRESS_MIDDLE_BUTTON, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
        break;

    case WM_MBUTTONUP:
        gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_RELEASE_MIDDLE_BUTTON, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
        UpdateCapture(wParam);
        break;

    case WM_MOUSEMOVE:
    {
        int x = GET_X_LPARAM(lParam);
        int y = GET_Y_LPARAM(lParam);

        if (__mouseCaptured)
        {
            // If the incoming position is the mouse capture point, ignore this event
            // since this is the event that warped the cursor back.
            if (x == __mouseCapturePoint.x && y == __mouseCapturePoint.y)
                break;

            // Convert to deltas
            x -= __mouseCapturePoint.x;
            y -= __mouseCapturePoint.y;

            // Warp mouse back to center of screen.
            WarpMouse(__mouseCapturePoint.x, __mouseCapturePoint.y);
        }

        // Allow Game::mouseEvent a chance to handle (and possibly consume) the event.
        if (!gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_MOVE, x, y, 0))
        {
            if ((wParam & MK_LBUTTON) == MK_LBUTTON)
            {
                // Mouse move events should be interpreted as touch move only if left mouse is held and the game did not consume the mouse event.
                gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_MOVE, x, y, 0, true);
                return 0;
            }
        }
        break;
    }

    case WM_MOUSEWHEEL:
        tagPOINT point;
        point.x = GET_X_LPARAM(lParam);
        point.y = GET_Y_LPARAM(lParam);
        ScreenToClient(__hwnd, &point);
        gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_WHEEL, point.x, point.y, GET_WHEEL_DELTA_WPARAM(wParam) / 120);
        break;

    case WM_KEYDOWN:
        if (wParam == VK_SHIFT || wParam == VK_LSHIFT || wParam == VK_RSHIFT)
            shiftDown = true;

        if (wParam == VK_CAPITAL)
            capsOn = !capsOn;

        gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_PRESS, getKey(wParam, shiftDown ^ capsOn));
        break;
        
    case WM_KEYUP:
        if (wParam == VK_SHIFT || wParam == VK_LSHIFT || wParam == VK_RSHIFT)
            shiftDown = false;

        gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_RELEASE, getKey(wParam, shiftDown ^ capsOn));
        break;

    case WM_CHAR:
        gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_CHAR, wParam);
        break;

    case WM_UNICHAR:
        gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_CHAR, wParam);
        break;

    case WM_SETFOCUS:
        break;

    case WM_KILLFOCUS:
        break;

    case WM_SIZE:
        // Window was resized.
        gameplay::Platform::resizeEventInternal((unsigned int)(short)LOWORD(lParam), (unsigned int)(short)HIWORD(lParam));
        break;
    }
    
    return DefWindowProc(hwnd, msg, wParam, lParam); 
}


namespace gameplay
{

struct WindowCreationParams
{
    RECT rect;
    std::string windowName;
    bool fullscreen;
    bool resizable;
    int samples;
};


extern void print(const char* format, ...)
{
    va_list argptr;
    va_start(argptr, format);
    int sz = vfprintf(stderr, format, argptr);
    if (sz > 0)
    {
        char* buf = new char[sz + 1];
        vsprintf(buf, format, argptr);
        buf[sz] = 0;
		gameplay::Logger::log(gameplay::Logger::LEVEL_INFO, "%s\n", buf);
		//OutputDebugStringA(buf);
        SAFE_DELETE_ARRAY(buf);
    }
    va_end(argptr);
}

extern int strcmpnocase(const char* s1, const char* s2)
{
    return _strcmpi(s1, s2);
}

Platform::Platform(Game* game)
    : _game(game)
{
}

Platform::~Platform()
{
    if (__hwnd)
    {
#ifndef GP_USE_ANGLE
		// 後処理
		// カレントコンテキストを無効にする
		wglMakeCurrent(NULL, NULL);
		// カレントコンテキストを削除
		wglDeleteContext(__hrc);
#endif

		//DestroyWindow(__hwnd);
        __hwnd = 0;
    }
}

bool createWindow(WindowCreationParams* params, HWND* hwnd, HDC* hdc)
{
    bool fullscreen = false;
    bool resizable = false;
    RECT rect = { CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT };
    std::string windowName;
    if (params)
    {
        windowName = params->windowName;
        memcpy(&rect, &params->rect, sizeof(RECT));
        fullscreen = params->fullscreen;
        resizable = params->resizable;
    }

    // Set the window style.
    DWORD style, styleEx;
    if (fullscreen)
    {
        style = WS_POPUP;
        styleEx = WS_EX_APPWINDOW;
    }
    else
    {
        if (resizable)
            style = WS_OVERLAPPEDWINDOW;
        else
            style = WS_POPUP | WS_BORDER | WS_CAPTION | WS_SYSMENU;
        styleEx = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
    }
    style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS;

    // Adjust the window rectangle so the client size is the requested size.
    AdjustWindowRectEx(&rect, style, FALSE, styleEx);

    // Create the native Windows window.
    *hwnd = CreateWindowEx(styleEx, "HSP3DishWindow", windowName.c_str(), style, 0, 0, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, __hinstance, NULL);
    if (*hwnd == NULL)
    {
        GP_ERROR("Failed to create window.");
        return false;
    }

    // Get the drawing context.
    *hdc = GetDC(*hwnd);
    if (*hdc == NULL)
    {
        GP_ERROR("Failed to get device context.");
        return false;
    }

    // Center the window
    GetWindowRect(*hwnd, &rect);
    const int screenX = (GetSystemMetrics(SM_CXSCREEN) - rect.right) / 2;
    const int screenY = (GetSystemMetrics(SM_CYSCREEN) - rect.bottom) / 2;
    SetWindowPos(*hwnd, *hwnd, screenX, screenY, -1, -1, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);

    return true;
}

#if defined(GP_USE_ANGLE)
bool initializeGL(WindowCreationParams* params)
{
	// Create a temporary window and context to we can initialize GLEW and get access
	// to additional OpenGL extension functions. This is a neccessary evil since the
	// function for querying GL extensions is a GL extension itself.
	HWND hwnd = __hwnd;
	HDC hdc = __hdc;

	PIXELFORMATDESCRIPTOR pfd;
	memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
	pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
	pfd.nVersion = 1;
	pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW;
	pfd.iPixelType = PFD_TYPE_RGBA;
	pfd.cColorBits = DEFAULT_COLOR_BUFFER_SIZE;
	pfd.cDepthBits = DEFAULT_DEPTH_BUFFER_SIZE;
	pfd.cStencilBits = DEFAULT_STENCIL_BUFFER_SIZE;
	pfd.iLayerType = PFD_MAIN_PLANE;

	int pixelFormat = ChoosePixelFormat(hdc, &pfd);
	if (pixelFormat == 0)
	{
		//DestroyWindow(hwnd);
		GP_ERROR("Failed to choose a pixel format.");
		return false;
	}

	if (!SetPixelFormat(hdc, pixelFormat, &pfd))
	{
		//DestroyWindow(hwnd);
		GP_ERROR("Failed to set the pixel format.");
		return false;
	}

	// Hard-coded to 32-bit/OpenGL ES 2.0.
	// NOTE: EGL_SAMPLE_BUFFERS, EGL_SAMPLES and EGL_DEPTH_SIZE MUST remain at the beginning of the attribute list
	// since they are expected to be at indices 0-5 in config fallback code later.
	// EGL_DEPTH_SIZE is also expected to
	EGLint eglConfigAttrs[] =
	{
		EGL_SAMPLE_BUFFERS, params ? (params->samples > 0 ? 1 : 0) : 0,
		EGL_SAMPLES, params ? params->samples : 0,
		EGL_DEPTH_SIZE, 24,
		EGL_RED_SIZE, 8,
		EGL_GREEN_SIZE, 8,
		EGL_BLUE_SIZE, 8,
		EGL_ALPHA_SIZE, 8,
		EGL_STENCIL_SIZE, 8,
		EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
		EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
		EGL_NONE
	};
	__multiSampling = params && params->samples > 0;

	EGLint eglConfigCount;
	const EGLint eglContextAttrs[] =
	{
		EGL_CONTEXT_CLIENT_VERSION, 2,
		EGL_NONE
	};

	const EGLint eglSurfaceAttrs[] =
	{
		EGL_RENDER_BUFFER, EGL_BACK_BUFFER,
		EGL_NONE
	};

	if (__eglDisplay == EGL_NO_DISPLAY && __eglContext == EGL_NO_CONTEXT)
	{
		// Get the EGL display and initialize.
		__eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
		if (__eglDisplay == EGL_NO_DISPLAY)
		{
			DestroyWindow(hwnd);
			GP_ERROR("eglGetDisplay");
			return false;
		}

		if (eglInitialize(__eglDisplay, NULL, NULL) != EGL_TRUE)
		{
			DestroyWindow(hwnd);
			GP_ERROR("eglInitialize");
			return false;
		}

		// Try both 24 and 16-bit depth sizes since some hardware (i.e. Tegra) does not support 24-bit depth
		bool validConfig = false;
		EGLint depthSizes[] = { 24, 16 };
		for (unsigned int i = 0; i < 2; ++i)
		{
			eglConfigAttrs[1] = params ? (params->samples > 0 ? 1 : 0) : 0;
			eglConfigAttrs[3] = params ? params->samples : 0;
			eglConfigAttrs[5] = depthSizes[i];

			if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) == EGL_TRUE && eglConfigCount > 0)
			{
				validConfig = true;
				break;
			}

			if (params && params->samples > 0)
			{
				// Try lowering the MSAA sample size until we find a  config
				int sampleCount = params->samples;
				while (sampleCount)
				{
					GP_WARN("No EGL config found for depth_size=%d and samples=%d. Trying samples=%d instead.", depthSizes[i], sampleCount, sampleCount / 2);
					sampleCount /= 2;
					eglConfigAttrs[1] = sampleCount > 0 ? 1 : 0;
					eglConfigAttrs[3] = sampleCount;
					if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) == EGL_TRUE && eglConfigCount > 0)
					{
						validConfig = true;
						break;
					}
				}

				__multiSampling = sampleCount > 0;

				if (validConfig)
					break;
			}
			else
			{
				GP_WARN("No EGL config found for depth_size=%d.", depthSizes[i]);
			}
		}

		if (!validConfig)
		{
			DestroyWindow(hwnd);
			GP_ERROR("eglChooseConfig");
			return false;
		}

		__eglContext = eglCreateContext(__eglDisplay, __eglConfig, EGL_NO_CONTEXT, eglContextAttrs);
		if (__eglContext == EGL_NO_CONTEXT)
		{
			DestroyWindow(hwnd);
			GP_ERROR("eglCreateContext");
			return false;
		}
	}

	__eglSurface = eglCreateWindowSurface(__eglDisplay, __eglConfig, hwnd, eglSurfaceAttrs);
	if (__eglSurface == EGL_NO_SURFACE)
	{
		DestroyWindow(hwnd);
		GP_ERROR("eglCreateWindowSurface");
		return false;
	}

	if (eglMakeCurrent(__eglDisplay, __eglSurface, __eglSurface, __eglContext) != EGL_TRUE)
	{
		DestroyWindow(hwnd);
		GP_ERROR("eglMakeCurrent");
		return false;
	}

	__hwnd = hwnd;
	__hdc = hdc;

	// Set vsync.
	eglSwapInterval(__eglDisplay, WINDOW_VSYNC ? 1 : 0);

	// Initialize OpenGL ES extensions.
	__glExtensions = (const char*)glGetString(GL_EXTENSIONS);

	if (strstr(__glExtensions, "GL_OES_vertex_array_object") || strstr(__glExtensions, "GL_ARB_vertex_array_object"))
	{
		// Disable VAO extension for now.
		glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)eglGetProcAddress("glBindVertexArrayOES");
		glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)eglGetProcAddress("glDeleteVertexArraysOES");
		glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)eglGetProcAddress("glGenVertexArraysOES");
		glIsVertexArray = (PFNGLISVERTEXARRAYOESPROC)eglGetProcAddress("glIsVertexArrayOES");
	}
	return true;
}
#else

bool initializeGL(WindowCreationParams* params)
{
    // Create a temporary window and context to we can initialize GLEW and get access
    // to additional OpenGL extension functions. This is a neccessary evil since the
    // function for querying GL extensions is a GL extension itself.
    HWND hwnd = NULL;
    HDC hdc = NULL;

	if (!createWindow(params, &hwnd, &hdc))
		return false;
	
    PIXELFORMATDESCRIPTOR pfd;
    memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
    pfd.nSize  = sizeof(PIXELFORMATDESCRIPTOR);
    pfd.nVersion   = 1;
    pfd.dwFlags    = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = DEFAULT_COLOR_BUFFER_SIZE;
    pfd.cDepthBits = DEFAULT_DEPTH_BUFFER_SIZE;
    pfd.cStencilBits = DEFAULT_STENCIL_BUFFER_SIZE;
    pfd.iLayerType = PFD_MAIN_PLANE;

    int pixelFormat = ChoosePixelFormat(hdc, &pfd);
    if (pixelFormat == 0)
    {
        //DestroyWindow(hwnd);
        GP_ERROR("Failed to choose a pixel format.");
        return false;
    }

    if (!SetPixelFormat(hdc, pixelFormat, &pfd))
    {
        //DestroyWindow(hwnd);
        GP_ERROR("Failed to set the pixel format.");
        return false;
    }

    HGLRC tempContext = wglCreateContext(hdc);
    if (!tempContext)
    {
        //DestroyWindow(hwnd);
        GP_ERROR("Failed to create temporary context for initialization.");
        return false;
    }
	wglMakeCurrent(hdc, tempContext);

    // Initialize GLEW
    if (GLEW_OK != glewInit())
    {
        wglDeleteContext(tempContext);
        DestroyWindow(hwnd);
        GP_ERROR("Failed to initialize GLEW.");
        return false;
    }

	if( wglChoosePixelFormatARB && wglCreateContextAttribsARB )
    {
    // Choose pixel format using wglChoosePixelFormatARB, which allows us to specify
    // additional attributes such as multisampling.
    //
    // Note: Keep multisampling attributes at the start of the attribute lists since code below
    // assumes they are array elements 0 through 3.
    int attribList[] = {
        WGL_SAMPLES_ARB, params ? params->samples : 0,
        WGL_SAMPLE_BUFFERS_ARB, params ? (params->samples > 0 ? 1 : 0) : 0,
        WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
        WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
        WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
        WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
        WGL_COLOR_BITS_ARB, DEFAULT_COLOR_BUFFER_SIZE,
        WGL_DEPTH_BITS_ARB, DEFAULT_DEPTH_BUFFER_SIZE,
        WGL_STENCIL_BITS_ARB, DEFAULT_STENCIL_BUFFER_SIZE,
        0
    };
    __multiSampling = params && params->samples > 0;

    UINT numFormats;
    if ( !wglChoosePixelFormatARB(hdc, attribList, NULL, 1, &pixelFormat, &numFormats) || numFormats == 0)
    {
        bool valid = false;
        if (params && params->samples > 0)
        {
            GP_WARN("Failed to choose pixel format with WGL_SAMPLES_ARB == %d. Attempting to fallback to lower samples setting.", params->samples);
            while (params->samples > 0)
            {
                params->samples /= 2;
                attribList[1] = params->samples;
                attribList[3] = params->samples > 0 ? 1 : 0;
                if (wglChoosePixelFormatARB(hdc, attribList, NULL, 1, &pixelFormat, &numFormats) && numFormats > 0)
                {
                    valid = true;
                    GP_WARN("Found pixel format with WGL_SAMPLES_ARB == %d.", params->samples);
                    break;
                }
            }

            __multiSampling = params->samples > 0;
        }

        if (!valid)
        {
            wglDeleteContext(tempContext);
            DestroyWindow(hwnd);
            GP_ERROR("Failed to choose a pixel format.");
            return false;
        }
    }

	DestroyWindow(hwnd);

	// Create new/final window if needed
    if (params)
    {
        hwnd = NULL;
        hdc = NULL;

        if (!createWindow(params, &__hwnd, &__hdc))
        {
            wglDeleteContext(tempContext);
            return false;
        }
	}
	else {
		hwnd = __hwnd;
		hdc = __hdc;
	}

    // Set final pixel format for window
    if (!SetPixelFormat(__hdc, pixelFormat, &pfd))
    {
        GP_ERROR("Failed to set the pixel format: %d.", (int)GetLastError());
        return false;
    }

    // Create our new GL context
    int attribs[] =
    {
        WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
        WGL_CONTEXT_MINOR_VERSION_ARB, 1,
        0
    };

    if (!(__hrc = wglCreateContextAttribsARB(__hdc, 0, attribs) ) )
    {
        wglDeleteContext(tempContext);
        GP_ERROR("Failed to create OpenGL context.");
        return false;
    }

    // Delete the old/temporary context and window
    wglDeleteContext(tempContext);

    // Make the new context current
    if (!wglMakeCurrent(__hdc, __hrc) || !__hrc)
    {
        GP_ERROR("Failed to make the window current.");
        return false;
    }
    } else    // fallback to OpenGL 2.0 if wglChoosePixelFormatARB or wglCreateContextAttribsARB is NULL.
    {
        // Context is already here, just use it.
        __hrc = tempContext;
        __hwnd = hwnd;
        __hdc = hdc;
    }

    // Vertical sync.
    if (wglSwapIntervalEXT) 
        wglSwapIntervalEXT(__vsync ? 1 : 0);
    else 
        __vsync = false;

    // Some old graphics cards support EXT_framebuffer_object instead of ARB_framebuffer_object.
    // Patch ARB_framebuffer_object functions to EXT_framebuffer_object ones since semantic is same.
    if( !GLEW_ARB_framebuffer_object && GLEW_EXT_framebuffer_object )
    {
        glBindFramebuffer = glBindFramebufferEXT;
        glBindRenderbuffer = glBindRenderbufferEXT;
        glBlitFramebuffer = glBlitFramebufferEXT;
        glCheckFramebufferStatus = glCheckFramebufferStatusEXT;
        glDeleteFramebuffers = glDeleteFramebuffersEXT;
        glDeleteRenderbuffers = glDeleteRenderbuffersEXT;
        glFramebufferRenderbuffer = glFramebufferRenderbufferEXT;
        glFramebufferTexture1D = glFramebufferTexture1DEXT;
        glFramebufferTexture2D = glFramebufferTexture2DEXT;
        glFramebufferTexture3D = glFramebufferTexture3DEXT;
        glFramebufferTextureLayer = glFramebufferTextureLayerEXT;
        glGenFramebuffers = glGenFramebuffersEXT;
        glGenRenderbuffers = glGenRenderbuffersEXT;
        glGenerateMipmap = glGenerateMipmapEXT;
        glGetFramebufferAttachmentParameteriv = glGetFramebufferAttachmentParameterivEXT;
        glGetRenderbufferParameteriv = glGetRenderbufferParameterivEXT;
        glIsFramebuffer = glIsFramebufferEXT;
        glIsRenderbuffer = glIsRenderbufferEXT;
        glRenderbufferStorage = glRenderbufferStorageEXT;
        glRenderbufferStorageMultisample = glRenderbufferStorageMultisampleEXT;
    }
    return true;
}
#endif


Platform* Platform::create(Game* game, void* attachToWindow, int sizex, int sizey, bool fullscreen )
{
    GP_ASSERT(game);

    FileSystem::setResourcePath("./");
    Platform* platform = new Platform(game);

    // Get the application module handle.
    __hinstance = ::GetModuleHandle(NULL);
    __attachToWindow = (HWND)attachToWindow;

    // Read window settings from config.
    WindowCreationParams params;
    params.fullscreen = fullscreen; // false;
    params.resizable = false;
    params.rect.left = 0;
    params.rect.top = 0;
    params.rect.right = sizex;
    params.rect.bottom = sizey;
    params.samples = 0;

	/*
    if (game->getConfig())
    {
        Properties* config = game->getConfig()->getNamespace("window", true);
        if (config)
        {
            // Read window title.
            const char* title = config->getString("title");
            if (title)
            {
                int len = MultiByteToWideChar(CP_ACP, 0, title, -1, NULL, 0);
                wchar_t* wtitle = new wchar_t[len];
                MultiByteToWideChar(CP_ACP, 0, title, -1, wtitle, len);
                params.windowName = wtitle;
                SAFE_DELETE_ARRAY(wtitle);
            }

            // Read fullscreen state.
            params.fullscreen = config->getBool("fullscreen");
            // Read resizable state.
            params.resizable = config->getBool("resizable");
            // Read multisampling state.
            params.samples = config->getInt("samples");

            // Read window rect.
            int x = config->getInt("x");
            if (x != 0)
                params.rect.left = x;
            int y = config->getInt("y");
            if (y != 0)
                params.rect.top = y;
            int width = config->getInt("width");
            int height = config->getInt("height");

            if (width == 0 && height == 0 && params.fullscreen)
                getDesktopResolution(width, height);

            if (width != 0)
                params.rect.right = params.rect.left + width;
            if (height != 0)
                params.rect.bottom = params.rect.top + height;
        }
    }
	*/

    // If window size was not specified, set it to a default value
    if (params.rect.right == 0)
        params.rect.right = params.rect.left + DEFAULT_RESOLUTION_X;
    if (params.rect.bottom == 0)
        params.rect.bottom = params.rect.top + DEFAULT_RESOLUTION_Y;
    int width = params.rect.right - params.rect.left;
    int height = params.rect.bottom - params.rect.top;

    if (params.fullscreen)
    {
        // Enumerate all supposed display settings
        bool modeSupported = false;
        DWORD modeNum = 0;
        DEVMODE devMode;
        memset(&devMode, 0, sizeof(DEVMODE));
        devMode.dmSize = sizeof(DEVMODE);
        devMode.dmDriverExtra = 0;
        while (EnumDisplaySettings(NULL, modeNum++, &devMode) != 0)
        {
            // Is mode supported?
            if (devMode.dmPelsWidth == width &&
                devMode.dmPelsHeight == height &&
                devMode.dmBitsPerPel == DEFAULT_COLOR_BUFFER_SIZE)
            {
                modeSupported = true;
                break;
            }
        }

        // If the requested mode is not supported, fall back to a safe default
        if (!modeSupported)
        {
            width = DEFAULT_RESOLUTION_X;
            height = DEFAULT_RESOLUTION_Y;
            params.rect.right = params.rect.left + width;
            params.rect.bottom = params.rect.top + height;
        }
    }


    if (!__attachToWindow)
    {
#if 0
		// Register our window class.
		WNDCLASSEX wc;
		wc.cbSize = sizeof(WNDCLASSEX);
		wc.style          = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
		wc.lpfnWndProc    = (WNDPROC)__WndProc;
		wc.cbClsExtra     = 0;
		wc.cbWndExtra     = 0;
		wc.hInstance      = __hinstance;
		wc.hIcon          = LoadIcon(NULL, IDI_APPLICATION);
		wc.hIconSm        = NULL;
		wc.hCursor        = LoadCursor(NULL, IDC_ARROW);
		wc.hbrBackground  = NULL;  // No brush - we are going to paint our own background
		wc.lpszMenuName   = NULL;  // No default menu
		wc.lpszClassName  = "gameplay";

		if (!::RegisterClassEx(&wc))
		{
			GP_ERROR("Failed to register window class.");
			goto error;
		}
#endif
		if (params.fullscreen)
		{
			DEVMODE dm;
			memset(&dm, 0, sizeof(dm));
			dm.dmSize= sizeof(dm);
			dm.dmPelsWidth  = width;
			dm.dmPelsHeight = height;
			dm.dmBitsPerPel = DEFAULT_COLOR_BUFFER_SIZE;
			dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

			// Try to set selected mode and get results. NOTE: CDS_FULLSCREEN gets rid of start bar.
			if (ChangeDisplaySettings(&dm, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
			{
				params.fullscreen = false;
				GP_ERROR("Failed to start game in full-screen mode with resolution %dx%d.", width, height);
				goto error;
			}
		}

		if (!initializeGL(&params))
			goto error;

		// Show the window.
		ShowWindow(__hwnd, SW_SHOW);

    }
    else
    {
        // Attach to a previous windows
        __hwnd = (HWND)__attachToWindow;
        __hdc = GetDC(__hwnd);

        //SetWindowLongPtr(__hwnd, GWLP_WNDPROC, (LONG)(WNDPROC)__WndProc);

        if (!initializeGL(NULL))
            goto error;
    }


#ifdef GP_USE_GAMEPAD
    // Initialize XInputGamepads.
    for (DWORD i = 0; i < XUSER_MAX_COUNT; i++)
    {
        if (XInputGetState(i, &__xInputState) == NO_ERROR)
        {
            if (!__connectedXInput[i])
            {
                // Gamepad is connected.
                Platform::gamepadEventConnectedInternal(i, XINPUT_BUTTON_COUNT, XINPUT_JOYSTICK_COUNT, XINPUT_TRIGGER_COUNT, "Microsoft XBox360 Controller");
                __connectedXInput[i] = true;
            }
        }
    }
#endif

    return platform;

error:

    //exit(0);
    return NULL;
}

int Platform::enterMessagePump()
{
    GP_ASSERT(_game);

    // Get the initial time.
    LARGE_INTEGER tps;
    QueryPerformanceFrequency(&tps);
    __timeTicksPerMillis = (double)(tps.QuadPart / 1000L);
    LARGE_INTEGER queryTime;
    QueryPerformanceCounter(&queryTime);
    GP_ASSERT(__timeTicksPerMillis);
    __timeStart = queryTime.QuadPart / __timeTicksPerMillis;

#if defined(GP_USE_ANGLE)
    eglSwapBuffers(__eglDisplay, __eglSurface);
#else
    SwapBuffers(__hdc);
#endif

    if (_game->getState() != Game::RUNNING)
        _game->run();

    if (__attachToWindow)
        return 0;

    // Enter event dispatch loop.
    MSG msg;
    while (true)
    {
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);

            if (msg.message == WM_QUIT)
            {
                gameplay::Platform::shutdownInternal();
                return msg.wParam;
            }
        }
        else
        {
#ifdef GP_USE_GAMEPAD
            // Check for connected XInput gamepads.
            for (DWORD i = 0; i < XUSER_MAX_COUNT; i++)
            {
                if (XInputGetState(i, &__xInputState) == NO_ERROR && !__connectedXInput[i])
                {
                    // Gamepad was just connected.
                    Platform::gamepadEventConnectedInternal(i, XINPUT_BUTTON_COUNT, XINPUT_JOYSTICK_COUNT, XINPUT_TRIGGER_COUNT, "Microsoft XBox360 Controller");
                    __connectedXInput[i] = true;
                }
                else if (XInputGetState(i, &__xInputState) != NO_ERROR && __connectedXInput[i])
                {
                    // Gamepad was just disconnected.
                    __connectedXInput[i] = false;
                    Platform::gamepadEventDisconnectedInternal(i);
                }
            }
#endif
            _game->frame();
#if defined(GP_USE_ANGLE)
            eglSwapBuffers(__eglDisplay, __eglSurface);
#else
            SwapBuffers(__hdc);
#endif
        }

        // If we are done, then exit.
        if (_game->getState() == Game::UNINITIALIZED)
            break;
    }
    return 0;
}

void Platform::signalShutdown() 
{
    // nothing to do  
}

bool Platform::canExit()
{
    return true;
}

unsigned int Platform::getDisplayWidth()
{
    static RECT rect;
    GetClientRect(__hwnd, &rect);
    return rect.right;
}

unsigned int Platform::getDisplayHeight()
{
    static RECT rect;
    GetClientRect(__hwnd, &rect);
    return rect.bottom;
}

double Platform::getAbsoluteTime()
{
    LARGE_INTEGER queryTime;
    QueryPerformanceCounter(&queryTime);
    GP_ASSERT(__timeTicksPerMillis);
    __timeAbsolute = queryTime.QuadPart / __timeTicksPerMillis;

    return __timeAbsolute - __timeStart;
}

void Platform::setAbsoluteTime(double time)
{
    __timeAbsolute = time;
}

bool Platform::isVsync()
{
    return __vsync;
}

void Platform::setVsync(bool enable)
{
    __vsync = enable;

#if defined(GP_USE_ANGLE)
    eglSwapInterval(__eglDisplay, __vsync ? 1 : 0);
#else
    if (wglSwapIntervalEXT) 
        wglSwapIntervalEXT(__vsync ? 1 : 0);
    else 
        __vsync = false;
#endif
}

void Platform::swapBuffers()
{
#if defined(GP_USE_ANGLE)
    eglSwapBuffers(__eglDisplay, __eglSurface);
#else
    if (__hdc)
        SwapBuffers(__hdc);
#endif
}

void Platform::sleep(long ms)
{
    Sleep(ms);
}

void Platform::setMultiSampling(bool enabled)
{
    if (enabled == __multiSampling)
    {
        return;
    }
#if !defined(GP_USE_ANGLE)
    if (enabled)
    {
        glEnable(GL_MULTISAMPLE);
    }
    else
    {
        glDisable(GL_MULTISAMPLE);
    }
#endif

    __multiSampling = enabled;
}

bool Platform::isMultiSampling()
{
    return __multiSampling;
}

void Platform::setMultiTouch(bool enabled)
{
    // not supported
}

bool Platform::isMultiTouch()
{
    return false;
}

bool Platform::hasAccelerometer()
{
    return false;
}

void Platform::getAccelerometerValues(float* pitch, float* roll)
{
    GP_ASSERT(pitch);
    GP_ASSERT(roll);

    *pitch = 0;
    *roll = 0;
}

void Platform::getSensorValues(float* accelX, float* accelY, float* accelZ, float* gyroX, float* gyroY, float* gyroZ)
{
    if (accelX)
    {
        *accelX = 0;
    }

    if (accelY)
    {
        *accelY = 0;
    }

    if (accelZ)
    {
        *accelZ = 0;
    }

    if (gyroX)
    {
        *gyroX = 0;
    }

    if (gyroY)
    {
        *gyroY = 0;
    }

    if (gyroZ)
    {
        *gyroZ = 0;
    }
}

void Platform::getArguments(int* argc, char*** argv)
{
    if (argc)
        *argc = __argc;
    if (argv)
        *argv = __argv;
}

bool Platform::hasMouse()
{
    return true;
}

void Platform::setMouseCaptured(bool captured)
{
    if (captured != __mouseCaptured)
    {
        if (captured)
        {
            // Hide the cursor and warp it to the center of the screen
            __mouseCapturePoint.x = getDisplayWidth() / 2;
            __mouseCapturePoint.y = getDisplayHeight() / 2;

            ShowCursor(FALSE);
            WarpMouse(__mouseCapturePoint.x, __mouseCapturePoint.y);
        }
        else
        {
            // Restore cursor
            WarpMouse(__mouseCapturePoint.x, __mouseCapturePoint.y);
            ShowCursor(TRUE);
        }

        __mouseCaptured = captured;
    }
}

bool Platform::isMouseCaptured()
{
    return __mouseCaptured;
}

void Platform::setCursorVisible(bool visible)
{
    if (visible != __cursorVisible)
    {
        ShowCursor(visible ? TRUE : FALSE);
        __cursorVisible = visible;
    }
}

bool Platform::isCursorVisible()
{
    return __cursorVisible;
}

void Platform::displayKeyboard(bool display)
{
    // Do nothing.
}

bool Platform::isGestureSupported(Gesture::GestureEvent evt)
{
    return false;
}

void Platform::registerGesture(Gesture::GestureEvent evt)
{
}

void Platform::unregisterGesture(Gesture::GestureEvent evt)
{
}
    
bool Platform::isGestureRegistered(Gesture::GestureEvent evt)
{
    return false;
}

#ifdef GP_USE_GAMEPAD
void Platform::pollGamepadState(Gamepad* gamepad)
{
    GP_ASSERT(gamepad->_handle < XUSER_MAX_COUNT);

    if (XInputGetState(gamepad->_handle, &__xInputState) == NO_ERROR)
    {
        WORD buttons = __xInputState.Gamepad.wButtons;

        // Map XInput buttons to Gamepad::ButtonMappings enum.
        static const unsigned int xInputMapping[16] = {
            Gamepad::BUTTON_UP,    // 0x0001
            Gamepad::BUTTON_DOWN,  // 0x0002
            Gamepad::BUTTON_LEFT,  // 0x0004
            Gamepad::BUTTON_RIGHT, // 0x0008
            Gamepad::BUTTON_MENU2, // 0x0010
            Gamepad::BUTTON_MENU1, // 0x0020
            Gamepad::BUTTON_L3,    // 0x0040
            Gamepad::BUTTON_R3,    // 0x0080
            Gamepad::BUTTON_L1,    // 0x0100
            Gamepad::BUTTON_R1,    // 0x0200
            0,
            0,
            Gamepad::BUTTON_A,     // 0x1000
            Gamepad::BUTTON_B,     // 0x2000
            Gamepad::BUTTON_X,     // 0x4000
            Gamepad::BUTTON_Y      // 0x8000
        };

        const unsigned int *mapping = xInputMapping;
        unsigned int mappedButtons;
        for (mappedButtons = 0; buttons; buttons >>= 1, mapping++)
        {
            if (buttons & 1)
            {
                mappedButtons |= (1 << *mapping);
            }
        }
        gamepad->setButtons(mappedButtons);

        unsigned int i;
        for (i = 0; i < gamepad->_joystickCount; ++i)
        {
            GP_ASSERT(i < 2);

            float x;
            float y;
            switch (i)
            {
            case 0:
                x = normalizeXInputJoystickAxis(__xInputState.Gamepad.sThumbLX, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
                y = normalizeXInputJoystickAxis(__xInputState.Gamepad.sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
                break;
            case 1:
                x = normalizeXInputJoystickAxis(__xInputState.Gamepad.sThumbRX, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
                y = normalizeXInputJoystickAxis(__xInputState.Gamepad.sThumbRY, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
                break;
            }

            gamepad->setJoystickValue(i, x, y);
        }

        for (i = 0; i < gamepad->_triggerCount; ++i)
        {
            GP_ASSERT(i < 2);

            BYTE trigger;
            switch (i)
            {
            case 0:
                trigger = __xInputState.Gamepad.bLeftTrigger;
                break;
            case 1:
                trigger = __xInputState.Gamepad.bRightTrigger;
                break;
            }

            if (trigger < XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
            {
                gamepad->setTriggerValue(i, 0.0f);
            }
            else
            {
                gamepad->setTriggerValue(i, (float)trigger / 255.0f);
            }
        }
    }
}
#else
void Platform::pollGamepadState(Gamepad* gamepad) { }
#endif

void Platform::shutdownInternal()
{
    Game::getInstance()->shutdown();
}

bool Platform::launchURL(const char* url)
{
    if (url == NULL || *url == '\0')
        return false;
 
    // Success when result code > 32
    //int len = MultiByteToWideChar(CP_ACP, 0, url, -1, NULL, 0);
    //wchar_t* wurl = new wchar_t[len];
    //MultiByteToWideChar(CP_ACP, 0, url, -1, wurl, len);
    int r = (int)ShellExecute(NULL, NULL, url, NULL, NULL, SW_SHOWNORMAL);
    //SAFE_DELETE_ARRAY(wurl);
    return (r > 32);
}

std::string Platform::displayFileDialog(size_t mode, const char* title, const char* filterDescription, const char* filterExtensions, const char* initialDirectory)
{
    std::string filename;
    OPENFILENAMEA ofn;
    memset(&ofn, 0, sizeof(ofn));

    char currentDir[1024];
    char absPath[1024];
    std::string initialDirectoryStr;
    if (initialDirectory == NULL)
    {
        GetCurrentDirectoryA(1024, currentDir);
        initialDirectoryStr = currentDir;
    }
    else
    {
        GetFullPathNameA(initialDirectory, 1024, absPath, 0);
        initialDirectoryStr = absPath;
    }

    // Filter on extensions
    std::istringstream f(filterExtensions);
    std::string s;
    unsigned int count = 0;
    std::string descStr = filterDescription;
    descStr += " (";
    std::string extStr = "";
    while (std::getline(f, s, ';'))
    {
        if (count > 0)
            extStr += ";";
        extStr += "*.";
        extStr += s;
        count++;
    }
    descStr += extStr;
    descStr += ")";
    char filter[1024];
    memset(filter, 0, 1024);
    strcpy(filter, descStr.c_str());
    strcpy(filter + descStr.length() + 1, extStr.c_str());

    char szFileName[1024] = "";
    ofn.lpstrFile = szFileName;
    ofn.lStructSize = sizeof(ofn);
    ofn.hwndOwner = GetForegroundWindow();
    ofn.lpstrTitle = title;
    ofn.lpstrFilter = filter;
    ofn.lpstrInitialDir = initialDirectoryStr.c_str();
    ofn.nMaxFile = 1024;
    ofn.lpstrDefExt = filter;

    if (mode == FileSystem::OPEN)
    {
        ofn.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST;
        GetOpenFileNameA(&ofn);
    }
    else
    {
        ofn.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
        GetSaveFileNameA(&ofn);
    }

    filename = szFileName;

    return filename;
}

}

#endif
#endif
