/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * $URL$
 * $Id$
 *
 */

//#define PSP_KB_SHELL	/* Need a hack to properly load the keyboard from the PSP shell */

#ifdef PSP_KB_SHELL
#define PSP_KB_SHELL_PATH 	"ms0:/psp/game4xx/scummvm-solid/"	/* path to kbd.zip */
#endif


#include <malloc.h>
#include <pspkernel.h>
#include <png.h>

#include "backends/platform/psp/psppixelformat.h"
#include "backends/platform/psp/pspkeyboard.h"
#include "common/keyboard.h"
#include "common/fs.h"
#include "common/unzip.h"

//#define __PSP_DEBUG_FUNCS__	/* For debugging the stack */
//#define __PSP_DEBUG_PRINT__

#include "backends/platform/psp/trace.h"

#define PSP_SCREEN_WIDTH 480
#define PSP_SCREEN_HEIGHT 272
#define K(x)	((short)(Common::KEYCODE_INVALID + (x)))
#define C(x)	((short)(Common::KEYCODE_##x))

// Layout of the keyboard: Order for the boxes is clockwise and then middle:
//			1
//		4	5	2
//			3
// and order of letters is clockwise in each box, plus 2 top letters:
//		e		f
//			a
//		d		b
//			c
// K(x) is used for ascii values. C(x) is used for keys without ascii values
short PSPKeyboard::_modeChar[MODE_COUNT][5][6] = {
	{	//standard letters
		{ K('a'),  K('b'), K('c'), K('d'), K('f'), K('g') },
		{ K('h'),  K('i'), K('l'), K('m'), K('j'), K('k') },
		{ K('o'),  K('n'), K('r'), K('s'), K('p'), K('q') },
		{ K('u'),  K('v'), K('w'), K('y'), K('x'), K('z') },
		{ K('\b'), K('t'), K(' '), K('e'), K(0),   K(0)   }
	},
	{	//capital letters
		{ K('A'),  K('B'), K('C'), K('D'), K('F'), K('G') },
		{ K('H'),  K('I'), K('L'), K('M'), K('J'), K('K') },
		{ K('O'),  K('N'), K('R'), K('S'), K('P'), K('Q') },
		{ K('U'),  K('V'), K('W'), K('Y'), K('X'), K('Z') },
		{ K('\b'), K('T'), K(' '), K('E'), K(0),   K(0)   }
	},
	{	//numbers
		{ K('1'),  K('2'), K('3'), K('4'), K(0),   K(0)   },
		{ C(F5),   C(F8),  C(F7),  C(F6),  C(F9),  C(F10) },
		{ K('5'),  K('6'), K('7'), K('8'), K(0),   K(0)   },
		{ C(F1),   C(F4),  C(F3),  C(F2),  K(0),   K(0)   },
		{ K('\b'), K('0'), K(' '), K('9'), K(0),   K(0)   }
	},
	{	//symbols
		{ K('!'),  K(')'), K('?'), K('('), K('<'), K('>') },
		{ K('+'),  K('/'), K('='), K('\\'), K('\''), K('"') },
		{ K(':'),  K(']'), K(';'), K('['), K('@'), K('#') },
		{ K('-'),  K('}'), K('_'), K('{'), K('*'), K('$') },
		{ K('\b'), K('.'), K(' '), K(','), K(0),   K(0)   }
	}
};

// Read function for png library to be able to read from our SeekableReadStream
//
void pngReadStreamRead(png_structp png_ptr, png_bytep data, png_size_t length) {
	Common::SeekableReadStream *file;

	file = (Common::SeekableReadStream *)png_ptr->io_ptr;

	file->read(data, length);
}

// Array with file names
const char *PSPKeyboard::_guiStrings[] = {
	"keys4.png", "keys_s4.png",
	"keys_c4.png", "keys_s_c4.png",
	"nums4.png", "nums_s4.png",
	"syms4.png", "syms_s4.png"
};

// Constructor
PSPKeyboard::PSPKeyboard() {
	DEBUG_ENTER_FUNC();

	_init = false;			// we're not initialized yet
	_prevButtons = 0;		// Reset previous buttons
	_dirty = false;        	// keyboard needs redrawing
	_mode = 0;              // charset selected. (0: letters, 1: uppercase 2: numbers 3: symbols)
	_oldCursor = kCenter;	// Center cursor by default
	_movedX = 20;			// Default starting location
	_movedY = 50;
	_moved = false;			// Keyboard wasn't moved recently
	_state = kInvisible;	// We start invisible
	_lastState = kInvisible;

	// Constant renderer settings
	_renderer.setAlphaBlending(true);
	_renderer.setColorTest(false);
	_renderer.setUseGlobalScaler(false);
}

