/****************************************************************************
 * libwiigui
 *
 * Tantric 2009
 *
 * gui_keyboard.cpp
 *
 * GUI class definitions
 ***************************************************************************/

#include "gui.h"
#include "Tools/translate.h"

#define MAX_KEYBOARD_DISPLAY	35

static char tmptxt[MAX_KEYBOARD_DISPLAY +1];

static char * GetDisplayText(int pos, char * t)
{
	if(!t)
		return NULL;

	int len = strlen(t);

	if(len < MAX_KEYBOARD_DISPLAY)
		return t;

	strncpy(tmptxt, &t[pos], MAX_KEYBOARD_DISPLAY);
	tmptxt[MAX_KEYBOARD_DISPLAY] = 0;
	return &tmptxt[0];
}

/**
 * Constructor for the GuiKeyboard class.
 */

GuiKeyboard::GuiKeyboard(char * t, u32 max, bool Br)
{
	width = 540;
	height = 400;
	shift = 0;
	caps = 0;
	br = Br;
	selectable = true;
	focus = 0; // allow focus
	alignmentHor = ALIGN_CENTRE;
	alignmentVert = ALIGN_MIDDLE;
	strncpy(kbtextstr, t, max);
	kbtextstr[max] = 0;
	kbtextmaxlen = max;
	if(strlen(kbtextstr) > MAX_KEYBOARD_DISPLAY)
		letterPos = strlen(kbtextstr) - MAX_KEYBOARD_DISPLAY;
	else
		letterPos = 0;

	Key thekeys[4][11] = {
	{
		{'1','!'},
		{'2','@'},
		{'3','#'},
		{'4','$'},
		{'5','%'},
		{'6','^'},
		{'7','&'},
		{'8','*'},
		{'9','('},
		{'0',')'},
		{'\0','\0'}
	},
	{
		{'q','Q'},
		{'w','W'},
		{'e','E'},
		{'r','R'},
		{'t','T'},
		{'z','Z'},
		{'u','U'},
		{'i','I'},
		{'o','O'},
		{'p','P'},
		{'-','_'}
	},
	{
		{'a','A'},
		{'s','S'},
		{'d','D'},
		{'f','F'},
		{'g','G'},
		{'h','H'},
		{'j','J'},
		{'k','K'},
		{'l','L'},
		{';',':'},
		{'\'','"'}
	},

	{
		{'y','Y'},
		{'x','X'},
		{'c','C'},
		{'v','V'},
		{'b','B'},
		{'n','N'},
		{'m','M'},
		{',','<'},
		{'.','>'},
		{'/','?'},
		{'\0','\0'}
	}
	};
	memcpy(keys, thekeys, sizeof(thekeys));

	trigA = new GuiTrigger;
	trigA->SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A);

	keyTextbox = new GuiImageData(keyboard_textbox_png);
	keyTextboxImg = new GuiImage(keyTextbox);
	keyTextboxImg->SetAlignment(ALIGN_CENTRE, ALIGN_TOP);
	keyTextboxImg->SetPosition(0, 0);
	keyTextboxImg->SetSelectable(false);
	this->Append(keyTextboxImg);

	kbText = new GuiText(GetDisplayText(letterPos, kbtextstr), 20, (GXColor){0, 0, 0, 0xff});
	kbText->SetAlignment(ALIGN_CENTRE, ALIGN_TOP);
	kbText->SetPosition(0, 13);
	kbText->SetLetterPos(strlen(GetDisplayText(letterPos, kbtextstr)));
	kbText->SetSelectable(false);
	this->Append(kbText);

	textpointer = new GuiImageData(textpointer_img_png);
	textpointerImg = new GuiImage(textpointer);
	textpointerImg->SetAlignment(ALIGN_CENTRE, ALIGN_TOP);
	textpointerImg->SetPosition(-kbText->GetTextWidth()/2 + kbText->GetTextWidthToPosition(kbText->GetLetterPos()) +1, 10);
	textpointerImg->SetSelectable(false);
	this->Append(textpointerImg);

	textpointerBtn = new GuiButton(keyTextbox->GetWidth(), keyTextbox->GetHeight());
	textpointerBtn->SetAlignment(ALIGN_CENTRE, ALIGN_TOP);
	textpointerBtn->SetPosition(0, 0);
	textpointerBtn->SetTrigger(trigA);
	this->Append(textpointerBtn);

	key = new GuiImageData(keyboard_key_png);
	keyOver = new GuiImageData(keyboard_key_over_png);
	keyMedium = new GuiImageData(keyboard_mediumkey_png);
	keyMediumOver = new GuiImageData(keyboard_mediumkey_over_png);
	keyLarge = new GuiImageData(keyboard_largekey_png);
	keyLargeOver = new GuiImageData(keyboard_largekey_over_png);

