/*
*
*    This program is free software; you can redistribute it and/or modify it
*    under the terms of the GNU General Public License as published by the
*    Free Software Foundation; either version 2 of the License, or (at
*    your option) any later version.
*
*    This program is distributed in the hope that it will be useful, but
*    WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*    General Public License for more details.
*
*    You should have received a copy of the GNU General Public License
*    along with this program; if not, write to the Free Software Foundation,
*    Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
*    In addition, as a special exception, the author gives permission to
*    link the code of this program with the Half-Life Game Engine ("HL
*    Engine") and Modified Game Libraries ("MODs") developed by Valve,
*    L.L.C ("Valve").  You must obey the GNU General Public License in all
*    respects for all of the code used other than the HL Engine and MODs
*    from Valve.  If you modify this file, you may extend this exception
*    to your version of the file, but you are not obligated to do so.  If
*    you do not wish to do so, delete this exception statement from your
*    version.
*
*/

#include "precompiled.h"

#if defined(_WIN32)

CTextConsoleWin32 console;
#pragma comment(lib, "user32.lib")

BOOL WINAPI ConsoleHandlerRoutine(DWORD CtrlType)
{
	// TODO ?
	/*if (CtrlType != CTRL_C_EVENT && CtrlType != CTRL_BREAK_EVENT)
	{
		// don't quit on break or ctrl+c
		m_System->Stop();
	}*/

	return TRUE;
}

// GetConsoleHwnd() helper function from MSDN Knowledge Base Article Q124103
// needed, because HWND GetConsoleWindow(VOID) is not avaliable under Win95/98/ME
HWND GetConsoleHwnd()
{
	HWND hwndFound;			// This is what is returned to the caller.
	char pszNewWindowTitle[1024];	// Contains fabricated WindowTitle
	char pszOldWindowTitle[1024];	// Contains original WindowTitle

	// Fetch current window title.
	GetConsoleTitle(pszOldWindowTitle, sizeof(pszOldWindowTitle));

	// Format a "unique" NewWindowTitle.
	wsprintf(pszNewWindowTitle, "%d/%d", GetTickCount(), GetCurrentProcessId());

	// Change current window title.
	SetConsoleTitle(pszNewWindowTitle);

	// Ensure window title has been updated.
	Sleep(40);

	// Look for NewWindowTitle.
	hwndFound = FindWindow(nullptr, pszNewWindowTitle);

	// Restore original window title.
	SetConsoleTitle(pszOldWindowTitle);

	return hwndFound;
}

CTextConsoleWin32::~CTextConsoleWin32()
{
	CTextConsoleWin32::ShutDown();
}

bool CTextConsoleWin32::Init(IBaseSystem *system)
{
	if (!AllocConsole())
		m_System = system;

	SetTitle(m_System ? m_System->GetName() : "Console");

	hinput = GetStdHandle(STD_INPUT_HANDLE);
	houtput = GetStdHandle(STD_OUTPUT_HANDLE);

	if (!SetConsoleCtrlHandler(&ConsoleHandlerRoutine, TRUE)) {
		Print("WARNING! TextConsole::Init: Could not attach console hook.\n");
	}

	Attrib = FOREGROUND_GREEN | FOREGROUND_INTENSITY | BACKGROUND_INTENSITY;
	SetWindowPos(GetConsoleHwnd(), HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOREPOSITION | SWP_SHOWWINDOW);

	return CTextConsole::Init(system);
}

void CTextConsoleWin32::ShutDown()
{
	FreeConsole();
	CTextConsole::ShutDown();
}

void CTextConsoleWin32::SetVisible(bool visible)
{
	ShowWindow(GetConsoleHwnd(), visible ? SW_SHOW : SW_HIDE);
	m_ConsoleVisible = visible;
}

