#include "Engine.h"
Engine *Engine::m_pEngine = NULL;
Engine::Engine(HINSTANCE hInst, LPTSTR szWinClass, LPTSTR szTitle, WORD wIcon, WORD wSmIcon, DWORD Style, int iW, int iH, int ShwCmd)
{
	//Use incoming variables to set optional pieces of data
	m_hInstance		= hInst;
	if (lstrlen(szWinClass) > 0)	lstrcpy(m_szWindowClass, szWinClass);
	if (lstrlen(szTitle) > 0)		lstrcpy(m_szTitle, szTitle);
	m_wIcon			= wIcon;
	m_wSmIcon		= wSmIcon;
	m_iWidth		= iW;
	m_iHeight		= iH;

	//Default data settings.
	m_pEngine		= this;
	m_hWindow		= NULL;
	m_iFPS			= 50;   // 20 FPS default
	m_bSleep		= TRUE;
	m_bMouseDrag	= false;
	
	//Default Window program is a video game. If it is not, it needs to be set after Initialization
	m_IsGame		= true; 

	//Definable on a per project basis
	m_Style			= Style;

	m_ShwCmd		= ShwCmd;
}
Engine::~Engine()
{
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
	MSG         msg;
	int         iTickCount		= 0;
	static int  iTickTrigger	= 0;

	if(GameInitialize(hInstance))
	{
		if (!Engine::GetEngine()->Initialize(iCmdShow)) return FALSE;

		iTickCount = iTickTrigger = GetTickCount();

		//Only test once as to not slow game down
		if(Engine::GetEngine()->GetIsGame() == true)
		{
			while(TRUE)
			{
				if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
				{
					if (msg.message == WM_QUIT) break;
					TranslateMessage(&msg);
					DispatchMessage(&msg);
				}
				else
				{
					iTickCount = GetTickCount();
					if(iTickCount > iTickTrigger)
					{
						iTickTrigger = iTickCount + Engine::GetEngine()->GetFrameDelay();
						HandleKeys();
						GameCycle();
					}
					//Be kind to laptops...
					else Sleep(int(Engine::GetEngine()->GetFrameDelay() / 2));
				}
			}
		}
		else
		{
			//Set up a 'business application' type
			while(GetMessage(&msg, NULL, 0, 0))
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}
	}
	return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)
{
	// Route all Windows messages to the game engine
	return Engine::GetEngine()->HandleEvent(hWindow, msg, wParam, lParam);
}
BOOL Engine::Initialize(int iCmdShow)
{
	WNDCLASSEX    wndclass;
	// Create the window class for the main window
	wndclass.cbSize         = sizeof(wndclass);
	wndclass.style          = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc    = WndProc;
	wndclass.cbClsExtra     = 0;
	wndclass.cbWndExtra     = 0;
	wndclass.hInstance      = m_hInstance;
	wndclass.hIcon          = LoadIcon(m_hInstance, MAKEINTRESOURCE(GetIcon()));
	wndclass.hIconSm        = LoadIcon(m_hInstance, MAKEINTRESOURCE(GetSmallIcon()));
	wndclass.hCursor        = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground  = (HBRUSH)(COLOR_WINDOW + 1);
	wndclass.lpszMenuName   = NULL;
	wndclass.lpszClassName  = m_szWindowClass;
	
	// Register the window class
	if(!RegisterClassEx(&wndclass))	return FALSE;

	// Calculate the window size and position based upon the game size
	int iW = m_iWidth + GetSystemMetrics(SM_CXFIXEDFRAME) * 2,
		iH = m_iHeight /*+ GetSystemMetrics(SM_CYFIXEDFRAME) * 2 + GetSystemMetrics(SM_CYCAPTION)*/;

	//Adding height in case there's a Menu Bar
	if(wndclass.lpszMenuName != NULL)
		iH += GetSystemMetrics(SM_CYMENU);

	//Set up starting position
	int iX = (GetSystemMetrics(SM_CXSCREEN) - iW) / 2;
	int iY = 0;//(GetSystemMetrics(SM_CYSCREEN) - iH) / 2;

	// Create the window
	m_hWindow = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, m_szTitle, m_szTitle, m_Style, iX, iY, iW, iH,
								NULL, NULL, m_hInstance, NULL);
	
	//Window failed to be created...
	if(!m_hWindow)
	{
		MessageBox(NULL, "Failed to Create Main Window", "Programming Error", MB_OK);
		return FALSE;
	}

	// Show and update the window
	//Added the m_ShwCmd to this function so that the calling game has control over this - M. White 13 March, 2019
	ShowWindow(m_hWindow, m_ShwCmd);
	UpdateWindow(m_hWindow);

	//Everything worked out and the rest of the program can continue
	return TRUE;
}
LRESULT Engine::HandleEvent(HWND hWindow, UINT msg, WPARAM wPar, LPARAM lPar)
{
	HDC         hDC;
	PAINTSTRUCT ps;
	WINDOWINFO	wi;
//	char typed;

	// Route Windows messages to game engine member functions
	switch (msg)
	{
	case WM_MOUSEMOVE:		MouseMove(LOWORD(lPar), HIWORD(lPar));						return 0;
	case WM_LBUTTONDOWN:	MouseButtonDN(LOWORD(lPar), HIWORD(lPar), TRUE);			return 0;
	case WM_LBUTTONUP:		MouseButtonUP(LOWORD(lPar), HIWORD(lPar), TRUE);			return 0;
	case WM_RBUTTONDOWN:	MouseButtonDN(LOWORD(lPar), HIWORD(lPar), FALSE);			return 0;
	case WM_RBUTTONUP:		MouseButtonUP(LOWORD(lPar), HIWORD(lPar), FALSE);			return 0;
	case WM_KEYDOWN:
		return 0;
	case WM_KEYUP:
		return 0;
	case WM_CHAR:
		if(HandleKeys() == 0)
		{
			if ((wPar >= 0) && (wPar <= 256))
			{
			//	sprintf(typed, "%c", (char)wPar);
				HandleTyping((char)wPar);
			}
		}
		return 0;
	case WM_PAINT:			hDC = BeginPaint(hWindow, &ps); EndPaint(hWindow, &ps);		return 0;
	case WM_COMMAND:		SetIsTyping(false);											return 0;
	case WM_CREATE:
		SetWindow(hWindow);
		/*
			Moved the GameStart() call into GameCycle() after a count down to ensure that 
			a full screen Window was maximized to be able to get some cheap data from the 
			User's SystemMetrics() - M. White 13 March, 2019

			GameStart(hWindow);
		*/
		return 0;
	case WM_MOVE:
		SetWasMinimized(true);
		GetWindowInfo(hWindow, &wi);
		m_iWidth = wi.rcClient.right - wi.rcClient.left;
		m_iHeight = wi.rcClient.bottom - wi.rcClient.top;
		return 0;
	case WM_ACTIVATE:
		if (wPar != WA_INACTIVE)
		{
			SetSleep(FALSE);
		}
		else
		{
			SetSleep(TRUE);
		}
		SetWasMinimized(true);
		return 0;
	case WM_SIZE:
		GetWindowInfo(hWindow, &wi);
		m_iWidth = wi.rcClient.right - wi.rcClient.left;
		m_iHeight = wi.rcClient.bottom - wi.rcClient.top;
		SetWasMinimized(true);
		return 0;
	case WM_DESTROY:
		//GameEnd() has to be here before the QuitMessage is posted...
		GameEnd();
		PostQuitMessage(0);
		break;
	}
	return DefWindowProc(hWindow, msg, wPar, lPar);
}
void Engine::MouseButtonDN(int x, int y, BOOL bLeft)
{
	m_bMouseDrag = true;
	m_bMouseUp = false;
	if(bLeft == TRUE)	m_bLeft = true;
	else				m_bLeft = false;
	MouseMove(x, y);
}
void Engine::MouseButtonUP(int x, int y, BOOL bLeft)
{
	m_bMouseDrag	= false;
	m_bMouseUp		= true;
	if(bLeft == TRUE)	m_bLeft = true;
	else				m_bLeft = false;
	MouseMove(x, y);
}
void Engine::MouseMove(int x, int y)
{
	m_iMouseX = x;
	m_iMouseY = y;
}
//A test function to determine a file exists before attempting to use 
//Source: https://stackoverflow.com/questions/25225948/how-to-check-if-a-file-exists-in-c-with-fstreamopen?rq=1
//- M. White 14 May, 2019
bool exists (const std::string& filename)
{
	struct stat buffer;
	return (stat (filename.c_str(), &buffer) == 0); 
}