// Destructor
PSPKeyboard::~PSPKeyboard() {
	DEBUG_ENTER_FUNC();

	if (!_init) {
		return;
	}

	for (int i = 0; i < guiStringsSize; i++) {
		_buffers[i].deallocate();
		_palettes[i].deallocate();
	}
	_init = false;
}

void PSPKeyboard::setVisible(bool val) {
	if (val && _state == kInvisible && _init) {	// Check also that were loaded correctly
		_lastState = _state;
		_state = kMove;
	} else if (!val && _state != kInvisible) {
		_lastState = _state;
		_state = kInvisible;
	}
	setDirty();
}

/* move the position the keyboard is currently drawn at */
void PSPKeyboard::moveTo(const int newX, const int newY) {
	DEBUG_ENTER_FUNC();

	_movedX = newX;
	_movedY = newY;
	setDirty();
}

/* move the position the keyboard is currently drawn at */
void PSPKeyboard::increaseKeyboardLocationX(int amount) {
	DEBUG_ENTER_FUNC();

	int newX = _movedX + amount;

	if (newX > PSP_SCREEN_WIDTH - 5 || newX < 0 - 140) {	// clamp
		return;
	}
	_movedX = newX;
	setDirty();
}

/* move the position the keyboard is currently drawn at */
void PSPKeyboard::increaseKeyboardLocationY(int amount) {
	DEBUG_ENTER_FUNC();

	int newY = _movedY + amount;

	if (newY > PSP_SCREEN_HEIGHT - 5 || newY < 0 - 140)	{ // clamp
		return;
	}
	_movedY = newY;
	setDirty();
}

/* draw the keyboard at the current position */
void PSPKeyboard::render() {
	DEBUG_ENTER_FUNC();

	unsigned int currentBuffer = _mode << 1;

	// Draw the background letters
	// Set renderer to current buffer & palette
	_renderer.setBuffer(&_buffers[currentBuffer]);
	_renderer.setPalette(&_palettes[currentBuffer]);
	_renderer.setOffsetOnScreen(_movedX, _movedY);
	_renderer.setOffsetInBuffer(0, 0);
	_renderer.setDrawWholeBuffer();
	_renderer.render();

	// Get X and Y coordinates for the orange block
	int x, y;
	convertCursorToXY(_oldCursor, x, y);

	const int OrangeBlockSize = 64;
	const int GrayBlockSize = 43;

	// Draw the current Highlighted Selector (orange block)
	_renderer.setBuffer(&_buffers[currentBuffer + 1]);
	_renderer.setPalette(&_palettes[currentBuffer + 1]);
	_renderer.setOffsetOnScreen(_movedX + (x * GrayBlockSize), _movedY + (y * GrayBlockSize));
	_renderer.setOffsetInBuffer(x * OrangeBlockSize, y * OrangeBlockSize);
	_renderer.setDrawSize(OrangeBlockSize, OrangeBlockSize);
	_renderer.render();
}

inline void PSPKeyboard::convertCursorToXY(CursorDirections cur, int &x, int &y) {
	switch (cur) {
	case kUp:
		x = 1;
		y = 0;
		break;
	case kRight:
		x = 2;
		y = 1;
		break;
	case kDown:
		x = 1;
		y = 2;
		break;
	case kLeft:
		x = 0;
		y = 1;
		break;
	default:
		x = 1;
		y = 1;
		break;
	}
}

