#include "stdafx.h"
#include "CMainWnd.h"

#include "AGK.h"
#include <windows.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include <stdio.h>
#include <gl\gl.h>								// Header File For The OpenGL32 Library
#include <gl\glu.h>								// Header File For The GLu32 Library
#include "direct.h"

#include "template.h"
#include "resource.h"
#include "CMouseDevice.h"

#include <string>

#define WM_SOUND_EVENT 0x8001
#define WM_VIDEO_EVENT 0x8002

//#define MULTI_MOUSE // comment out to revert to single mouse

using namespace AGK;

// Globals for core
HANDLE hDebugConsoleOut = NULL;
HWND g_hWnd = NULL;

//
// Windows Application STUB requires entry function, create window and message pump
//
#define AGK_MOUSE_LEFTDOWN		0x00000001
#define AGK_MOUSE_RIGHTDOWN		0x00000002
#define AGK_MOUSE_MIDDLEDOWN	0x00000004

CMouseDevice *pMouseList = NULL;

UINT TranslateKey(UINT key)
{
	switch (key)
	{
	// Caps lock F9 F10 F11 F12 Alt Gr Windows key (left) Windows key (right)
	case 20:  	case 120: 	case 121: 	case 122:
	case 123: 	case 18:  	case 91:  	case 92:  
		key = 0;
		break;

	// Num pad 0-9
	case 96:	case 97:	case 98:	case 99:	case 100:
	case 101:	case 102:	case 103:	case 104:	case 105: 
		key -= 48;
		break;

	case 111: key = 192; break; // Num pad /
	case 109: key = 189; break; // Num pad -
	case 110: key = 190; break; // Num pad .

	case 144: key = 0; break;  // Num lock
	case 19: key = 0; break;  // pause/break
	}

	return key;
}

// delay the loading of these functions so Windows XP and Vista don't complain
int(__stdcall *CloseTouchInputHandleDelayed)(HTOUCHINPUT) = 0;
int(__stdcall *GetTouchInputInfoDelayed)(HTOUCHINPUT, unsigned int, PTOUCHINPUT, int) = 0;
BOOL(__stdcall *RegisterTouchWindowDelay)(HWND, ULONG) = 0;

void LoadDelayedFunctions()
{
	// only checks once
	static int first = 1;
	if (first == 1)
	{
		first = 0;
		HMODULE user32 = LoadLibrary(L"USER32.dll");
		CloseTouchInputHandleDelayed = (int(__stdcall *)(HTOUCHINPUT)) GetProcAddress(user32, "CloseTouchInputHandle");
		GetTouchInputInfoDelayed = (int(__stdcall *)(HTOUCHINPUT, unsigned int, PTOUCHINPUT, int)) GetProcAddress(user32, "GetTouchInputInfo");
		RegisterTouchWindowDelay = (BOOL(__stdcall *)(HWND, ULONG)) GetProcAddress(user32, "RegisterTouchWindow");
		FreeLibrary(user32);
	}
}

BEGIN_MESSAGE_MAP(CMainWnd, CFrameWnd)
	ON_WM_PAINT()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_RBUTTONDOWN()
	ON_WM_RBUTTONUP()
	ON_WM_MBUTTONDOWN()
	ON_WM_MBUTTONUP()
	ON_WM_MOUSEWHEEL()
	ON_WM_MOUSEMOVE()
	ON_WM_SIZE()
	ON_WM_WINDOWPOSCHANGED()
	ON_WM_MOVE()
	ON_WM_ACTIVATE()
	ON_WM_NCACTIVATE()
	ON_WM_SETFOCUS()
	ON_WM_KEYDOWN()
	ON_WM_CHAR()
	ON_WM_SYSKEYDOWN()
	ON_WM_SYSKEYUP()
	ON_WM_CLOSE()
	ON_WM_ERASEBKGND()
	ON_WM_DWMCOMPOSITIONCHANGED()
		
	ON_MESSAGE(WM_COMMAND, on_command)
	ON_MESSAGE(WM_TOUCH, OnTouch)
	ON_MESSAGE(WM_SOUND_EVENT, OnSoundEvent)
	ON_MESSAGE(WM_VIDEO_EVENT, OnVideoEvent)
END_MESSAGE_MAP()

