//+--------------------------------------------------
// Unleeb's OpenGL Tutorials Framework
//		(C) 2006 by Fredrik Åsgård
//+--------------------------------------------------

#include "studio.h"

studio_c::studio_c(char* lpszTitle, int iWidth, int iHeight, int iColorBits, int iDepthBits, int iStencilBits, bool bFullscreen)
{
	WNDCLASS	wc;
	RECT		wr;
	DWORD		dwExStyle;
	DWORD		dwStyle;

	m_hInst				= GetModuleHandle(NULL);
	m_bFullscreen		= bFullscreen;

	wr.left				= (long) 0;
	wr.right			= (long) iWidth;
	wr.top				= (long) 0;
	wr.bottom			= (long) iHeight;
	wc.style			= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wc.lpfnWndProc		= (WNDPROC) WndProc;
	wc.cbClsExtra		= 0;
	wc.cbWndExtra		= 0;
	wc.hInstance		= m_hInst;
	wc.hIcon			= LoadIcon(NULL, IDI_WINLOGO);
	wc.hCursor			= LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground	= NULL;
	wc.lpszMenuName		= NULL;
	wc.lpszClassName	= "glWindow";

	if ( !RegisterClass(&wc) )
	{
		MessageBox(NULL, "Unable to register WC...", "Critical Error", MB_OK | MB_ICONERROR);
	}

	if ( m_bFullscreen )
	{
		DEVMODE		dm;

		memset(&dm, 0, sizeof(dm));

		dm.dmSize		= sizeof(dm);
		dm.dmPelsWidth	= iWidth;
		dm.dmPelsHeight	= iHeight;
		dm.dmBitsPerPel	= iColorBits;
		dm.dmFields	= DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
		
		if ( ChangeDisplaySettings(&dm, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL )
		{
			if ( MessageBox(NULL, "The requested fullscreen mode is not supported by\nyour video card. Use windowed mode instead?", lpszTitle, MB_YESNO | MB_ICONQUESTION) == IDYES )
			{
				m_bFullscreen = FALSE;
			}
			else
			{
				MessageBox(NULL, "The requested fullscreen mode is not supported by\nyour video card. Exiting...", "Information", MB_OK | MB_ICONINFORMATION);
				return;
			}
		}
	}

	if ( m_bFullscreen )
	{
		dwExStyle	= WS_EX_APPWINDOW;
		dwStyle		= WS_POPUP | WS_VISIBLE;
		ShowCursor(FALSE);
	}
	else
	{
		dwExStyle	= WS_EX_CLIENTEDGE;
		dwStyle		= WS_SYSMENU | WS_BORDER | WS_CAPTION | WS_VISIBLE;
	}
	
	AdjustWindowRectEx(&wr, dwStyle, FALSE, dwExStyle);

	if( !(m_hWnd = CreateWindowEx(dwExStyle,
						"glWindow",
						lpszTitle,
						WS_CLIPSIBLINGS |
						WS_CLIPCHILDREN |
						dwStyle,
						0, 0,
						wr.right - wr.left,
						wr.bottom - wr.top,
						NULL,
						NULL,
						m_hInst,
						NULL)) )
	{
		MessageBox(NULL, "Unable to create hWnd...", "Critical Error", MB_OK | MB_ICONERROR);
		finalize();
	}

	GLuint pixelFormat;
	GLuint iAlphaBits	= 0;
	
	if ( iColorBits == 32 )
	{
		iAlphaBits = 8;
	}

	static PIXELFORMATDESCRIPTOR objPFD =
	{
		sizeof(PIXELFORMATDESCRIPTOR),
		1,
		PFD_DRAW_TO_WINDOW |
		PFD_SUPPORT_OPENGL |
		PFD_DOUBLEBUFFER,
		PFD_TYPE_RGBA,
		iColorBits,
		0, 0, 0, 0, 0, 0,
		iAlphaBits,
		0,
		0,
		0, 0, 0, 0,
		iDepthBits,
		iStencilBits,
		0,
		PFD_MAIN_PLANE,
		0,
		0, 0, 0
	};

	if ( !(m_hDC = GetDC(m_hWnd)) )
	{
		MessageBox(NULL, "Unable to fetch glDC...", "Critical Error", MB_OK | MB_ICONERROR);
		finalize();
	}
	
	if ( !(pixelFormat = ChoosePixelFormat(m_hDC, &objPFD)) )
	{
		MessageBox(NULL, "Unable to find a suitable pixel descriptor...", "Critical Error", MB_OK | MB_ICONERROR);
		finalize();
	}

	if ( !SetPixelFormat(m_hDC, pixelFormat, &objPFD) )
	{
		MessageBox(NULL, "Unable to set a pixel format...", "Critical Error", MB_OK | MB_ICONERROR);
		finalize();
	}

	if ( !(m_hRC = wglCreateContext(m_hDC)))
	{
		MessageBox(NULL, "Unable to create an OpenGL rendering context...", "Critical Error", MB_OK | MB_ICONERROR);
		finalize();
	}

	if ( !wglMakeCurrent(m_hDC, m_hRC) )
	{
		MessageBox(NULL, "Unable to activate the OpenGL rendering context...", "Critical Error", MB_OK | MB_ICONERROR);
		finalize();
	}

	//get pixel format parameters
	static PIXELFORMATDESCRIPTOR objFinalPfd;
	DescribePixelFormat(m_hDC, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &objFinalPfd);

	m_iColorBits	= objFinalPfd.cColorBits;
	m_iAlphaBits	= objFinalPfd.cAlphaBits;
	m_iDepthBits	= objFinalPfd.cDepthBits;
	m_iStencilBits	= objFinalPfd.cStencilBits;
	
	ShowWindow(m_hWnd, SW_SHOW);
	SetForegroundWindow(m_hWnd);
	SetFocus(m_hWnd);

	onInitialize	= NULL;
	onUpdate		= NULL;
	onRender		= NULL;
	onFinalize		= NULL;
	onKeyDown		= NULL;
	onKeyUp			= NULL;
}

HRESULT studio_c::begin()
{
	if ( !FAILED(deviceValidate()) )
	{
		if ( !FAILED(deviceCreate()) )
		{
			initialize();

			while ( handleMessages() )
			{
				update();
				render();
			}
		}
	}

	if ( !FAILED(deviceDestroy()) )
	{

	}

	finalize();

	return S_OK;
}

HRESULT studio_c::deviceValidate()
{
	return S_OK;
}

HRESULT studio_c::deviceCreate()
{
	return S_OK;
}

HRESULT studio_c::deviceDestroy()
{
	return S_OK;
}

HRESULT studio_c::initialize()
{
	if ( onInitialize )
		return onInitialize();

	return S_OK;
}

HRESULT studio_c::update()
{
	if ( onUpdate )
		return onUpdate();

	return S_OK;
}

HRESULT studio_c::render()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	if ( onRender )
	{
		onRender();
	}

	::SwapBuffers(m_hDC);

	return S_OK;
}