//	keySoundOver = new GuiSound(button_over_pcm, button_over_pcm_size, SOUND_PCM);
//	keySoundClick = new GuiSound(button_click_pcm, button_click_pcm_size, SOUND_PCM);

	keyBrImg = new GuiImage(keyMedium);
	keyBrOverImg = new GuiImage(keyMediumOver);
	keyBrText = new GuiText(tr("Linebreak"), 20, (GXColor){0, 0, 0, 0xff});
	keyBr = new GuiButton(keyMedium->GetWidth(), keyMedium->GetHeight());
	keyBr->SetImage(keyBrImg);
	keyBr->SetImageOver(keyBrOverImg);
	keyBr->SetLabel(keyBrText);
//	keyBr->SetSoundOver(keySoundOver);
//	keyBr->SetSoundClick(keySoundClick);
	keyBr->SetTrigger(trigA);
	keyBr->SetPosition(0, 4*42+80);
	keyBr->SetEffectGrow();
	if(br)
		this->Append(keyBr);
	
	keyBackImg = new GuiImage(keyMedium);
	keyBackOverImg = new GuiImage(keyMediumOver);
	keyBackText = new GuiText("Back", 20, (GXColor){0, 0, 0, 0xff});
	keyBack = new GuiButton(keyMedium->GetWidth(), keyMedium->GetHeight());
	keyBack->SetImage(keyBackImg);
	keyBack->SetImageOver(keyBackOverImg);
	keyBack->SetLabel(keyBackText);
//	keyBack->SetSoundOver(keySoundOver);
//	keyBack->SetSoundClick(keySoundClick);
	keyBack->SetTrigger(trigA);
	keyBack->SetPosition(10*42+40, 0*42+80);
	keyBack->SetEffectGrow();
	this->Append(keyBack);

	keyCapsImg = new GuiImage(keyMedium);
	keyCapsOverImg = new GuiImage(keyMediumOver);
	keyCapsText = new GuiText("Caps", 20, (GXColor){0, 0, 0, 0xff});
	keyCaps = new GuiButton(keyMedium->GetWidth(), keyMedium->GetHeight());
	keyCaps->SetImage(keyCapsImg);
	keyCaps->SetImageOver(keyCapsOverImg);
	keyCaps->SetLabel(keyCapsText);
//	keyCaps->SetSoundOver(keySoundOver);
//	keyCaps->SetSoundClick(keySoundClick);
	keyCaps->SetTrigger(trigA);
	keyCaps->SetPosition(0, 2*42+80);
	keyCaps->SetEffectGrow();
	this->Append(keyCaps);

	keyShiftImg = new GuiImage(keyMedium);
	keyShiftOverImg = new GuiImage(keyMediumOver);
	keyShiftText = new GuiText("Shift", 20, (GXColor){0, 0, 0, 0xff});
	keyShift = new GuiButton(keyMedium->GetWidth(), keyMedium->GetHeight());
	keyShift->SetImage(keyShiftImg);
	keyShift->SetImageOver(keyShiftOverImg);
	keyShift->SetLabel(keyShiftText);
//	keyShift->SetSoundOver(keySoundOver);
//	keyShift->SetSoundClick(keySoundClick);
	keyShift->SetTrigger(trigA);
	keyShift->SetPosition(21, 3*42+80);
	keyShift->SetEffectGrow();
	this->Append(keyShift);

	keySpaceImg = new GuiImage(keyLarge);
	keySpaceOverImg = new GuiImage(keyLargeOver);
	keySpace = new GuiButton(keyLarge->GetWidth(), keyLarge->GetHeight());
	keySpace->SetImage(keySpaceImg);
	keySpace->SetImageOver(keySpaceOverImg);