LRESULT CMainWnd::OnTouch(WPARAM wParam, LPARAM lParam)
{
	LoadDelayedFunctions();

	if (CloseTouchInputHandleDelayed)
	{
		// windows based touch device
		UINT cInputs = LOWORD(wParam);
		if (cInputs > 0)
		{
			// compensate for window (which may not be in top left)
			RECT rect;
			this->GetWindowRect(&rect);

			PTOUCHINPUT pInputs = new TOUCHINPUT[cInputs];
			if (GetTouchInputInfoDelayed((HTOUCHINPUT)lParam, cInputs, pInputs, sizeof(TOUCHINPUT)))
			{
				// process pInputs
				for (UINT i = 0; i < cInputs; i++)
				{
					pInputs[i].x -= (rect.left * 100);
					pInputs[i].y -= (rect.top * 100);
					pInputs[i].x /= 100;
					pInputs[i].y /= 100;

					if (pInputs[i].dwFlags & TOUCHEVENTF_DOWN) agk::TouchPressed(pInputs[i].dwID, pInputs[i].x, pInputs[i].y);
					if (pInputs[i].dwFlags & TOUCHEVENTF_MOVE) agk::TouchMoved(pInputs[i].dwID, pInputs[i].x, pInputs[i].y);
					if (pInputs[i].dwFlags & TOUCHEVENTF_UP) agk::TouchReleased(pInputs[i].dwID, pInputs[i].x, pInputs[i].y);
				}

				CloseTouchInputHandleDelayed((HTOUCHINPUT)lParam);
			}

			delete[] pInputs;
		}
	}

	return 0;
}

LRESULT CMainWnd::OnSoundEvent(WPARAM wParam, LPARAM lParam)
{
	agk::HandleMusicEvents((void*)lParam);
	return 0;
}

LRESULT CMainWnd::OnVideoEvent(WPARAM wParam, LPARAM lParam)
{
	agk::HandleVideoEvents();
	return 0;
}

LRESULT CMainWnd::on_command(WPARAM wParam, LPARAM lParam)
{
	if ((wParam & 0xffff) == 124)
	{		
		agk::CaptureImage();
		agk::CancelCapture();
	}
	else if ((wParam & 0xffff) == 125)
		agk::CancelCapture();

	return 0;
}

void CMainWnd::OnLButtonDown(UINT nFlags, CPoint point)
{
	this->bLeftDown = TRUE;

	agk::MouseLeftButton(0, TRUE);
}

void CMainWnd::OnLButtonUp(UINT nFlags, CPoint point)
{
	this->bLeftDown = FALSE;

	agk::MouseLeftButton(0, FALSE);
}

void CMainWnd::OnRButtonDown(UINT nFlags, CPoint point)
{
	agk::MouseRightButton(0, TRUE);
}

void CMainWnd::OnRButtonUp(UINT nFlags, CPoint point)
{
	agk::MouseRightButton(0, FALSE);
}

void CMainWnd::OnMButtonDown(UINT nFlags, CPoint point)
{
	agk::MouseMiddleButton(0, TRUE);
}

void CMainWnd::OnMButtonUp(UINT nFlags, CPoint point)
{
	agk::MouseRightButton(0, FALSE);
}

BOOL CMainWnd::OnMouseWheel(UINT nFlags, SHORT zDelta, CPoint point)
{
	agk::MouseWheel(0, zDelta > 0 ? (float)1 : (float)0);

	return CFrameWnd::OnMouseWheel(nFlags, zDelta, point);
}

void CMainWnd::OnMouseMove(UINT nFlags, CPoint point)
{
	agk::MouseMove(0, point.x, point.y);
}

void CMainWnd::OnSize(UINT nType, INT newWidth, INT newHeight)
{
	this->g_bShouldBeTopMost = (nType == SIZE_MAXIMIZED);

	if(this->g_bShouldBeTopMost)
		this->SetWindowPos(&wndTopMost,  0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE);
	else
		this->SetWindowPos(&wndNoTopMost, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE);
	
	agk::UpdateDeviceSize();
	agk::WindowMoved();
}

void CMainWnd::OnWindowPosChanged(WINDOWPOS *posStructure)
{	
	agk::WindowMoved();
	return CFrameWnd::OnWindowPosChanged(posStructure);
}
void CMainWnd::OnMove(INT newX, INT newY)
{
	agk::WindowMoved();
}

void CMainWnd::OnActivate(UINT nState, CWnd *pWndOther, BOOL bMinimized)
{
	if (nState != WA_INACTIVE)
		agk::Resumed();
	else
	{
		agk::MouseLeftButton(0, 0);
		agk::MouseRightButton(0, 0);
		agk::MouseMiddleButton(0, 0);
	}

	if (this->g_bShouldBeTopMost)
		this->SetWindowPos(&wndTopMost, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE);
	
	agk::WindowMoved();
}

BOOL CMainWnd::OnNcActivate(BOOL bActive)
{
	if(bActive)	agk::WindowMoved();
	return  CFrameWnd::OnNcActivate(bActive);
}

void CMainWnd::OnSetFocus(CWnd *oldWnd)
{
	agk::WindowMoved();
}

void CMainWnd::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	UINT key = TranslateKey(nChar);
	if (key > 0)
		agk::KeyUp(key);
}

void CMainWnd::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	agk::CharDown(nChar);
}