HRESULT studio_c::finalize()
{
	if ( m_bFullscreen )
	{
		ChangeDisplaySettings(NULL, 0);
		ShowCursor(TRUE);
	}
	
	if ( !wglMakeCurrent(NULL, NULL) )
	{
		MessageBox(NULL, "Release of device context and rendering context failed...", "Warning", MB_OK | MB_ICONWARNING);
	}

	if ( m_hRC && !wglDeleteContext(m_hRC) )
	{
		MessageBox(NULL, "Rendering context delete failed...", "Warning", MB_OK | MB_ICONWARNING);
	}

	if ( m_hDC && !ReleaseDC(m_hWnd, m_hDC) )
	{
		MessageBox(NULL, "Device context release failed...", "Warning", MB_OK | MB_ICONWARNING);
	}

	if ( m_hWnd && !DestroyWindow(m_hWnd) )
	{
		MessageBox(NULL, "Unable to release hWnd...", "Warning", MB_OK | MB_ICONWARNING);
	}

	if ( !UnregisterClass("glWindow", m_hInst) )
	{
		MessageBox(NULL, "Unable to unregister window class...", "Warning", MB_OK | MB_ICONWARNING);
	}

	m_hRC	= NULL;
	m_hDC	= NULL;
	m_hWnd	= NULL;
	m_hInst	= NULL;

	if ( onFinalize )
		return onFinalize();

	return S_OK;
}

HRESULT studio_c::keyDown(int iKeyCode)
{
	if ( onKeyDown )
		return onKeyDown(iKeyCode);

	return S_OK;
}

HRESULT studio_c::keyPress(int iKeyCode)
{
	if ( onKeyPress )
		return onKeyPress(iKeyCode);

	return S_OK;
}

HRESULT studio_c::keyUp(int iKeyCode)
{
	if ( onKeyUp )
		return onKeyUp(iKeyCode);

	return S_OK;
}

LRESULT CALLBACK studio_c::WndProc ( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
	switch (message)
	{
	/*case WM_CREATE:
	case WM_DESTROY:
		return 0;*/

	case WM_CLOSE:
		PostQuitMessage(0);
		return 0;

	case WM_KEYDOWN:
		switch ( wParam )
		{
		/*case VK_ESCAPE:
			PostQuitMessage(0);
			return 0;*/
		}
	}

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

bool studio_c::handleMessages ( void )
{
	while ( PeekMessage(&m_hMsg, NULL, 0, 0, PM_REMOVE) )
	{
		switch ( m_hMsg.message )
		{
		case WM_QUIT:
			return false;

		case WM_KEYDOWN:
			// WM_KEYDOWN functions like a keypress event
			keyPress(m_hMsg.wParam);
			break;

		/*case WM_KEYPRESS:
			// handle input later
			keyPress(m_hMsg.wParam);
			break;*/

		case WM_KEYUP:
			// handle input later
			keyUp(m_hMsg.wParam);
			break;

		case WM_LBUTTONDOWN:
			// handle input later
			break;

		case WM_RBUTTONDOWN:
			// handle input later
			break;
		}
		
		TranslateMessage(&m_hMsg);
		DispatchMessage(&m_hMsg);
	}

	return true;
}