char *CTextConsoleWin32::GetLine()
{
	while (true)
	{
		INPUT_RECORD recs[1024];
		unsigned long numread;
		unsigned long numevents;

		if (!GetNumberOfConsoleInputEvents(hinput, &numevents))
		{
			if (m_System) {
				m_System->Errorf("CTextConsoleWin32::GetLine: !GetNumberOfConsoleInputEvents\n");
			}

			return nullptr;
		}

		if (numevents <= 0)
			break;

		if (!ReadConsoleInput(hinput, recs, ARRAYSIZE(recs), &numread))
		{
			if (m_System) {
				m_System->Errorf("CTextConsoleWin32::GetLine: !ReadConsoleInput\n");
			}

			return nullptr;
		}

		if (numread == 0)
			return nullptr;

		for (int i = 0; i < (int)numread; i++)
		{
			INPUT_RECORD *pRec = &recs[i];
			if (pRec->EventType != KEY_EVENT)
				continue;

			if (pRec->Event.KeyEvent.bKeyDown)
			{
				// check for cursor keys
				if (pRec->Event.KeyEvent.wVirtualKeyCode == VK_UP)
				{
					ReceiveUpArrow();
				}
				else if (pRec->Event.KeyEvent.wVirtualKeyCode == VK_DOWN)
				{
					ReceiveDownArrow();
				}
				else if (pRec->Event.KeyEvent.wVirtualKeyCode == VK_LEFT)
				{
					ReceiveLeftArrow();
				}
				else if (pRec->Event.KeyEvent.wVirtualKeyCode == VK_RIGHT)
				{
					ReceiveRightArrow();
				}
				else
				{
					int nLen;
					char ch = pRec->Event.KeyEvent.uChar.AsciiChar;
					switch (ch)
					{
					case '\r':	// Enter
						nLen = ReceiveNewline();
						if (nLen)
						{
							return m_szConsoleText;
						}
						break;
					case '\b':	// Backspace
						ReceiveBackspace();
						break;
					case '\t':	// TAB
						ReceiveTab();
						break;
					default:
						// dont' accept nonprintable chars
						if ((ch >= ' ') && (ch <= '~'))
						{
							ReceiveStandardChar(ch);
						}
						break;
					}
				}
			}
		}
	}

	return nullptr;
}

void CTextConsoleWin32::PrintRaw(char *pszMsg, int nChars)
{
#ifdef LAUNCHER_FIXES
	char outputStr[2048];
	WCHAR unicodeStr[1024];

	DWORD nSize = MultiByteToWideChar(CP_UTF8, 0, pszMsg, -1, NULL, 0);
	if (nSize > sizeof(unicodeStr))
		return;

	MultiByteToWideChar(CP_UTF8, 0, pszMsg, -1, unicodeStr, nSize);
	DWORD nLength = WideCharToMultiByte(CP_OEMCP, 0, unicodeStr, -1, 0, 0, NULL, NULL);
	if (nLength > sizeof(outputStr))
		return;

	WideCharToMultiByte(CP_OEMCP, 0, unicodeStr, -1, outputStr, nLength, NULL, NULL);
	WriteFile(houtput, outputStr, nChars ? nChars : Q_strlen(outputStr), NULL, NULL);
#else
	WriteFile(houtput, pszMsg, nChars ? nChars : Q_strlen(pszMsg), NULL, NULL);
#endif
}

void CTextConsoleWin32::Echo(char *pszMsg, int nChars)
{
	PrintRaw(pszMsg, nChars);
}

int CTextConsoleWin32::GetWidth()
{
	CONSOLE_SCREEN_BUFFER_INFO csbi;
	int nWidth = 0;

	if (GetConsoleScreenBufferInfo(houtput, &csbi)) {
		nWidth = csbi.dwSize.X;
	}

	if (nWidth <= 1)
		nWidth = 80;

	return nWidth;
}

void CTextConsoleWin32::SetStatusLine(char *pszStatus)
{
	Q_strncpy(statusline, pszStatus, sizeof(statusline) - 1);
	statusline[sizeof(statusline) - 2] = '\0';
	UpdateStatus();
}

void CTextConsoleWin32::UpdateStatus()
{
	COORD coord;
	DWORD dwWritten = 0;
	WORD wAttrib[ 80 ];

	for (int i = 0; i < 80; i++)
	{
		wAttrib[i] = Attrib; // FOREGROUND_GREEN | FOREGROUND_INTENSITY | BACKGROUND_INTENSITY;
	}

	coord.X = coord.Y = 0;

	WriteConsoleOutputAttribute(houtput, wAttrib, 80, coord, &dwWritten);
	WriteConsoleOutputCharacter(houtput, statusline, 80, coord, &dwWritten);
}

void CTextConsoleWin32::SetTitle(char *pszTitle)
{
	SetConsoleTitle(pszTitle);
}

void CTextConsoleWin32::SetColor(WORD attrib)
{
	Attrib = attrib;
}

#endif // defined(_WIN32)