void CMainWnd::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{

}

void CMainWnd::OnSysKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{

}

void CMainWnd::OnClose()
{
	App.End();
	agk::CleanUp();
	PostQuitMessage(0);
}

BOOL CMainWnd::OnEraseBkgnd(CDC *pDc)
{
	return CFrameWnd::OnEraseBkgnd(pDc);
}

void CMainWnd::OnPaint()
{
	this->ValidateRect(NULL);

	CFrameWnd::OnPaint();
}

void CMainWnd::OnCompositionChanged()
{
	agk::CompositionChanged();
}

CMainWnd::CMainWnd()
	:CFrameWnd()
{
	INT width = DEVICE_WIDTH;
	INT height = DEVICE_HEIGHT;
	INT posX = DEVICE_POS_X;
	INT posY = DEVICE_POS_Y;
	BOOL FullScreen = FULLSCREEN;

	CWnd *desktopWnd = this->GetDesktopWindow();
	CRect rc;
	desktopWnd->GetWindowRect(&rc);
	if (width < 1) width = 1;
	if (height < 1) height = 1;

	float appAspect = width / (float)height;
	float windowAspect = (rc.right - rc.left - 15) / (float)(rc.bottom - rc.top - 80);

	if (appAspect > windowAspect)
	{
		if (width > rc.right - rc.left - 15)
		{
			float ratio = (rc.right - rc.left - 15) / (float)width;
			width = rc.right - rc.left - 15;
			height = (int)(height*ratio);
		}
	}
	else
	{
		if (height > rc.bottom - rc.top - 80)
		{
			float ratio = (rc.bottom - rc.top - 80) / (float)height;
			height = rc.bottom - rc.top - 80;
			width = (int)(width*ratio);
		}
	}

	if (posX == -1) posX = ((rc.right - rc.left) - width) / 2 + rc.left;
	if (posY == -1) posY = ((rc.bottom - rc.top - 50) - height) / 2 + rc.top;
	if (posX < 0) posX = 0;
	if (posY < 0) posY = 0;
	if (posX + width > rc.right) posX = rc.right - width;
	if (posY + height > rc.bottom) posY = rc.bottom - height;

	if (FullScreen)
	{
		width = rc.right - rc.left;
		height = rc.bottom - rc.top;
		posX = 0;
		posY = 0;

		/*
		DEVMODE dmScreenSettings;								// Device Mode
		memset(&dmScreenSettings,0,sizeof(dmScreenSettings));	// Makes Sure Memory's Cleared
		dmScreenSettings.dmSize=sizeof(dmScreenSettings);		// Size Of The Devmode Structure
		dmScreenSettings.dmPelsWidth	= width;				// Selected Screen Width
		dmScreenSettings.dmPelsHeight	= height;				// Selected Screen Height
		dmScreenSettings.dmBitsPerPel	= 32;					// Selected Bits Per Pixel
		dmScreenSettings.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(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
		{
		fullscreen = false;
		}
		*/
	}

	DWORD dwExStyle;
	DWORD dwStyle;
	if (FullScreen == TRUE)
	{
		dwExStyle = WS_EX_APPWINDOW;
		dwStyle = WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
	}
	else
	{
		dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
		dwStyle = WS_OVERLAPPEDWINDOW & (~WS_SIZEBOX);
	}

	// adjust the window and make it bigger for borders, window title, etc
	CRect WindowRect(0, 0, width, height);
	::AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);
	
	CString class_name = AfxRegisterWndClass(
		CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
		AfxGetApp()->LoadStandardCursor(IDC_ARROW),
		(HBRUSH)::GetStockObject(BLACK_BRUSH),
		AfxGetApp()->LoadStandardIcon(IDI_ASTERISK));

	HRESULT hresult = this->CreateEx(
		dwExStyle,									//ExStyle
		class_name,									//window class name
		L"MFC AGK by basjak",						//Caption
		dwStyle,									//style
		posX, posY,									//window 0, 0 position.
		WindowRect.Width(), WindowRect.Height(),	//window width  and height.
		0,											//Parent CWnd in this case desktop
		0											//menu Id
	);

	//	this->RegisterTouchWindow(TRUE, 0);
	g_bShouldBeTopMost = FALSE;

	LoadDelayedFunctions();
	if (RegisterTouchWindowDelay) RegisterTouchWindowDelay(this->GetSafeHwnd(), TWF_WANTPALM);

	try
	{
		agk::InitGL((void*)this->GetSafeHwnd());
		App.Begin();
	}
	catch (...)
	{
		CString err = agk::GetLastError();
		err = L"Uncaught exception: \n\n" + err;
		this->MessageBox(err, L"Error", MB_OK);
		exit(EXIT_FAILURE);
	}
}

CMainWnd::~CMainWnd() { }

