#include "lgi/common/Lgi.h"
#include "lgi/common/CssTools.h"
#include "lgi/common/Edit.h"
#include "lgi/common/Button.h"
#include "lgi/common/LgiRes.h"
#include "TouchKbd.h"
#include "mc4.h"

#define BTN_WID			60
#define BTN_HT			72
#define BTN_SPACING_X	10
#define BTN_SPACING_Y	17

enum Btns
{
	IDC_SWITCH_LETTERS = 300,
	IDC_SWITCH_NUMBERS,
	IDC_SWITCH_SYMBOLS,
	IDC_SHIFT
};

const char *BlackBtnStyle = "color: white; background: #666; no-paint-color: transparent;";
const char *ReturnBtnStyle = "color: white; background: #56a; no-paint-color: transparent;";

struct TouchKeyboardPriv
{
	TouchKeyboard::KbdMode Mode;
	TouchKeyboard::KbdView View;
	LEdit *e;
	LArray<LButton*> Btns;
	bool InNotify;
	bool Shifted;
	LAutoPtr<LSurface> Icons;
	
	TouchKeyboardPriv(const char *ResourcePath)
	{
		InNotify = false;
		Shifted = false;
		e = NULL;
		
		LFile::Path p = ResourcePath;
		p += "icons.png";
		Icons.Reset(GdcD->Load(p));
		if (!Icons)
		{
			LgiTrace("%s:%i - Couldn't load '%s'\n", _FL, p.GetFull().Get());
			LAssert(!"Icons failed to load.");
		}
	}
	
	LSurface *GetIcon(int Idx)
	{
		if (!Icons) return NULL;
		int x = Idx * 16;
		return Icons->SubImage(x, 0, x+15, 15);
	}
};

TouchKeyboard::TouchKeyboard(const char *ResourcePath, KbdMode m)
{
	d = new TouchKeyboardPriv(ResourcePath);
	d->Mode = m;
	if (m == KbdNumbers)
		d->View = EnterNumbersAndPunctuation;
	else
		d->View = EnterLetters;

	LFontType type;
	type.GetSystemFont("System");
	type.SetPointSize(type.GetPointSize()+2);
	LFont *f = type.Create();	
	AddView(d->e = new LEdit(100, 0, 0, 100, 20));

	auto r = HW_SCREEN;
	SetPos(r);

	d->e->SetFont(f, true);
	
	LResources::StyleElement(this);
}

TouchKeyboard::~TouchKeyboard()
{
	delete d;
}

const char *TouchKeyboard::Name()
{
	return d->e ? d->e->Name() : NULL;
}

bool TouchKeyboard::Name(const char *s)
{
	return d->e ? d->e->Name(s) : false;
}

const char16 *TouchKeyboard::NameW()
{
	return d->e ? d->e->NameW() : NULL;
}

bool TouchKeyboard::NameW(const char16 *s)
{
	return d->e ? d->e->NameW(s) : false;
}

int64 TouchKeyboard::Value()
{
	return d->e ? d->e->Value() : -1;
}

void TouchKeyboard::Value(int64 i)
{
	if (d->e)
	{
		d->e->Value(i);
		
		LString n = d->e->Name();
		LUtf8Ptr p(n);
		d->e->SetCaret(p.GetChars());
	}
}

static void RightAlign(LView *btn, int &x2)
{
	LRect r = btn->GetPos();
	r.Offset(x2 - r.X() - r.x1, 0);
	btn->SetPos(r);
	x2 -= btn->X() + BTN_SPACING_X;
}

LButton *TouchKeyboard::CreateBtn(int id, int &x, int y, const char16 *btn, int flags)
{
	LString s = btn;
	return CreateBtn(id, x, y, s, flags);
}

LButton *TouchKeyboard::CreateBtn(int id, int &x, int y, const char *name, int flags)
{
	size_t Len = 0;
	if (name)
	{
		LUtf8Ptr p(name);
		Len = p.GetChars();
	}
	bool isAmp = Len == 1 && name[0] == '&';
	LButton *btn = new LButton(id, x, y, Len > 1 ? -1 : BTN_WID, BTN_HT, isAmp?"&&":name);
	if (btn)
	{
		d->Btns.Add(btn);

		if (id == LK_RETURN)
		{
			const char *Defs = ReturnBtnStyle;
			btn->GetCss(true)->Parse(Defs);
		}
		else if (flags & KfDark)
		{
			const char *Defs = BlackBtnStyle;
			btn->GetCss(true)->Parse(Defs);
		}

		AddView(btn);

		if (flags & KfRightAlign)
		{
			RightAlign(btn, x);
		}
		else
		{
			x += btn->X() + BTN_SPACING_X;
		}
	}
	
	return btn;
}

void TouchKeyboard::CreateBtnRow(int &x, int y, const char16 *btns)
{
	LString s = btns;
	CreateBtnRow(x, y, s);
}

void TouchKeyboard::CreateBtnRow(int &x, int y, const char *btns)
{
	LString s = btns;
	LString::Array a = s.Split(" ");
	for (unsigned i=0; i<a.Length(); i++)
	{
		CreateBtn(a[i][0], x, y, a[i]);
	}
}