/* load the keyboard into memory */
bool PSPKeyboard::load() {
	DEBUG_ENTER_FUNC();

	if (_init) {
		PSP_DEBUG_PRINT("keyboard already loaded into memory\n");
		return true;
	}

	// For the shell, we must use a hack
#ifdef PSP_KB_SHELL
	Common::FSNode node(PSP_KB_SHELL_PATH);
#else /* normal mode */
	Common::FSNode node(".");				// Look in current directory
#endif
	PSP_DEBUG_PRINT("path[%s]\n", node.getPath().c_str());

	Common::Archive *fileArchive = NULL;
	Common::Archive *zipArchive = NULL;
	Common::SeekableReadStream * file = 0;

	if (node.getChild("kbd").exists() && node.getChild("kbd").isDirectory()) {
		PSP_DEBUG_PRINT("found directory ./kbd\n");
		fileArchive = new Common::FSDirectory(node.getChild("kbd"));
	}
	if (node.getChild("kbd.zip").exists()) {
		PSP_DEBUG_PRINT("found kbd.zip\n");
		zipArchive  = Common::makeZipArchive(node.getChild("kbd.zip"));
	}

	int i;

	// Loop through all png images
	for (i = 0; i < guiStringsSize; i++) {
		uint32 height = 0, width = 0, paletteSize = 0;

		PSP_DEBUG_PRINT("Opening %s.\n", _guiStrings[i]);

		// Look for the file in the kbd directory
		if (fileArchive && fileArchive->hasFile(_guiStrings[i])) {
			PSP_DEBUG_PRINT("found it in kbd directory.\n");

			file = fileArchive->createReadStreamForMember(_guiStrings[i]);
			if (!file) {
				PSP_ERROR("Can't open kbd/%s for keyboard. No keyboard will load.\n", _guiStrings[i]);
				goto ERROR;
			}
		}
		// We didn't find it. Look for it in the zip file
		else if (zipArchive && zipArchive->hasFile(_guiStrings[i])) {
			PSP_DEBUG_PRINT("found it in kbd.zip.\n");

			file = zipArchive->createReadStreamForMember(_guiStrings[i]);
			if (!file) {
				PSP_ERROR("Can't open %s in kbd.zip for keyboard. No keyboard will load.\n", _guiStrings[i]);
				goto ERROR;
			}
		} else {	// Couldn't find the file
			PSP_ERROR("Can't find %s for keyboard. No keyboard will load.\n", _guiStrings[i]);
			goto ERROR;
		}

		if (getPngImageSize(file, &width, &height, &paletteSize) == 0) { // Check image size and palette size
			// Allocate memory for image
			PSP_DEBUG_PRINT("width[%d], height[%d], paletteSize[%d]\n", width, height, paletteSize);
			_buffers[i].setSize(width, height, Buffer::kSizeByTextureSize);

			if (paletteSize) {	// 8 or 4-bit image
				if (paletteSize <= 16) { // 4 bit
					_buffers[i].setPixelFormat(PSPPixelFormat::Type_Palette_4bit);
					_palettes[i].setPixelFormats(PSPPixelFormat::Type_4444, PSPPixelFormat::Type_Palette_4bit);
					paletteSize = 16;
				} else if (paletteSize <= 256) {			// 8-bit image
					paletteSize = 256;
					_buffers[i].setPixelFormat(PSPPixelFormat::Type_Palette_8bit);
					_palettes[i].setPixelFormats(PSPPixelFormat::Type_4444, PSPPixelFormat::Type_Palette_8bit);
				} else {
					PSP_ERROR("palette of %d too big!\n", paletteSize);
					goto ERROR;
				}

			} else {				// 32-bit image
				_buffers[i].setPixelFormat(PSPPixelFormat::Type_8888);
			}

			_buffers[i].allocate();
			_palettes[i].allocate();

			// Try to load the image
			file->seek(0);	// Go back to start

			if (loadPngImage(file, _buffers[i], _palettes[i]) != 0)
				goto ERROR;
			else {	// Success
				PSP_DEBUG_PRINT("Managed to load the image\n");

				if (paletteSize == 16)		// 4-bit
					_buffers[i].flipNibbles();

				delete file;
			}
		} else {
			PSP_ERROR("couldn't obtain PNG image size\n");
			goto ERROR;
		}
	} /* for loop */

	_init = true;

	delete fileArchive;
	delete zipArchive;

	return true;

ERROR:

	delete file;
	delete fileArchive;
	delete zipArchive;

	for (int j = 0; j < i; j++) {
		_buffers[j].deallocate();
		_palettes[j].deallocate();
	}
	_init = false;

	return false;
}

static void user_warning_fn(png_structp png_ptr, png_const_charp warning_msg) {
	// ignore PNG warnings
}

