/******************************************************************************

	COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.

	This file is part of Sortix.

	Sortix 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 3 of the License, or (at your option) any later
	version.

	Sortix 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 Sortix. If not, see <http://www.gnu.org/licenses/>.

	keyboard.cpp
	A driver for the PS2 Keyboard.

******************************************************************************/

#include "platform.h"
#include "globals.h"
#include "iprintable.h"
#include "iirqhandler.h"
#include "log.h"
#include "panic.h"
#include "keyboard.h"
#include "isr.h"

namespace Sortix
{
	DEFINE_GLOBAL_OBJECT(Keyboard, Keyboard);

	namespace Layouts
	{
		const uint32_t UNKNOWN = 0xFFFFFFFF;
		const uint32_t ESC = 0xFFFFFFFF - 1;
		const uint32_t CTRL = 0xFFFFFFFF - 2;
		const uint32_t LSHFT = 0xFFFFFFFF - 3;
		const uint32_t RSHFT = 0xFFFFFFFF - 4;
		const uint32_t ALT = 0xFFFFFFFF - 5;
		const uint32_t F1 = 0xFFFFFFFF - 6;
		const uint32_t F2 = 0xFFFFFFFF - 7;
		const uint32_t F3 = 0xFFFFFFFF - 8;
		const uint32_t F4 = 0xFFFFFFFF - 9;
		const uint32_t F5 = 0xFFFFFFFF - 10;
		const uint32_t F6 = 0xFFFFFFFF - 11;
		const uint32_t F7 = 0xFFFFFFFF - 12;
		const uint32_t F8 = 0xFFFFFFFF - 13;
		const uint32_t F9 = 0xFFFFFFFF - 14;
		const uint32_t F10 = 0xFFFFFFFF - 15;
		const uint32_t F11 = 0xFFFFFFFF - 16;
		const uint32_t F12 = 0xFFFFFFFF - 17;
		const uint32_t SCRLCK = 0xFFFFFFFF - 18;
		const uint32_t HOME = 0xFFFFFFFF - 19;
		const uint32_t UP = 0xFFFFFFFF - 20;
		const uint32_t LEFT = 0xFFFFFFFF - 21;
		const uint32_t RIGHT = 0xFFFFFFFF - 22;
		const uint32_t DOWN = 0xFFFFFFFF - 23;
		const uint32_t PGUP = 0xFFFFFFFF - 24;
		const uint32_t PGDOWN = 0xFFFFFFFF - 25;
		const uint32_t END = 0xFFFFFFFF - 26;
		const uint32_t INS = 0xFFFFFFFF - 27;
		const uint32_t DEL = 0xFFFFFFFF - 28;
		const uint32_t CAPS = 0xFFFFFFFF - 29;
		const uint32_t NONE = 0xFFFFFFFF - 30;
		const uint32_t ALTGR = 0xFFFFFFFF - 31;

		namespace US
		{
			uint32_t sg[128] =
			{
				UNKNOWN,
				ESC,
				'1',
				'2',
				'3',
				'4',
				'5',
				'6',
				'7',
				'8',
				'9',
				'0',
				'-',
				'=',
				'\b',
				'\t',
				'q',
				'w',
				'e',
				'r',
				't',
				'y',
				'u',
				'i',
				'o',
				'p',
				'[',
				']',
				'\n',
				CTRL,
				'a',
				's',
				'd',
				'f',
				'g',
				'h',
				'j',
				'k',
				'l',
				';',
				'\'',
				'`',
				LSHFT,
				'\\',
				'z',
				'x',
				'c',
				'v',
				'b',
				'n',
				'm',
				',',
				'.',
				'/',
				RSHFT,
				'*',
				ALT,
				' ',
				CAPS,
				F1,
				F2,
				F3,
				F4,
				F5,
				F6,
				F7,
				F8,
				F9,
				F10,
				SCRLCK,
				HOME,
				UP,
				PGUP,
				'-',
				LEFT,
				UNKNOWN,
				RIGHT,
				'+',
				RIGHT,
				END,
				DOWN,
				PGDOWN,
				INS,
				DEL,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				F11,
				F12,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
			};

			uint32_t Sg[128] =
			{
				UNKNOWN,
				ESC,
				'!',
				'@',
				'#',
				'$',
				'%',
				'^',
				'&',
				'*',
				'(',
				')',
				'_',
				'+',
				'\b',
				'\t', // TODO: Make it de-tab!
				'Q',
				'W',
				'E',
				'R',
				'T',
				'Y',
				'U',
				'I',
				'O',
				'P',
				'{',
				'}',
				'\n',
				CTRL,
				'A',
				'S',
				'D',
				'F',
				'G',
				'H',
				'J',
				'K',
				'L',
				':',
				'"',
				'~',
				LSHFT,
				'|',
				'Z',
				'X',
				'C',
				'V',
				'B',
				'N',
				'M',
				'<',
				'>',
				'?',
				RSHFT,
				'*',
				ALT,
				' ',
				CAPS,
				F1,
				F2,
				F3,
				F4,
				F5,
				F6,
				F7,
				F8,
				F9,
				F10,
				SCRLCK,
				HOME,
				UP,
				PGUP,
				'-',
				LEFT,
				UNKNOWN,
				RIGHT,
				'+',
				RIGHT,
				END,
				DOWN,
				PGDOWN,
				INS,
				DEL,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				F11,
				F12,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
			};