void TouchKeyboard::CreateView(KbdView v)
{
	d->View = v;
	
	// Clear existing buttons
	d->Btns.DeleteObjects();
	
	LRect epos = d->e->GetPos();	
	int half = BTN_WID >> 1;
	int y = epos.y2 + BTN_SPACING_Y;
	int y_space = BTN_HT + BTN_SPACING_Y;
	int offset = 6;
	LButton *b;

	switch (d->View)
	{
		default:
		case TouchKeyboard::EnterLetters:
		{
			LString row1("q w e r t y u i o p");
			LString row2("a s d f g h j k l");
			LString row3("z x c v b n m");

			int x1 = epos.x1 + offset;
			int x2 = epos.x2 - offset;
			CreateBtnRow(x1, y, d->Shifted ? row1.Upper() : row1);
			
			x1 = epos.x1 + offset + half;
			y += y_space;
			CreateBtnRow(x1, y, d->Shifted ? row2.Upper() : row2);

			x1 = epos.x1 + offset;
			y += y_space;
			b = CreateBtn(IDC_SHIFT, x1, y, (const char*)NULL, KfDark);
			if (b)
			{
				LRect p = b->GetPos();
				p.x2 += 12;
				x1 += 16;
				b->SetPos(p);
				b->SetIsToggle(true);
				b->Value(d->Shifted);

				b->SetImage(d->GetIcon(1), true);
				x1 = b->GetPos().x2 + BTN_SPACING_X;
			}
			
			int xtmp = x1;
			b = CreateBtn(LK_BACKSPACE, xtmp, y, (const char*)NULL, KfDark);
			b->SetImage(d->GetIcon(2), true);
			RightAlign(b, x2);
			int btnPx = (7 * BTN_WID) + (6 * BTN_SPACING_X);
			x1 += (x2 - x1 - btnPx) >> 1;
			CreateBtnRow(x1, y, d->Shifted ? row3.Upper() : row3);
			
			x1 = epos.x1 + offset;
			x2 = epos.x2 - offset;
			y += y_space;
			b = CreateBtn(IDC_SWITCH_NUMBERS, x1, y, "?123", KfDark);
			x1 += BTN_SPACING_X;
			b = CreateBtn(',', x1, y, ",");
			LButton *space = CreateBtn(' ', x1, y, " ");
			b = CreateBtn(LK_RETURN, x1, y);
			b->SetImage(d->GetIcon(0), true);
			RightAlign(b, x2);
			x2 -= BTN_SPACING_X;
			b = CreateBtn('.', x2, y, ".", KfRightAlign);
			LRect r = space->GetPos();
			r.x2 = x2;
			space->SetPos(r);
			break;
		}
		case TouchKeyboard::EnterNumbersAndPunctuation:
		{
			int x1 = epos.x1 + offset;
			int x2 = epos.x2 - offset;
			CreateBtnRow(x1, y, "1 2 3 4 5 6 7 8 9 0");
			
			x1 = epos.x1 + offset + half;
			y += y_space;
			CreateBtnRow(x1, y, "@ # $ % & - + ( )");
			
			x1 = epos.x1 + offset;
			y += y_space;
			
			LButton *b = CreateBtn(IDC_SWITCH_SYMBOLS, x1, y, "=\\<", KfDark);
			int xtmp = x1;
			b = CreateBtn(LK_BACKSPACE, xtmp, y, (const char*)NULL, KfDark);
			b->SetImage(d->GetIcon(2), true);
			x2 -= BTN_SPACING_X;
			RightAlign(b, x2);
			// int btnPx = (7 * BTN_WID) + (6 * BTN_SPACING);
			// x1 += (x2 - x1 - btnPx) >> 1;
			x1 = epos.x1 + offset + BTN_WID + BTN_SPACING_X;
			CreateBtnRow(x1, y, "* \" \' : ; ! ?");

			b = CreateBtn(LK_LEFT, x1, y);
			b->SetImage(d->GetIcon(3), true);
			b = CreateBtn(LK_RIGHT, x1, y);
			b->SetImage(d->GetIcon(5), true);

			x1 = epos.x1 + offset;
			x2 = epos.x2 - offset;
			y += y_space;
			
			b = CreateBtn(IDC_SWITCH_LETTERS, x1, y, "ABC", KfDark);
			x1 += BTN_SPACING_X;
			b = CreateBtn(',', x1, y, ",");
			b = CreateBtn('_', x1, y, "_");
			LButton *space = CreateBtn(' ', x1, y, "Space");
			b = CreateBtn(LK_RETURN, x1, y);
			b->SetImage(d->GetIcon(0), true);
			x2 -= BTN_SPACING_X;
			RightAlign(b, x2);
			b = CreateBtn('.', x2, y, ".", KfRightAlign);
			b = CreateBtn('/', x2, y, "/", KfRightAlign);
			LRect r = space->GetPos();
			r.x2 = x2;
			space->SetPos(r);
			y += y_space;
		
			break;
		}
		case TouchKeyboard::EnterSymbols:
		{
			int x1 = epos.x1 + offset;
			int x2 = epos.x2 - offset;
			CreateBtnRow(x1, y, L"~ ` | · √ π ÷ × ¶ ℅");
			
			x1 = epos.x1 + offset + half;
			y += y_space;
			CreateBtnRow(x1, y, L"£ ¢ € ¥ ^ ° = { }");
			
			x1 = epos.x1 + offset;
			y += y_space;
			LButton *b = CreateBtn(IDC_SWITCH_NUMBERS, x1, y, "?123", KfDark);
			int xtmp = x1;
			b = CreateBtn(LK_BACKSPACE, xtmp, y, (const char*)NULL, KfDark);
			b->SetImage(d->GetIcon(2), true);
			RightAlign(b, x2);
			x1 += BTN_WID >> 1;
			CreateBtnRow(x1, y, L"\\ © ® ™ [ ]");

			x1 = epos.x1 + offset;
			x2 = epos.x2 - offset;
			y += y_space;
			b = CreateBtn(IDC_SWITCH_LETTERS, x1, y, "ABC", KfDark);
			x1 += BTN_SPACING_X;
			b = CreateBtn(',', x1, y, ",");
			b = CreateBtn('<', x1, y, "<");
			LButton *space = CreateBtn(' ', x1, y, " ");
			b = CreateBtn(LK_RETURN, x1, y);
			b->SetImage(d->GetIcon(0), true);
			RightAlign(b, x2);
			x2 -= BTN_SPACING_X;
			b = CreateBtn('.', x2, y, ".", KfRightAlign);
			b = CreateBtn('>', x2, y, ">", KfRightAlign);
			LRect r = space->GetPos();
			r.x2 = x2;
			space->SetPos(r);
			break;
		}
	}
	
	Invalidate();
}