/* Get the width and height of a png image */
int PSPKeyboard::getPngImageSize(Common::SeekableReadStream *file, uint32 *png_width, uint32 *png_height, u32 *paletteSize) {
	DEBUG_ENTER_FUNC();

	png_structp png_ptr;
	png_infop info_ptr;
	unsigned int sig_read = 0;
	png_uint_32 width, height;
	int bit_depth, color_type, interlace_type;

	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if (png_ptr == NULL) {
		return -1;
	}
	png_set_error_fn(png_ptr, (png_voidp) NULL, (png_error_ptr) NULL, user_warning_fn);
	info_ptr = png_create_info_struct(png_ptr);
	if (info_ptr == NULL) {
		png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
		return -1;
	}
	// Set the png lib to use our read function
	png_set_read_fn(png_ptr, (void *)file, pngReadStreamRead);

	png_set_sig_bytes(png_ptr, sig_read);
	png_read_info(png_ptr, info_ptr);
	png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, int_p_NULL, int_p_NULL);
	if (color_type & PNG_COLOR_MASK_PALETTE)
		*paletteSize = info_ptr->num_palette;
	else
		*paletteSize = 0;

	png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);

	*png_width = width;
	*png_height = height;

	return 0;
}

// Load a texture from a png image
//
int PSPKeyboard::loadPngImage(Common::SeekableReadStream *file, Buffer &buffer, Palette &palette) {
	DEBUG_ENTER_FUNC();

	png_structp png_ptr;
	png_infop info_ptr;
	unsigned int sig_read = 0;
	png_uint_32 width, height;
	int bit_depth, color_type, interlace_type;
	size_t y;

	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if (png_ptr == NULL) {
		PSP_ERROR("Couldn't create read struct to load keyboard\n");
		return -1;
	}
	// Use dummy error function
	png_set_error_fn(png_ptr, (png_voidp) NULL, (png_error_ptr) NULL, user_warning_fn);

	info_ptr = png_create_info_struct(png_ptr);
	if (info_ptr == NULL) {
		PSP_ERROR("Couldn't create info struct to load keyboard\n");
		png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
		return -1;
	}

	// Set the png lib to use our customized read function
	png_set_read_fn(png_ptr, (void *)file, pngReadStreamRead);

	png_set_sig_bytes(png_ptr, sig_read);
	png_read_info(png_ptr, info_ptr);
	png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, int_p_NULL, int_p_NULL);

	// Strip off 16 bit channels. Not really needed but whatever
	png_set_strip_16(png_ptr);

	if (color_type == PNG_COLOR_TYPE_PALETTE) {
		// Copy the palette
		png_colorp srcPal = info_ptr->palette;
		for (int i = 0; i < info_ptr->num_palette; i++) {
			unsigned char alphaVal = (i < info_ptr->num_trans) ? info_ptr->trans[i] : 0xFF;	// Load alpha if it's there
			palette.setSingleColorRGBA(i, srcPal->red, srcPal->green, srcPal->blue, alphaVal);
			srcPal++;
		}
	} else {	// Not a palettized image
		// Round up grayscale images
		if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_gray_1_2_4_to_8(png_ptr);
		// Convert trans channel to alpha for 32 bits
		if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr);
		// Filler for alpha?
		png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
	}

	unsigned char *line = (unsigned char*) malloc(info_ptr->rowbytes);
	if (!line) {
		png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
		PSP_ERROR("Couldn't allocate line\n");
		return -1;
	}

	for (y = 0; y < height; y++) {
		png_read_row(png_ptr, line, png_bytep_NULL);
		buffer.copyFromRect(line, info_ptr->rowbytes, 0, y, width, 1);	// Copy into buffer
		//memcpy(buffer.getPixels()[y * buffer.getWidthInBytes()], line, info_ptr->rowbytes);
	}

	free(line);

	png_read_end(png_ptr, info_ptr);
	png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);

	return 0;
}

// Defines for working with PSP buttons
#define CHANGED(x)	 (_buttonsChanged & (x))
#define PRESSED(x)   ((_buttonsChanged & (x)) && (pad.Buttons & (x)))
#define UNPRESSED(x) ((_buttonsChanged & (x)) && !(pad.Buttons & (x)))
#define DOWN(x)		 (pad.Buttons & (x))
#define UP(x)		 (!(pad.Buttons & (x)))
#define PSP_DPAD	 (PSP_CTRL_DOWN|PSP_CTRL_UP|PSP_CTRL_LEFT|PSP_CTRL_RIGHT)
#define PSP_4BUTTONS (PSP_CTRL_CROSS | PSP_CTRL_CIRCLE | PSP_CTRL_TRIANGLE | PSP_CTRL_SQUARE)

/*
 *  Attempts to read a character from the controller
 *  Uses the state machine.
 *  returns whether we have an event
 */
