#include "..\Engine.h"
#include <MMSystem.h>
#include "..\..\build\VisualStudio\resource.h"

HDC			hDC = NULL;
HGLRC		hRC = NULL;
HWND		hWnd = NULL;
HINSTANCE	hInstance;
MCIDEVICEID musicDev = NULL;

//#define SIZABLE_WINDOW

bool	keys[256];
bool	active = true;
bool	fullscreen = true;

typedef BOOL (WINAPI * PFNWGLCHOOSEPIXELFORMATARBPROC) (HDC hdc, const int* piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);
PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL;

LRESULT	CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

GLvoid ReSizeGLScene(GLsizei width, GLsizei height)	
{
	if (height == 0) height = 1;										

	glViewport(0, 0, width, height);						

	glMatrixMode(GL_PROJECTION);						
	glLoadIdentity();									
	gluPerspective(45.0f, (GLfloat)width/(GLfloat)height, 0.1f, 100.0f);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

void CloseMusic()
{
	MCI_GENERIC_PARMS st_generic_params;
	st_generic_params.dwCallback=(DWORD)hWnd;

	MCIERROR err = mciSendCommand(musicDev,MCI_CLOSE,MCI_WAIT,(DWORD)&st_generic_params);
}

void PlayMusic()
{
	#define	MCI_SETAUDIO			0x0873
	#define	MCI_DGV_SETAUDIO_VOLUME	0x4002
	#define	MCI_DGV_SETAUDIO_ITEM	0x00800000
	#define	MCI_DGV_SETAUDIO_VALUE	0x01000000

	struct MCI_DGV_SETAUDIO_PARMS
	{
		DWORD dwCallback;
		DWORD dwItem;
		DWORD dwValue;
		DWORD dwOver;
		LPCTSTR	lpstrAlgorithm;
		LPCTSTR	lpstrQuality;
	};

	MCI_OPEN_PARMSA st_open_parm;

	st_open_parm.dwCallback			= (DWORD)hWnd;
	st_open_parm.lpstrDeviceType	=  NULL;
	st_open_parm.lpstrElementName	= (LPCSTR)"Data\\music.mp3";
	st_open_parm.lpstrAlias			= (LPCSTR)"";
	st_open_parm.lpstrDeviceType	= (LPCSTR)"";
	st_open_parm.wDeviceID			=  NULL;

	MCIERROR err = mciSendCommandA(0, MCI_OPEN, MCI_OPEN_ELEMENT,(DWORD)&st_open_parm);
	
	if(!err)
	{
		musicDev = st_open_parm.wDeviceID;

		MCI_PLAY_PARMS st_play_parm;
		st_play_parm.dwCallback =(DWORD)hWnd;
		st_play_parm.dwFrom = 0;
	
		mciSendCommand(musicDev, MCI_PLAY, MCI_FROM | MCI_NOTIFY, (DWORD)&st_play_parm);

		MCI_DGV_SETAUDIO_PARMS st_dvg_parms;

		st_dvg_parms.dwCallback		= (DWORD)hWnd;
		st_dvg_parms.dwItem			= MCI_DGV_SETAUDIO_VOLUME;
		st_dvg_parms.dwValue		= 250;
		st_dvg_parms.dwOver			= 0;
		st_dvg_parms.lpstrAlgorithm	= NULL;
		st_dvg_parms.lpstrQuality	= NULL;

		mciSendCommand(musicDev, MCI_SETAUDIO, MCI_NOTIFY | MCI_DGV_SETAUDIO_VALUE | MCI_DGV_SETAUDIO_ITEM, (DWORD)&st_dvg_parms);
	}
}

bool InitGL()
{
	return InitEng() && Init();
}

bool DrawGLScene()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
	return Render();
}

bool ProcessGLScene()
{
	static DWORD tick = GetTickCount();

	if(GetTickCount() - tick >= UPDATE_INTERVAL)
	{
		tick = GetTickCount();
		return Process(keys);
	}
	else
		return true;
}

GLvoid KillGLWindow()
{
	Free();

	if (fullscreen)
	{
		ChangeDisplaySettings(NULL, 0);
		ShowCursor(TRUE);
	}

	if (hRC)
	{
		wglMakeCurrent(NULL, NULL);
		wglDeleteContext(hRC);
		hRC  = NULL;
	}

	if (hDC && !ReleaseDC(hWnd,hDC)) hDC = NULL;

	if (hWnd && !DestroyWindow(hWnd)) hWnd = NULL;

	if (!UnregisterClass("MiniEng", hInstance)) hInstance = NULL;
}
 