void TouchKeyboard::OnPosChange()
{
	LCssTools Tools(GetCss(), GetFont());
	LRect c = GetClient();
	c = Tools.ApplyPadding(c);
	if (d->e)
	{
		LRect p = c;
		p.y2 = p.y1 + d->e->GetFont()->GetHeight() + 12;
		d->e->SetPos(p);
	}
	if (d->Btns.Length() == 0)
	{
		CreateView(d->View);
	}
}

void TouchKeyboard::OnPaint(LSurface *pDC)
{
	LCssTools Tools(GetCss(), GetFont());
	LRect c = GetClient();
	Tools.PaintBorderAndPadding(pDC, c);
	pDC->Colour(Tools.GetBack());
	pDC->Rectangle();
}

int TouchKeyboard::OnNotify(LViewI *Ctrl, LNotification n)
{
	if (d->InNotify)
		return 0;
	d->InNotify = true;
	
	switch (Ctrl->GetId())
	{
		case IDC_SWITCH_LETTERS:
		{
			CreateView(EnterLetters);
			break;
		}
		case IDC_SWITCH_NUMBERS:
		{
			CreateView(EnterNumbersAndPunctuation);
			break;
		}
		case IDC_SWITCH_SYMBOLS:
		{
			CreateView(EnterSymbols);
			break;
		}
		case IDC_SHIFT:
		{
			d->Shifted = Ctrl->Value() != 0;
			CreateView(EnterLetters);
			break;
		}
		case LK_RETURN:
		{
			EndModal(1);
			break;
		}
		case LK_BACKSPACE:
		{
			if (!d->e) break;
			auto Pos = d->e->GetCaret();
			if (Pos > 0)
			{
				LString Old = d->e->Name();
				Pos--;
				LString New = Old(0, Pos) + Old(Pos+1, -1);
				if (d->View == TouchKeyboard::EnterNumbersAndPunctuation &&
					New.Length() == 0)
					New = "0";
					
				d->e->Name(New);
				d->e->SetCaret(Pos);
				d->e->Invalidate();
				d->e->Focus(true);
			}
			break;
		}
		case LK_LEFT:
		{
			if (!d->e) break;
			auto p = d->e->GetCaret();
			if (p > 0)
				d->e->SetCaret(p - 1);
			break;
		}
		case LK_RIGHT:
		{
			if (!d->e) break;
			auto p = d->e->GetCaret();
			d->e->SetCaret(p + 1);
			break;
		}
		default:
		{
			auto Ch = Ctrl->NameW();
			if (Ch && d->e)
			{
				if (d->View == KbdNumbers &&
					!Stricmp(d->e->Name(), "0") &&
					*Ch >= '0' &&
					*Ch <= '9')
				{
					// If we are entering a number and there is a zero...
					// Clear that first before inserting the new number.
					d->e->Name("");
				}
				
				auto Len = StrlenW(Ch);
				auto Pos = d->e->GetCaret();
				LString Old = d->e->Name();
				LString New = Old(0, Pos) + LString(Ch, 1) + Old(Pos, -1);
				d->e->Name(New);
				d->e->SetCaret(Pos + 1);
				d->e->Invalidate();
				d->e->Focus(true);
			}
			break;
		}
	}
	
	d->InNotify = false;
	return 0;
}