//	keySpace->SetSoundOver(keySoundOver);
//	keySpace->SetSoundClick(keySoundClick);
	keySpace->SetTrigger(trigA);
	keySpace->SetPosition(0, 4*42+80);
	keySpace->SetAlignment(ALIGN_CENTRE, ALIGN_TOP);
	keySpace->SetEffectGrow();
	this->Append(keySpace);

	keyEmptyImg = new GuiImage(keyMedium);
	keyEmptyOverImg = new GuiImage(keyMediumOver);
	keyEmptyText = new GuiText(tr("Empty"), 20, (GXColor){0, 0, 0, 0xff});
	keyEmpty = new GuiButton(keyMedium->GetWidth(), keyMedium->GetHeight());
	keyEmpty->SetImage(keyEmptyImg);
	keyEmpty->SetImageOver(keyEmptyOverImg);
	keyEmpty->SetLabel(keyEmptyText);
//	keyEmpty->SetSoundOver(keySoundOver);
//	keyEmpty->SetSoundClick(keySoundClick);
	keyEmpty->SetTrigger(trigA);
	keyEmpty->SetPosition(10*42+40, 4*42+80);
	keyEmpty->SetEffectGrow();
	this->Append(keyEmpty);

	char txt[2] = { 0, 0 };

	for(int i=0; i<4; i++)
	{
		for(int j=0; j<11; j++)
		{
			if(keys[i][j].ch != '\0')
			{
				txt[0] = keys[i][j].ch;
				keyImg[i][j] = new GuiImage(key);
				keyImgOver[i][j] = new GuiImage(keyOver);
				keyTxt[i][j] = new GuiText(txt, 20, (GXColor){0, 0, 0, 0xff});
				keyTxt[i][j]->SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM);
				keyTxt[i][j]->SetPosition(0, -10);
				keyBtn[i][j] = new GuiButton(key->GetWidth(), key->GetHeight());
				keyBtn[i][j]->SetImage(keyImg[i][j]);
				keyBtn[i][j]->SetImageOver(keyImgOver[i][j]);
//				keyBtn[i][j]->SetSoundOver(keySoundOver);
//				keyBtn[i][j]->SetSoundClick(keySoundClick);
				keyBtn[i][j]->SetTrigger(trigA);
				keyBtn[i][j]->SetLabel(keyTxt[i][j]);
				keyBtn[i][j]->SetPosition(j*42+21*i+40, i*42+80);
				keyBtn[i][j]->SetEffectGrow();
				this->Append(keyBtn[i][j]);
			}
		}
	}
}

/**
 * Destructor for the GuiKeyboard class.
 */
GuiKeyboard::~GuiKeyboard()
{
	delete kbText;
	delete keyTextbox;
	delete keyTextboxImg;
	delete textpointer;
	delete textpointerImg;
	delete textpointerBtn;
	delete keyCapsText;
	delete keyCapsImg;
	delete keyCapsOverImg;
	delete keyCaps;
	delete keyShiftText;
	delete keyShiftImg;
	delete keyShiftOverImg;
	delete keyShift;
	if(br)
	{
		delete keyBrText;
		delete keyBrImg;
		delete keyBrOverImg;
		delete keyBr;
	}
	delete keyBackText;
	delete keyBackImg;
	delete keyBackOverImg;
	delete keyBack;
	delete keySpaceImg;
	delete keySpaceOverImg;
	delete keySpace;
	delete keyEmptyText;
	delete keyEmptyImg;
	delete keyEmptyOverImg;
	delete keyEmpty;
	delete key;
	delete keyOver;
	delete keyMedium;
	delete keyMediumOver;
	delete keyLarge;
	delete keyLargeOver;
//	delete keySoundOver;
//	delete keySoundClick;
	delete trigA;

	for(int i=0; i<4; i++)
	{
		for(int j=0; j<11; j++)
		{
			if(keys[i][j].ch != '\0')
			{
				delete keyImg[i][j];
				delete keyImgOver[i][j];
				delete keyTxt[i][j];
				delete keyBtn[i][j];
			}
		}
	}
}

