
#include "include/Utils/Utils.h"
#include "include/SalmonEngineWindows.h"


namespace SE
{

//==============================================================
//========= GLOBAL VARIABLES - REMEMBER THIS LIST!!! ===========
//==============================================================

HGLRC hRC;		//Render context
HWND Hwnd;		//Main window handle
HDC hDC;		//Device context

//==============================================================
//=============== GLOBAL VARS ENDS =============================
//==============================================================

//There might be anything
const char CONST_WINDOW_CLASS_NAME[] = "TSalmonEngineForm";

const cardinal CONST_TIMER_INTERVAL = 10;

//To calculate when to call Update
cardinal NewTickCount;
cardinal LastTickCount;

//User application
TApplication* App = NULL;

vec2 MouseButtonPos;
vec2 MouseTotalShift;
bool MouseButtonPressed = false;
bool MouseMoved = false;


TApplication::TApplication() 
{
}

TApplication::~TApplication()
{
}

void TApplication::OnKeyPress(cardinal key)
{
	if (IsConsoleOut)
	{
		
		if (key == 96 || key == 126 /*|| key == 184 || key == 168*/) //~, 184 and 168 is russian e
		{
			IsConsoleOut = false;
		}
		else if (key == 8) //Backspace
		{
			if (Console->ConsoleInput.size() > 0 && Console->ConsoleCursor > 0)
			{
				Console->ConsoleInput.erase(Console->ConsoleInput.begin() + Console->ConsoleCursor-1);
				Console->ConsoleCursor--;
			}
		}
		else if (key == 13|| key == 10) //Enter
		{
			ResourceManager->ScriptManager.RunScript(Console->ConsoleInput);

			Console->InputHistory.push_back(Console->ConsoleInput);

			Console->InputHistoryCursor = Console->InputHistory.size();

			Console->ConsoleInput = "";
			Console->ConsoleCursor = 0;
		}
		else if (key >= 32 && key <= 255) //ASCII
		{
			Console->ConsoleInput.insert(Console->ConsoleInput.begin()+Console->ConsoleCursor, static_cast<char>(key));
			Console->ConsoleCursor++;
		}
		else if (key > 0x100) //VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT
		{
			cardinal realKey = key - 0x100;

			if (realKey == VK_UP)
			{
				if (Console->InputHistoryCursor > 0)
				{
					Console->InputHistoryCursor--;
					Console->ConsoleInput = Console->InputHistory[Console->InputHistoryCursor];
					Console->ConsoleCursor = Console->ConsoleInput.size();
				}
			}
			if (realKey == VK_DOWN)
			{
				if ( Console->InputHistoryCursor+1 < Console->InputHistory.size())
				{
					Console->InputHistoryCursor++;
					Console->ConsoleInput = Console->InputHistory[Console->InputHistoryCursor];
					Console->ConsoleCursor = Console->ConsoleInput.size();
				}
			}
			if (realKey == VK_LEFT)
			{
				if (Console->ConsoleCursor > 0)
				{
					Console->ConsoleCursor--;
				}
			}
			if (realKey == VK_RIGHT)
			{
				if (Console->ConsoleCursor < Console->ConsoleInput.size())
				{
					Console->ConsoleCursor++;
				}
			}

		}
		else
		{
			*Console<<"Key is "+tostr(key)+endl;
		}

	}
	else
	{
		if (key == 96 || key == 126 /*|| key == 184 || key == 168*/) //~, 184 and 168 is russian e
		{
			IsConsoleOut = true;
			Console->ConsoleInput = "";
			Console->ConsoleCursor = Console->ConsoleInput.size();
		}
		else
		{
			ResourceManager->GUIManager.KeyPressedSignal(static_cast<int>(key));
		}
	}
}


void DrawScene()
{
	App->OuterDraw();
    SwapBuffers(hDC);
}

void ProcessTickCount()
{
	//Can't choose witch one is better

	/*
	App->UpdateQuick();
	NewTickCount = GetTickCount();
	if (NewTickCount - LastTickCount > CONST_TIMER_INTERVAL)
	{
		LastTickCount = NewTickCount;
		App->OuterUpdate(CONST_TIMER_INTERVAL);
	}*/


	NewTickCount = GetTickCount();
	if (NewTickCount - LastTickCount > CONST_TIMER_INTERVAL)
	{
		App->OuterUpdate(NewTickCount - LastTickCount);
		LastTickCount = NewTickCount;
	}

}

LONG WINAPI WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{ 
    static PAINTSTRUCT ps;

	TMouseState mouseState;

    switch(uMsg) 
	{
	case WM_CLOSE:
		Hwnd = 0; //HACK to force window to close
		PostQuitMessage(0);
		//Xperimental
		return DefWindowProc(hWnd, uMsg, wParam, lParam); 

		break;
	case WM_PAINT: 
		DrawScene();
		BeginPaint(hWnd, &ps);
		EndPaint(hWnd, &ps);

		//ProcessTickCount();
		break;
	case WM_SIZE:
		
		PostMessage(hWnd, WM_PAINT, 0, 0);
		break;
	case WM_CHAR:
		App->OnKeyPress(wParam);
		/*
		switch (wParam) 
		{
		case 27:
				PostQuitMessage(0);
				Hwnd = 0;
				
				//Xperimental
				return DefWindowProc(hWnd, uMsg, wParam, lParam); 

				break;
		//... Will add more code later
		}*/
		break;

case WM_MOUSEMOVE:
		mouseState.X = (lParam << 16) >> 16;
		mouseState.Y = lParam >> 16;
		mouseState.LeftButtonPressed = (wParam & MK_LBUTTON);
		mouseState.MiddleButtonPressed = (wParam & MK_MBUTTON);
		mouseState.RightButtonPressed = (wParam & MK_RBUTTON);

		App->OnMouseMove(mouseState);

		if (MouseButtonPressed)
		{
			vec2 currentMousePos(static_cast<float>(mouseState.X), static_cast<float>(Renderer->GetScreenHeight() - mouseState.Y));
			vec2 shift = (MouseButtonPos - currentMousePos);
			//shift.v[1] = - shift.v[1];
			App->OuterOnMove(shift);
			//App->OuterOnMove(currentMousePos - MouseButtonPos);
			MouseButtonPos = currentMousePos;

			MouseTotalShift += shift;

			if (fabs(MouseTotalShift.v[0]) > 10.f || fabs(MouseTotalShift.v[1]) > 10.f)
			{
				MouseMoved = true;
			}
		}

		break;

	case WM_LBUTTONDOWN:
	case WM_RBUTTONDOWN:
		mouseState.X = (lParam << 16) >> 16;
		mouseState.Y = lParam >> 16;
		mouseState.LeftButtonPressed = (wParam & MK_LBUTTON);
		mouseState.MiddleButtonPressed = (wParam & MK_MBUTTON);
		mouseState.RightButtonPressed = (wParam & MK_RBUTTON);

		MouseButtonPos = vec2(static_cast<float>(mouseState.X), static_cast<float>(Renderer->GetScreenHeight() - mouseState.Y));

		if (mouseState.LeftButtonPressed)
		{
			App->OuterOnTapDown(MouseButtonPos);

		}

		MouseButtonPressed = true;

		MouseMoved = false;

		MouseTotalShift = vec2(0,0);
		


		break;
	case WM_LBUTTONUP:
	case WM_RBUTTONUP:
		mouseState.X = (lParam << 16) >> 16;
		mouseState.Y = lParam >> 16;
		mouseState.LeftButtonPressed = (wParam & MK_LBUTTON);
		mouseState.MiddleButtonPressed = (wParam & MK_MBUTTON);
		mouseState.MiddleButtonPressed = (wParam & MK_RBUTTON);

		if (MouseMoved)
		{
			App->OuterOnTapUpAfterMove(vec2(static_cast<float>(mouseState.X), static_cast<float>(Renderer->GetScreenHeight() - mouseState.Y)));
		}
		else
		{
			App->OuterOnTapUp(vec2(static_cast<float>(mouseState.X), static_cast<float>(Renderer->GetScreenHeight() - mouseState.Y)));
		}

		MouseButtonPressed = false;

		break;
	
	case WM_MOUSEWHEEL:
		App->OnMouseWheel(wParam >> 16);
		break;
    }

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

bool CreateOpenGLWindow(const char* title, int x, int y, int width, int height,HWND& hWnd, HDC& hDC, HGLRC& hRC)
{
    int pf;
    WNDCLASS wc;
    PIXELFORMATDESCRIPTOR pfd;
    static HINSTANCE hInstance = 0;

    if (!hInstance) 
	{
		hInstance = GetModuleHandle(NULL);
		wc.style = CS_OWNDC;
		wc.lpfnWndProc = (WNDPROC)WindowProc;
		wc.cbClsExtra = 0;
		wc.cbWndExtra = 0;
		wc.hInstance = hInstance;
		wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
		wc.hCursor = LoadCursor(NULL, IDC_ARROW);
		wc.hbrBackground = NULL;
		wc.lpszMenuName  = NULL;
		wc.lpszClassName = CONST_WINDOW_CLASS_NAME;

		if (!RegisterClass(&wc)) 
		{
			return NULL;
		}
    }

	RECT r;
	r.left = x;
	r.right = x + width;
	r.top = y;
	r.bottom = y + height;
	
	DWORD windowStyle = WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; 

	AdjustWindowRect(&r, windowStyle, false);
	
	hWnd = CreateWindow(CONST_WINDOW_CLASS_NAME, title, windowStyle, 0, 0, r.right-r.left, r.bottom-r.top, NULL, NULL, hInstance, NULL);

    if (hWnd == NULL) 
	{
		return false;
    }

    hDC = GetDC(hWnd);

    memset(&pfd, 0, sizeof(pfd));
    pfd.nSize = sizeof(pfd);
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = 32;

    pf = ChoosePixelFormat(hDC, &pfd);
    if (pf == 0) 
	{
		return false;
    } 
 
    if (SetPixelFormat(hDC, pf, &pfd) == FALSE) 
	{
		return false;
    } 

    hRC = wglCreateContext( hDC );
	wglMakeCurrent( hDC, hRC );

    return true;
}

bool CreateEngine(int width, int height, int x, int y, std::string windowName, std::string logFileName)
{
	
	//Here console log is not created so we can not use ErrorCommon
	try
	{
		Console = new TFileConsole(logFileName);
	}
	catch(...)
	{
		return false;
	}

	//Here console log is already created

	*Console<<"Log started";
    
	if (!CreateOpenGLWindow(windowName.c_str(), x, y, width, height, Hwnd, hDC, hRC))
	{
		*Console<<"Unable to create OpenGL Window!";
		return false;
	}

	try
	{

		Renderer = new TSalmonRenderer;
		ResourceManager = new TResourceManager;


		return Renderer->BindOpenGLFunctions();

	}
	catch (...)
	{
		return false;
	}
}

void DestroyEngine()
{
	try
	{

		*Console<<"Program prepares to quit";

		delete ResourceManager;
		delete Renderer;


		wglMakeCurrent(NULL, NULL);
		ReleaseDC(Hwnd, hDC);
		wglDeleteContext(hRC);
		DestroyWindow(Hwnd);

	}
	catch(...)
	{
		//... let the ErrorTypes.h do the buisness
	}


	delete Console;

}

void MainLoop(TApplication* app)
{
	App = app;

	MSG msg;

	ShowWindow(Hwnd, SW_SHOW);
	UpdateWindow(Hwnd);

	NewTickCount = GetTickCount();
	LastTickCount = NewTickCount;

	bool StayIn = true;

	while (StayIn) 
	{
		while(PeekMessage(&msg, Hwnd, 0, 0, PM_NOREMOVE)) 
		{
			if(GetMessage(&msg, Hwnd, 0, 0)) 
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			} 
			else
			{
				StayIn = false;
			}
		}
		DrawScene();

		ProcessTickCount();
	}
}



} //namespace SE