bool CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag, int fsaa)
{
	GLuint		PixelFormat = -1;
	WNDCLASS	wc;
	DWORD		dwExStyle;
	DWORD		dwStyle;
	RECT		WindowRect;
	WindowRect.left		= (long)0;
	WindowRect.right	= (long)width;
	WindowRect.top		= (long)0;
	WindowRect.bottom	= (long)height;

	fullscreen = fullscreenflag;

	hInstance			= GetModuleHandle(NULL);
	wc.style			= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wc.lpfnWndProc		= (WNDPROC) WndProc;
	wc.cbClsExtra		= 0;
	wc.cbWndExtra		= 0;
	wc.hInstance		= hInstance;
	wc.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
	wc.hCursor			= LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground	= NULL;
	wc.lpszMenuName		= NULL;
	wc.lpszClassName	= "MiniEng";

	if (!RegisterClass(&wc))
		return false;
	
	if (fullscreen)
	{
		DEVMODE dmScreenSettings;
		memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
		dmScreenSettings.dmSize = sizeof(dmScreenSettings);
		dmScreenSettings.dmPelsWidth	= width;
		dmScreenSettings.dmPelsHeight	= height;
		dmScreenSettings.dmBitsPerPel	= bits;
		dmScreenSettings.dmFields		= DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

		fullscreen = ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL;
	}

	if (fullscreen)
	{
		dwExStyle	= WS_EX_APPWINDOW;
		dwStyle		= WS_POPUP;
		ShowCursor(FALSE);
	}
	else
	{
		dwExStyle	= WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
#ifdef SIZABLE_WINDOW
		dwStyle		= WS_OVERLAPPEDWINDOW;
#else
		dwStyle		= WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
#endif
	}

	AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);

	int desktop_width = 0, desktop_height = 0;
	HDC desktop_dc	= GetDC(GetDesktopWindow());
	desktop_width	= GetDeviceCaps(desktop_dc, HORZRES);
	desktop_height	= GetDeviceCaps(desktop_dc, VERTRES);
	ReleaseDC(GetDesktopWindow(), desktop_dc);

	if (!(hWnd = CreateWindowEx(dwExStyle, "MiniEng", title, dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, (desktop_width-(WindowRect.right-WindowRect.left))/2, (desktop_height - (WindowRect.bottom-WindowRect.top))/2, WindowRect.right-WindowRect.left, WindowRect.bottom-WindowRect.top, NULL, NULL, hInstance, NULL)))
	{
		KillGLWindow();
		return false;
	}

	static PIXELFORMATDESCRIPTOR pfd=
	{
		sizeof(PIXELFORMATDESCRIPTOR),				// Size Of This Pixel Format Descriptor
		1,											// Version Number
		PFD_DRAW_TO_WINDOW |						// Format Must Support Window
		PFD_SUPPORT_OPENGL |						// Format Must Support OpenGL
		PFD_DOUBLEBUFFER,							// Must Support Double Buffering
		PFD_TYPE_RGBA,								// Request An RGBA Format
		bits,										// Select Our Color Depth
		0, 0, 0, 0, 0, 0,							// Color Bits Ignored
		0,											// No Alpha Buffer
		0,											// Shift Bit Ignored
		0,											// No Accumulation Buffer
		0, 0, 0, 0,									// Accumulation Bits Ignored
		16,											// 16Bit Z-Buffer (Depth Buffer)  
		0,											// No Stencil Buffer
		0,											// No Auxiliary Buffer
		PFD_MAIN_PLANE,								// Main Drawing Layer
		0,											// Reserved
		0, 0, 0										// Layer Masks Ignored
	};

	if(fsaa > 1)
	{
		HWND	temp_win_handle	= NULL;
		HDC		temp_win_dc		= NULL;
		HGLRC	temp_win_rc		= NULL;
		int temp_pixel_format	= NULL;

		temp_win_handle = CreateWindowEx(0, "STATIC", NULL, 0, 0, 0, 0, 0, 0, 0, 0, NULL);
		temp_win_dc = GetDC(temp_win_handle);
		temp_pixel_format = ChoosePixelFormat(temp_win_dc, &pfd);
		SetPixelFormat(temp_win_dc, temp_pixel_format, &pfd);
		temp_win_rc = wglCreateContext(temp_win_dc);

		if(temp_win_handle && temp_win_dc && temp_win_rc && wglMakeCurrent(temp_win_dc, temp_win_rc))
		{

			wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");

			if(wglChoosePixelFormatARB)
			{
				int ia_attributes[] =
				{
					0x2001,GL_TRUE,
					0x2010,GL_TRUE,
					0x2003,0x2027,
					0x2014, bits,
					0x201B,8,
					0x2022,24,
					0x2023,0,
					0x2011,GL_TRUE,
					0x2041,GL_TRUE,
					0x2042, fsaa,
					0,0
				};

				float	fa_attributes[] = {0,0};
				UINT	formats_cnt;
				int		tmp_pixel_format;
				
				int valid = wglChoosePixelFormatARB(temp_win_dc, ia_attributes, fa_attributes, 1, &tmp_pixel_format, &formats_cnt);

				if(valid && formats_cnt >= 1)
					PixelFormat = tmp_pixel_format;
				else
					PixelFormat = -1;
			}
		}

		wglMakeCurrent(NULL, NULL);
		if (temp_win_rc != NULL) wglDeleteContext(temp_win_rc);
		if (temp_win_dc != NULL) ReleaseDC(temp_win_handle, temp_win_dc);
		if (temp_win_handle != NULL) DestroyWindow(temp_win_handle);
	}

	if (!(hDC = GetDC(hWnd)))
	{
		KillGLWindow();
		return false;
	}

	if (PixelFormat == -1 && !(PixelFormat = ChoosePixelFormat(hDC, &pfd)))
	{
		KillGLWindow();
		return false;
	}

	if (!SetPixelFormat(hDC, PixelFormat, &pfd))
	{
		KillGLWindow();
		return false;
	}

	if (!(hRC = wglCreateContext(hDC)))
	{
		KillGLWindow();
		return false;
	}

	if(!wglMakeCurrent(hDC, hRC))
	{
		KillGLWindow();
		return false;
	}

	ShowWindow(hWnd, SW_SHOW);
	SetForegroundWindow(hWnd);
	SetFocus(hWnd);
	ReSizeGLScene(width, height);

	ZeroMemory(keys, 256);

	if (!InitGL())
	{
		KillGLWindow();
		return false;
	}

	return true;
}

LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
		case MM_MCINOTIFY:
			if(wParam != MCI_NOTIFY_ABORTED && lParam==musicDev)
			{
				MCI_SEEK_PARMS st_seek_parms;
				mciSendCommand(musicDev, MCI_SEEK, MCI_SEEK_TO_START | MCI_WAIT, (DWORD)&st_seek_parms);
				MCI_PLAY_PARMS st_play_parm;
				st_play_parm.dwCallback = (DWORD)hWnd;
				mciSendCommand(musicDev, MCI_PLAY, MCI_NOTIFY, (DWORD)&st_play_parm);
			}
		break;
		case WM_ACTIVATE:
		{
			active = (!HIWORD(wParam));
			return 0;
		}

		case WM_SYSCOMMAND:
		{
			switch (wParam)
			{
				case SC_SCREENSAVE:
				case SC_MONITORPOWER:
				return 0;
			}
			break;
		}

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

		case WM_KEYDOWN:
		{
			keys[wParam] = true;
			return 0;
		}

		case WM_KEYUP:
		{
			keys[wParam] = false;
			return 0;
		}

		case WM_SIZE:
		{
			ReSizeGLScene(LOWORD(lParam),HIWORD(lParam));
			return 0;
		}
	}

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

int WINAPI WinMain(	HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	MSG		msg;
	bool	done = false;

	fullscreen = MessageBox(NULL, "Would you like to run in FullScreen mode?", "Start FullScreen?", MB_YESNO | MB_ICONQUESTION) == IDYES;

	int resx = SCREEN_RES_X, resy = SCREEN_RES_Y;

	if(fullscreen)
	{
		HDC desktop_dc	= GetDC(GetDesktopWindow());
		resx = GetDeviceCaps(desktop_dc, HORZRES);
		resy = GetDeviceCaps(desktop_dc, VERTRES);
		ReleaseDC(GetDesktopWindow(), desktop_dc);
	}

	if (!CreateGLWindow(GAME_NAME, resx, resy, COLOR_DEPTH, fullscreen, FSAA))
	{
		MessageBox(NULL, "Something went terribly wrong!", "Shit happens...", MB_OK | MB_ICONSTOP);
		return 0;
	}

	while(!done)
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			if (msg.message == WM_QUIT)
				done = true;
			else
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}
		else
		{
			if ((active && (!ProcessGLScene() || !DrawGLScene())) || keys[VK_ESCAPE])
				done = true;
			else
				SwapBuffers(hDC);
		}

	KillGLWindow();

	return (msg.wParam);
}