bool PSPKeyboard::processInput(Common::Event &event, SceCtrlData &pad) {
	DEBUG_ENTER_FUNC();

	bool haveEvent = false;		// Whether we have an event for the event manager to process
	event.kbd.flags = 0;

	_buttonsChanged = _prevButtons ^ pad.Buttons;

	if (!_init)					// In case we never had init
		return false;
	if (_state == kInvisible)	// Return if we're invisible
		return false;
	if (_state != kMove && PRESSED(PSP_CTRL_SELECT)) {
		_lastState = _state;
		_state = kMove;			// Check for move or visible state
	} else if (CHANGED(PSP_CTRL_START)) {		// Handle start button: enter, make KB invisible
		event.kbd.ascii = '\r';
		event.kbd.keycode = Common::KEYCODE_RETURN;
		event.type = DOWN(PSP_CTRL_START) ? Common::EVENT_KEYDOWN : Common::EVENT_KEYUP;
		haveEvent = true;
		_dirty = true;
		if (UP(PSP_CTRL_START))
			_state = kInvisible;			// Make us invisible if unpressed
	}
	// Check for being in state of moving the keyboard onscreen or pressing select
	else if (_state == kMove)
		handleMoveState(pad);
	else if (_state == kDefault)
		haveEvent = handleDefaultState(event, pad);
	else if (_state == kCornersSelected)
		haveEvent = handleCornersSelectedState(event, pad);
	else if (_state == kRTriggerDown)
		handleRTriggerDownState(pad);	// Deal with trigger states
	else if (_state == kLTriggerDown)
		handleLTriggerDownState(pad);	// Deal with trigger states

	_prevButtons = pad.Buttons;

	return haveEvent;
}

void PSPKeyboard::handleMoveState(SceCtrlData &pad) {
	DEBUG_ENTER_FUNC();
	if (UP(PSP_CTRL_SELECT)) {
		// Toggle between visible and invisible
		_state = (_lastState == kInvisible) ? kDefault : kInvisible;
		_dirty = true;

		if (_moved) {					// We moved the keyboard. Keep the keyboard onscreen anyway
			_state = kDefault;
			_moved = false;				// reset moved flag
		}
	} else if (DOWN(PSP_DPAD)) {		// How we move the KB onscreen
		_moved = true;
		_dirty = true;

		if (DOWN(PSP_CTRL_DOWN))
			increaseKeyboardLocationY(5);
		else if (DOWN(PSP_CTRL_UP))
			increaseKeyboardLocationY(-5);
		else if (DOWN(PSP_CTRL_LEFT))
			increaseKeyboardLocationX(-5);
		else  /* DOWN(PSP_CTRL_RIGHT) */
			increaseKeyboardLocationX(5);
	}
}

bool PSPKeyboard::handleDefaultState(Common::Event &event, SceCtrlData &pad) {
	DEBUG_ENTER_FUNC();
	bool haveEvent = false;

	if (PRESSED(PSP_CTRL_LTRIGGER)) 			// Don't say we used up the input
		_state = kLTriggerDown;
	else if (PRESSED(PSP_CTRL_RTRIGGER)) 	// Don't say we used up the input
		_state = kRTriggerDown;
	else if (CHANGED(PSP_4BUTTONS))			// We only care about the 4 buttons
		haveEvent = getInputChoice(event, pad);
	else if (!DOWN(PSP_4BUTTONS))				// Must be up to move cursor
		getCursorMovement(pad);

	return haveEvent;
}

bool PSPKeyboard::handleCornersSelectedState(Common::Event &event, SceCtrlData &pad) {
	DEBUG_ENTER_FUNC();
	// We care about 4 buttons + triggers (for letter selection)
	bool haveEvent = false;

	if (CHANGED(PSP_4BUTTONS | PSP_CTRL_RTRIGGER | PSP_CTRL_LTRIGGER))
		haveEvent = getInputChoice(event, pad);
	if (!DOWN(PSP_4BUTTONS | PSP_CTRL_RTRIGGER | PSP_CTRL_LTRIGGER)) // Must be up to move cursor
		getCursorMovement(pad);

	return haveEvent;
}