			uint32_t sG[128] =
			{
				UNKNOWN,
				ESC,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				'\b',
				'\t',
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				'\n',
				CTRL,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				LSHFT,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				RSHFT,
				UNKNOWN,
				ALT,
				' ',
				CAPS,
				F1,
				F2,
				F3,
				F4,
				F5,
				F6,
				F7,
				F8,
				F9,
				F10,
				SCRLCK,
				HOME,
				UP,
				PGUP,
				'-',
				LEFT,
				UNKNOWN,
				RIGHT,
				'+',
				RIGHT,
				END,
				DOWN,
				PGDOWN,
				INS,
				DEL,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				F11,
				F12,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
			};

			uint32_t SG[128] =
			{
				UNKNOWN,
				ESC,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				'\b',
				'\t',
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				'\n',
				CTRL,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				LSHFT,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				RSHFT,
				UNKNOWN,
				ALT,
				' ',
				CAPS,
				F1,
				F2,
				F3,
				F4,
				F5,
				F6,
				F7,
				F8,
				F9,
				F10,
				SCRLCK,
				HOME,
				UP,
				PGUP,
				'-',
				LEFT,
				UNKNOWN,
				RIGHT,
				'+',
				RIGHT,
				END,
				DOWN,
				PGDOWN,
				INS,
				DEL,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				F11,
				F12,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
				UNKNOWN,
			};

			uint32_t* Layout[4] = { sg, Sg, sG, SG }; 
		}

		uint32_t** Layout = US::Layout;

		nat Mask = 0;
		nat LockMask = 0;

		const nat Shift = (1<<0);
		const nat AltGr = (1<<1);
		const nat ScrLck = (1<<2);

		uint32_t GetCodePoint(uint8_t Scancode)
		{
			nat TableIndex = 0x3 & (Mask ^ LockMask);

			//GLog->PrintF("[m=%x,lm=%x,ti=%x]", Mask, LockMask, TableIndex); GLog->UpdateScreen();

			uint32_t CodePoint = Layout[TableIndex][Scancode & 0x7F];

			if ( CodePoint == UNKNOWN ) { return UNKNOWN; }
			if ( Scancode & 0x80 )
			{
				if ( CodePoint == LSHFT ) { Mask &= ~Shift; }
				if ( CodePoint == ALTGR ) { Mask &= ~AltGr; }
				if ( CodePoint == SCRLCK ) { Mask &= ~ScrLck; }
			}
			else
			{
				if ( CodePoint == LSHFT ) { Mask |= Shift; }
				if ( CodePoint == ALTGR ) { Mask |= AltGr; }
				if ( CodePoint == SCRLCK ) { Mask |= ScrLck; }
				if ( CodePoint == CAPS ) { LockMask ^= Shift; }
			}


			return CodePoint;
		}
	}

	void Keyboard::Init()
	{
		// Initialize variables.
		Reader = NULL;

		// Register our keystroke callback.
		register_interrupt_handler(IRQ1, this);		
	}

	void Keyboard::SetReader(IKeystrokable* NewReader)
	{
		Reader = NewReader;
	}

	void Keyboard::OnIRQ(InterruptRegisters* Regs)
	{
		// Read the scancode from the data register.
		uint8_t Scancode = X86::InPortB(0x60);

		GLog->PrintF("[%u]", Scancode); GLog->UpdateScreen();

		// TODO: We should pass this information along instead of filtering it.
		if ( Scancode & 0x80 ) { return; }

		uint32_t CodePoint = Layouts::GetCodePoint(Scancode);

		//if ( Reader != NULL ) { Reader->OnKeystroke(CodePoint); return; }

		// Use this to debug the exact scancodes you receive!
		//GLog->PrintF("[%u/U+%x]", Scancode, CodePoint); GLog->UpdateScreen();

		if ( CodePoint == Layouts::UNKNOWN ) { GLog->PrintF("^%u", Scancode); GLog->UpdateScreen(); return; }

		if ( CodePoint & (1<<31) ) { return; }

		// The high bit of the scancode is set if the key is released.
		if ( Scancode & 0x80 ) { return; }

		if ( CodePoint & 0xFFFFFF80 ) { GLog->PrintF("U+%x", CodePoint); GLog->UpdateScreen(); return; }

		GLog->PrintF("%s", &CodePoint); // Little endian hack!
		GLog->UpdateScreen();
	}
}