void GuiKeyboard::Update(GuiTrigger * t)
{
	if(_elements.size() == 0 || (state == STATE_DISABLED && parentElement))
		return;

	for (u8 i = 0; i < _elements.size(); i++)
	{
		try	{ _elements.at(i)->Update(t); }
		catch (const std::exception& e) { }
	}

	bool update = false;
	
	if(keySpace->GetState() == STATE_CLICKED)
	{
		keySpace->SetState(STATE_SELECTED, t->chan);
		if(strlen(kbtextstr) < kbtextmaxlen)
		{
			for(int x = strlen(kbtextstr); x > kbText->GetLetterPos() + letterPos; x--)
				kbtextstr[x] = kbtextstr[x-1];
				
			kbtextstr[kbText->GetLetterPos() + letterPos] = ' ';
				
			if(strlen(kbtextstr) > MAX_KEYBOARD_DISPLAY)
				letterPos++;
			else
				kbText->SetLetterPos(kbText->GetLetterPos() +1);
				
			kbText->SetText(GetDisplayText(letterPos, kbtextstr));
			textpointerImg->SetPosition(-kbText->GetTextWidth()/2 + kbText->GetTextWidthToPosition(kbText->GetLetterPos()) +1, 10);
		}
	}
	else if(keyBack->GetState() == STATE_CLICKED)
	{
		keyBack->SetState(STATE_SELECTED, t->chan);
		if(kbText->GetLetterPos() > 0)
		{
			for(int i = kbText->GetLetterPos() + letterPos -1; i < (signed)strlen(kbtextstr) -1; i++)
				kbtextstr[i] = kbtextstr[i +1];
				
			kbtextstr[strlen(kbtextstr) -1] = 0;
			if(letterPos > 0)
			{
				letterPos--;
				kbText->SetText(GetDisplayText(letterPos, kbtextstr));
			}
			else
			{
				kbText->SetText(GetDisplayText(letterPos, kbtextstr));
				kbText->SetLetterPos(kbText->GetLetterPos()-1);
			}
			
			textpointerImg->SetPosition(-kbText->GetTextWidth()/2 + kbText->GetTextWidthToPosition(kbText->GetLetterPos()) +1, 10);
		}
	}
	else if(keyBr->GetState() == STATE_CLICKED)
	{
		keyBr->SetState(STATE_SELECTED, t->chan);
		if(strlen(kbtextstr) < kbtextmaxlen)
		{
			for(int x = strlen(kbtextstr); x > kbText->GetLetterPos() + letterPos; x--)
				kbtextstr[x] = kbtextstr[x-1];
				
			kbtextstr[kbText->GetLetterPos() + letterPos] = '¶';
				
			if(strlen(kbtextstr) > MAX_KEYBOARD_DISPLAY)
				letterPos++;
			else
				kbText->SetLetterPos(kbText->GetLetterPos() +1);
				
			kbText->SetText(GetDisplayText(letterPos, kbtextstr));
			textpointerImg->SetPosition(-kbText->GetTextWidth()/2 + kbText->GetTextWidthToPosition(kbText->GetLetterPos()) +1, 10);
		}
	}
	else if(keyEmpty->GetState() == STATE_CLICKED)
	{
		while (strlen(kbtextstr) > 0)
			kbtextstr[strlen(kbtextstr)-1] = 0;
			
		letterPos = 0;
		kbText->SetLetterPos(0);
		kbText->SetText(GetDisplayText(letterPos, kbtextstr));
		textpointerImg->SetPosition(0, 10);
		keyEmpty->SetState(STATE_SELECTED, t->chan);
	}
	else if(keyShift->GetState() == STATE_CLICKED)
	{
		shift ^= 1;
		keyShift->SetState(STATE_SELECTED, t->chan);
		update = true;
	}
	else if(keyCaps->GetState() == STATE_CLICKED)
	{
		caps ^= 1;
		keyCaps->SetState(STATE_SELECTED, t->chan);
		update = true;
	}
	else if(textpointerBtn->GetState() == STATE_CLICKED)
	{
		int pos = kbText->GetTextMaxWidth((int)t->wpad->ir.x-48 - (width - kbText->GetTextWidth()) /2);
		
		if(pos == 0)
			textpointerImg->SetPosition(-kbText->GetTextWidth()/2, 10);
		else
			textpointerImg->SetPosition(-kbText->GetTextWidth()/2 + pos + 1, 10);
		
		textpointerBtn->SetState(STATE_SELECTED, t->chan);
	}

	char txt[2] = { 0, 0 };

	startloop:

	for(int i=0; i<4; i++)
	{
		for(int j=0; j<11; j++)
		{
			if(keys[i][j].ch != '\0')
			{
				if(update)
				{
					if(shift || caps)
						txt[0] = keys[i][j].chShift;
					else
						txt[0] = keys[i][j].ch;

					keyTxt[i][j]->SetText(txt);
				}

				if(keyBtn[i][j]->GetState() == STATE_CLICKED)
				{
					keyBtn[i][j]->SetState(STATE_SELECTED, t->chan);
					if(strlen(kbtextstr) < kbtextmaxlen)
					{
						if(shift || caps)
						{
							for(int x = strlen(kbtextstr); x > kbText->GetLetterPos() + letterPos; x--)
								kbtextstr[x] = kbtextstr[x-1];
								
							kbtextstr[kbText->GetLetterPos() + letterPos] = keys[i][j].chShift;
								
							if(strlen(kbtextstr) > MAX_KEYBOARD_DISPLAY)
								letterPos++;
							else
								kbText->SetLetterPos(kbText->GetLetterPos() +1);
						}
						else
						{
							for(int x = strlen(kbtextstr); x > kbText->GetLetterPos() + letterPos; x--)
								kbtextstr[x] = kbtextstr[x-1];
								
							kbtextstr[kbText->GetLetterPos() + letterPos] = keys[i][j].ch;
								
							if(strlen(kbtextstr) > MAX_KEYBOARD_DISPLAY)
								letterPos++;
							else
								kbText->SetLetterPos(kbText->GetLetterPos() +1);
						}
					}
					kbText->SetText(GetDisplayText(letterPos, kbtextstr));
					textpointerImg->SetPosition(-kbText->GetTextWidth()/2 + kbText->GetTextWidthToPosition(kbText->GetLetterPos()) +1, 10);
					
					if(shift)
					{
						shift ^= 1;
						update = true;
						goto startloop;
					}
				}
			}
		}
	}
	this->ToggleFocus(t);

	if(focus) // only send actions to this window if it's in focus
	{
		if(textpointerBtn->GetState() == STATE_SELECTED)
		{
			bool changed = false;
			int pos = 0;
			if(t->Right())
			{
				changed = true;
				if(kbText->GetLetterPos() == MAX_KEYBOARD_DISPLAY && kbText->GetLetterPos() + (unsigned)letterPos < strlen(kbtextstr))
				{
					letterPos++;
					kbText->SetText(GetDisplayText(letterPos, kbtextstr));
				}
				pos = kbText->GetTextWidthToPosition(kbText->GetLetterPos() +1);
			}
			else if(t->Left())
			{
				changed = true;
				if(letterPos > 0 && kbText->GetLetterPos() == 0)
				{
					letterPos--;
					kbText->SetText(GetDisplayText(letterPos, kbtextstr));
				}
				pos = kbText->GetTextWidthToPosition(kbText->GetLetterPos() -1);
			}
			else if(t->Down())
				this->MoveSelectionVert(1);
			
			if(changed)
			{
				changed = false;
				if(pos == 0)
					textpointerImg->SetPosition(-kbText->GetTextWidth()/2, 10);
				else
					textpointerImg->SetPosition(-kbText->GetTextWidth()/2 + pos + 1, 10);
			}
		}
		else
		{
			// pad/joystick navigation
			if(t->Right())
				this->MoveSelectionHor(1);
			else if(t->Left())
				this->MoveSelectionHor(-1);
			else if(t->Down())
				this->MoveSelectionVert(1);
			else if(t->Up())
				this->MoveSelectionVert(-1);
		}
	}
}