bool PSPKeyboard::getInputChoice(Common::Event &event, SceCtrlData &pad) {
	DEBUG_ENTER_FUNC();
	int innerChoice;
	bool haveEvent = false;

	if (UNPRESSED(PSP_CTRL_TRIANGLE)) {
		innerChoice = 0;
		event.type = Common::EVENT_KEYUP;			// We give priority to key_up
	} else if (UNPRESSED(PSP_CTRL_CIRCLE)) {
		innerChoice = 1;
		event.type = Common::EVENT_KEYUP;			// We give priority to key_up
	} else if (UNPRESSED(PSP_CTRL_CROSS)) {
		innerChoice = 2;
		event.type = Common::EVENT_KEYUP;			// We give priority to key_up
	} else if (UNPRESSED(PSP_CTRL_SQUARE)) {
		innerChoice = 3;
		event.type = Common::EVENT_KEYUP;			// We give priority to key_up
	} else if (UNPRESSED(PSP_CTRL_LTRIGGER) && _state == kCornersSelected) {
		innerChoice = 4;
		event.type = Common::EVENT_KEYUP;			// We give priority to key_up
	} else if (UNPRESSED(PSP_CTRL_RTRIGGER) && _state == kCornersSelected) {
		innerChoice = 5;
		event.type = Common::EVENT_KEYUP;			// We give priority to key_up
	} else if (PRESSED(PSP_CTRL_TRIANGLE)) {
		innerChoice = 0;
		event.type = Common::EVENT_KEYDOWN;
	} else if (PRESSED(PSP_CTRL_CIRCLE)) {
		innerChoice = 1;
		event.type = Common::EVENT_KEYDOWN;
	} else if (PRESSED(PSP_CTRL_CROSS)) {
		innerChoice = 2;
		event.type = Common::EVENT_KEYDOWN;
	} else if (PRESSED(PSP_CTRL_SQUARE)) {
		innerChoice = 3;
		event.type = Common::EVENT_KEYDOWN;
	} else if (PRESSED(PSP_CTRL_LTRIGGER) && _state == kCornersSelected) {
		innerChoice = 4;
		event.type = Common::EVENT_KEYDOWN;			// We give priority to key_up
	} else { /* (PRESSED(PSP_CTRL_RTRIGGER)) && _state == kCornersSelected */
		innerChoice = 5;
		event.type = Common::EVENT_KEYDOWN;			// We give priority to key_up
	}

#define IS_UPPERCASE(x)	((x) >= (unsigned short)'A' && (x) <= (unsigned short)'Z')
#define TO_LOWER(x)		((x) += 'a'-'A')

	//Now grab the value out of the array
	short choice = _modeChar[_mode][_oldCursor][innerChoice];

	event.kbd.ascii = choice <= 255 ? choice : 0;

	// Handle upper-case which is missing in Common::KeyCode
	if (IS_UPPERCASE(choice)) {
		event.kbd.keycode = (Common::KeyCode) TO_LOWER(choice);
		event.kbd.flags = Common::KBD_SHIFT;
	} else
		event.kbd.keycode = (Common::KeyCode) choice;

	haveEvent = (choice != Common::KEYCODE_INVALID) ? true : false;	// We have an event/don't if it's invalid

	return haveEvent;
}

void PSPKeyboard::getCursorMovement(SceCtrlData &pad) {
	DEBUG_ENTER_FUNC();
	CursorDirections cursor;

	// Find where the cursor is pointing
	cursor = kCenter;
	_state = kDefault;

	if (DOWN(PSP_DPAD)) {
		_state = kCornersSelected;

		if (DOWN(PSP_CTRL_UP))
			cursor = kUp;
		else if (DOWN(PSP_CTRL_RIGHT))
			cursor = kRight;
		else if (DOWN(PSP_CTRL_DOWN))
			cursor = kDown;
		else if (DOWN(PSP_CTRL_LEFT))
			cursor = kLeft;
	}

	if (cursor != _oldCursor) { //If we've moved, update dirty and return
		_dirty = true;
		_oldCursor = cursor;
	}
}

void PSPKeyboard::handleLTriggerDownState(SceCtrlData &pad) {
	DEBUG_ENTER_FUNC();
	if (UNPRESSED(PSP_CTRL_LTRIGGER)) {
		_dirty = true;

		if (_mode < 2)
			_mode = 2;
		else
			_mode = (_mode == 2) ? 3 : 2;

		_state = kDefault;
	}
}

void PSPKeyboard::handleRTriggerDownState(SceCtrlData &pad) {
	DEBUG_ENTER_FUNC();
	if (UNPRESSED(PSP_CTRL_RTRIGGER)) {
		_dirty = true;

		if (_mode > 1)
			_mode = 0;
		else
			_mode = (_mode == 0) ? 1 : 0;

		_state = kDefault;
	}
}