#include "lgi/common/Lgi.h"
#include "lgi/common/Button.h"
#include "lgi/common/CssTools.h"
#include "lgi/common/ScrollBar.h"
#include "lgi/common/TextLabel.h"
#include "mc4.h"
#include "TouchKbd.h"
#include "resdefs.h"

static const char *BtnNormalStyle = "no-paint-color: transparent; color: black; background: ThreeDFace;";
static const char *BtnDarkStyle = "no-paint-color: transparent; color: white; background: #666;";

enum Controls
{
	IDC_STATIC = -1,
	IDC_RED = 700,
	IDC_GREEN,
	IDC_BLUE,
	IDC_RGB_BTN,
};

static const int GroupIds[] = { IDC_BLOCKS_1, IDC_BLOCKS_2, IDC_BLOCKS_3, IDC_BLOCKS_4, IDC_BLOCKS_5, 0 };				
class IaBlockDlg : public Mc4Dialog
{
	IaRef EditRef;

public:
	IaBlockDlg(Mc4Ui *ui, IaRef ref) : Mc4Dialog(ui), EditRef(ref)
	{
		if (Ui->LoadResource(Resource = IDD_IA_SEL_BLOCK))
		{
			for (int i=0; GroupIds[i]; i++)
			{
				LButton *b;
				if (Ui->GetViewById(GroupIds[i], b))
				{
					const char *Defs = BtnNormalStyle;
					b->GetCss(true)->Parse(Defs);
					b->SetIsToggle(true);
				}
			}
				
			LViewI *v;
			if (Ui->GetViewById(IDC_BLOCKS_1, v))
			{
				v->Value(true);
				OnNotify(v, LNotifyValueChanged);
			}
		}
	}

	int OnNotify(LViewI *Ctrl, LNotification n) override
	{
		switch (Ctrl->GetId())
		{
			// Select block page
			case IDC_BLOCKS_1:
			case IDC_BLOCKS_2:
			case IDC_BLOCKS_3:
			case IDC_BLOCKS_4:
			case IDC_BLOCKS_5:
			{
				LString n = Ctrl->Name();
				LString::Array a = n.Split("-");
				if (a.Length() == 2)
					Ui->ShowAxefxBlocks(a[0].Lower().Get()[0], a[1].Lower().Get()[0]);
				else
					Ui->ShowAxefxBlocks(a[0].Lower().Get()[0], a[0].Lower().Get()[0]);
				
				for (int i=0; GroupIds[i]; i++)
				{
					LButton *b;
					if (Ui->GetViewById(GroupIds[i], b))
					{
						bool Sel = GroupIds[i] == Ctrl->GetId();
						if (Sel)
						{
							const char *Defs = BtnDarkStyle;
							b->GetCss(true)->Parse(Defs);
							b->Value(true);
						}
						else if (b->Value())
						{
							const char *Defs = BtnNormalStyle;
							b->GetCss(true)->Parse(Defs);
							b->Value(false);
						}
					}
				}
				return true;
			}
			default:
			{
				int Id = Ctrl->GetId();
				int BlockCount = AxefxBlockCount() - 1;

				if (Id >= AXEFXBLOCK_BTN_BASE && Id < AXEFXBLOCK_BTN_BASE + BlockCount)
				{
					int BlockIndex = Id - AXEFXBLOCK_BTN_BASE;
					auto b = AxefxGetBlocks() + BlockIndex;
				
					auto Sh = Shared->Lock(_FL);
					if (Sh)
					{
						auto &Cur = Sh->Preset.GetIa(EditRef);
						MidiCmd &Cmd = Cur.Cmds.First();
						Cmd.Id = MIDI_CMD_BLOCK;
						Cmd.Param = BlockIndex;
				
						Sh->Preset.SetDirty();
					}
					Ui->OnBack(2);
					return true;
				}
				break;
			}
		}

		return Mc4Dialog::OnNotify(Ctrl, n);
	}
};

class SelectSceneDlg : public Mc4Dialog
{
	IaRef EditRef;

public:
	SelectSceneDlg(Mc4Ui *ui, IaRef ref) : Mc4Dialog(ui), EditRef(ref)
	{
		Load();
	}

	bool Load()
	{
		return Ui->LoadResource(Resource = IDD_IA_SEL_SCENE);
	}

	int OnNotify(LViewI *Ctrl, LNotification n) override
	{
		switch (Ctrl->GetId())
		{
			// Select scene dialog
			#define HandleSetSceneBtn(idx) \
				case IDC_SCENE_##idx: \
				{ \
					auto Sh = Shared->Lock(_FL); \
					if (Sh) \
					{ \
						auto &Cur = Sh->Preset.GetIa(EditRef); \
						MidiCmd &c = Cur.Cmds.First(); \
						c.Id = MIDI_CMD_SCENE; \
						c.Param = idx; \
						Sh->Preset.SetDirty(); \
					} \
					Ui->OnBack(2); \
					return true; \
				}

			HandleSetSceneBtn(1)
			HandleSetSceneBtn(2)
			HandleSetSceneBtn(3)
			HandleSetSceneBtn(4)
			HandleSetSceneBtn(5)
			HandleSetSceneBtn(6)
			HandleSetSceneBtn(7)
			HandleSetSceneBtn(8)		
		}

		return Mc4Dialog::OnNotify(Ctrl, n);
	}
};

class LooperConfigDlg : public Mc4Dialog
{
	IaRef EditRef;

public:
	LooperConfigDlg(Mc4Ui *ui, IaRef ref) : Mc4Dialog(ui), EditRef(ref)
	{
		Load();
	}

	bool Load()
	{
		return Ui->LoadResource(Resource = IDD_IA_LOOPER);
	}

	int OnNotify(LViewI *Ctrl, LNotification n) override
	{
		switch (Ctrl->GetId())
		{
			// Select looper button
			#define HandleSetLooperBtn(btn_id, cc) \
				case btn_id: \
				{ \
					auto Sh = Shared->Lock(_FL); \
					if (Sh) \
					{ \
						auto &Cur = Sh->Preset.GetIa(EditRef); \
						auto &c = Cur.Cmds.First(); \
						c.Id = MIDI_CMD_CC; \
						c.Param = cc; \
						Sh->Preset.SetDirty(); \
					} \
					Ui->OnBack(2); \
					return true; \
				}
			HandleSetLooperBtn(IDC_LOOPER_REC, 	CcLooperRec)
			HandleSetLooperBtn(IDC_LOOPER_PLAY, CcLooperPlay)
			HandleSetLooperBtn(IDC_LOOPER_ONCE, CcLooperOnce)
			HandleSetLooperBtn(IDC_LOOPER_DUB, CcLooperDub)
			HandleSetLooperBtn(IDC_LOOPER_REV, CcLooperRev)
			HandleSetLooperBtn(IDC_LOOPER_BYPASS, CcLooperByp)
			HandleSetLooperBtn(IDC_LOOPER_HALF, CcLooperHalf)
			HandleSetLooperBtn(IDC_LOOPER_UNDO, CcLooperUndo)
		}

		return Mc4Dialog::OnNotify(Ctrl, n);
	}
};

class IaTypeDlg : public Mc4Dialog
{
	IaRef EditRef;

public:
	IaTypeDlg(Mc4Ui *ui, IaRef ref) : Mc4Dialog(ui), EditRef(ref)
	{
		Load();
	}

	bool Load()
	{
		return Ui->LoadResource(Resource = IDD_IA_BTN_TYPE);
	}

	int OnNotify(LViewI *Ctrl, LNotification n) override
	{
		switch (Ctrl->GetId())
		{
			// IA button type
			case IDC_IA_SET_CC:
			{
				TouchKeyboard k(Ui->GetResourcePath(), TouchKeyboard::KbdNumbers);
				k.SetParent(Ui);
				k.Value(0);
				{
					auto Sh = Shared->Lock(_FL);
					if (Sh)
					{
						auto &Cur = Sh->Preset.GetIa(EditRef);
						auto &Cmd = Cur.Cmds.First();
						if (Cmd.Id == MIDI_CMD_CC)
							k.Value(Cmd.Param);
					}
				}
				k.MoveSameScreen(Ui);
				if (k.DoModal())
				{
					auto Sh = Shared->Lock(_FL);
					if (Sh)
					{
						auto &Cur = Sh->Preset.GetIa(EditRef);
						auto &Cmd = Cur.Cmds.First();
						Cmd.Id = MIDI_CMD_CC;
						Cmd.Param = (int)k.Value();

						Sh->Preset.SetDirty();
					}

					Ui->OnBack();
				}
				return true;
			}
			case IDC_IA_SET_BLOCK:
			{
				new IaBlockDlg(Ui, EditRef);
				return true;
			}
			case IDC_IA_SET_SCENE:
			{
				new SelectSceneDlg(Ui, EditRef);
				return true;
			}
			case IDC_IA_SET_LOOPER:
			{
				new LooperConfigDlg(Ui, EditRef);
				return true;
			}
			case IDC_IA_SET_NONE:
			{
				auto Sh = Shared->Lock(_FL);
				if (Sh)
				{
					auto &Cur = Sh->Preset.GetIa(EditRef);
					auto &Cmd = Cur.Cmds.First();
					Cmd.Id = MIDI_CMD_NONE;
					Cmd.Param = 0;

					Sh->Preset.SetDirty();
				}
				Ui->OnBack();
				return true;
			}
		}

		return Mc4Dialog::OnNotify(Ctrl, n);
	}
};

class ColourButton : public LView
{
public:
	const char *GetClass() { return "ColourButton"; }

	ColourButton(int id)
	{
		SetId(id);
		LRect r(0, 0, 90, 70);
		SetPos(r);
	}
	
	void OnPaint(LSurface *pDC)
	{
		LCssTools t(GetCss(), GetFont());
		LColour &bk = t.GetBack();
		
		pDC->Colour(bk);
		pDC->Rectangle();
	}
	
	void OnMouseClick(LMouse &m)
	{
		if (m.Down() && m.Left())
			SendNotify();
	}
};

class ColourSelectDlg : public Mc4Dialog
{
	IaRef EditRef;
	
public:
	ColourSelectDlg(Mc4Ui *ui, IaRef ref) : Mc4Dialog(ui), EditRef(ref)
	{
		Load();
	}

	bool Load()
	{
		if (!Ui->LoadResource(Resource = IDD_SELECT_COLOUR))
			return false;

		LButton *b;
		if (Ui->GetViewById(IDC_RGB, b))
			b->SetIsToggle(true);
		else return false;
		if (Ui->GetViewById(IDC_PALETTE, b))
			b->SetIsToggle(true);
		else return false;

		return ShowColours(0);
	}

	void UpdateRgb()
	{
		ColourButton *btn;
		LScrollBar *sbR, *sbG, *sbB;
		if (Ui->GetViewById(IDC_RGB_BTN, btn) &&
			Ui->GetViewById(IDC_RED, sbR) &&
			Ui->GetViewById(IDC_GREEN, sbG) &&
			Ui->GetViewById(IDC_BLUE, sbB))
		{
			btn->GetCss(true)->BackgroundColor
			(
				LCss::ColorDef
				(
					LCss::ColorRgb,
					Rgb32(sbR->Value(), sbG->Value(), sbB->Value())
				)
			);
		}
	}

	int OnNotify(LViewI *Ctrl, LNotification n) override
	{
		switch (Ctrl->GetId())
		{
			// Select colour page
			case IDC_PALETTE:
			{
				ShowColours(0);
				return true;
			}
			case IDC_RGB:
			{
				ShowColours(1);
				return true;
			}
			case IDC_RED:
			case IDC_GREEN:
			case IDC_BLUE:
			{
				UpdateRgb();
				return true;
			}
			case IDC_RGB_BTN:
			{
				// select the current colour
				return true;
			}
			case IDC_NO_COLOUR:
			{
				auto Sh = Shared->Lock(_FL);
				if (Sh)
				{
					auto &Cur = Sh->Preset.GetIa(EditRef);
					Cur.LedColour.Empty();

					Sh->Preset.SetDirty();
				}
				Ui->OnBack();
				return true;
			}
			default:
			{
				int Id = Ctrl->GetId();
				if (Id >= PALETTE_COLOUR_BASE && Id < PALETTE_COLOUR_BASE + 100)
				{
					LCss::ColorDef c = Ctrl->GetCss(true)->BackgroundColor();
					if (c.IsValid())
					{
						auto Sh = Shared->Lock(_FL);
						if (Sh)
						{
							auto &Cur = Sh->Preset.GetIa(EditRef);
							Cur.LedColour.Set(c.Rgb32, 32);
							Sh->Preset.SetDirty();
						}
						Ui->OnBack();
					}
					return true;
				}
				break;
			}
		}

		return Mc4Dialog::OnNotify(Ctrl, n);
	}

	bool ShowColours(bool RGB)
	{
		auto *Base = Ui->EmptyCell(0, 2);
		if (!Base)
			return false;

		// Add in buttons for the colours
		auto *l = new LTableLayout(IDC_TABLE);
		if (l)
		{
			LButton *b;
			if (Ui->GetViewById(IDC_RGB, b))
			{
				const char *Defs = RGB ? BtnDarkStyle : BtnNormalStyle;
				b->GetCss(true)->Parse(Defs);
				b->Value(RGB);
			}
			if (Ui->GetViewById(IDC_PALETTE, b))
			{
				const char *Defs = !RGB ? BtnDarkStyle : BtnNormalStyle;
				b->GetCss(true)->Parse(Defs);
				b->Value(!RGB);
			}
			
			if (RGB)
			{
				int y = 0;
				LScrollBar *sbR, *sbG, *sbB;
				LCss::Len w(LCss::LenPx, 500);
				LCss::Len h(LCss::LenPx, 60);

				l->GetCss(true)->BorderSpacing( LCss::Len(LCss::LenPx, 12) );
			
				auto *c = l->GetCell(0, y);
				c->Add(new LTextLabel(IDC_STATIC, 0, 0, -1, -1, "Red"));
				c = l->GetCell(1, y);
				c->Add(sbR = new LScrollBar(IDC_RED, 0, 0, (int)w.Value, (int)h.Value, "red"));
				sbR->GetCss(true)->Width(w);
				sbR->GetCss(true)->Height(h);
				sbR->SetRange(LRange(0, 256));
				sbR->SetPage(16);

				c = l->GetCell(0, ++y);
				c->Add(new LTextLabel(IDC_STATIC, 0, 0, -1, -1, "Green"));
				c = l->GetCell(1, y);
				c->Add(sbG = new LScrollBar(IDC_GREEN, 0, 0, (int)w.Value, (int)h.Value, "green"));
				sbG->GetCss(true)->Width(w);
				sbG->GetCss(true)->Height(h);
				sbG->SetRange(LRange(0, 256));
				sbG->SetPage(16);

				c = l->GetCell(0, ++y);
				c->Add(new LTextLabel(IDC_STATIC, 0, 0, -1, -1, "Blue"));
				c = l->GetCell(1, y);
				c->Add(sbB = new LScrollBar(IDC_BLUE, 0, 0, (int)w.Value, (int)h.Value, "blue"));
				sbB->GetCss(true)->Width(w);
				sbB->GetCss(true)->Height(h);
				sbB->SetRange(LRange(0, 256));
				sbB->SetPage(16);

				c = l->GetCell(0, ++y, true, 2);
				ColourButton *btn = new ColourButton(IDC_RGB_BTN);
				if (btn)
				{
					btn->GetCss(true)->BackgroundColor
					(
						LCss::ColorDef
						(
							LCss::ColorRgb,
							Rgb32(sbR->Value(), sbG->Value(), sbB->Value())
						)
					);
					c->Add(btn);
				}
			}
			else
			{
				int Cols = 6;
				int Hues = Cols * 2;
				int Lumas = 2;
				int Rows = Hues / Cols;

				l->GetCss(true)->BorderSpacing( LCss::Len(LCss::LenPx, 9) );

				int LumLut[] = { 0x90, 0x40 };
				int Id = PALETTE_COLOUR_BASE;
				for (int hue=0; hue<Hues; hue++)
				{
					for (int lum=0; lum<Lumas; lum++)
					{
						LColour col;
						col.SetHLS(hue * 360 / Hues, LumLut[lum], 0xff);
					
						int x = hue % Cols;
						int y = (hue / Cols * Lumas) + lum;
					
						auto *c = l->GetCell(x, y);
						ColourButton *btn = new ColourButton(Id++);
						btn->GetCss(true)->BackgroundColor(LCss::ColorDef(LCss::ColorRgb, col.c32()));
						c->Add(btn);
					
					}
				}
			}		

			Base->Add(l);
		}

		auto Tbl = Base->GetTable();
		Tbl->InvalidateLayout();
		Tbl->Invalidate();

		return true;
	}

};

IaConfigDlg::IaConfigDlg(Mc4Ui *ui, IaRef ref) : Mc4Dialog(ui), EditRef(ref)
{
	Load();
}

bool IaConfigDlg::Load()
{
	if (!Ui->LoadResource(Resource = IDD_IA_CONFIG))
		return false;

	auto Sh = Shared->Lock(_FL);
	auto &Cur = Sh->Preset.GetIa(EditRef);
	OnAutoSelectParam(Cur.AutoSelectMode);
	OnBlockAction(Cur.BlockAction);

	return Update();
}

bool IaConfigDlg::Update()
{
	auto Sh = Shared->Lock(_FL);	
	auto &Cur = Sh->Preset.GetIa(EditRef);
	if (Cur.Cmds.Length() == 0)
		Cur.Cmds[0].Init();

	auto &Cmd = Cur.Cmds.First();
	switch (Cmd.Id)
	{
		default:
		case MIDI_CMD_NONE:
			Ui->SetCtrlName(IDC_IA_TYPE, "None");
			break;
		case MIDI_CMD_SCENE:
		{
			LString s;
			s.Printf("Scene: %i", Cmd.Param);
			Ui->SetCtrlName(IDC_IA_TYPE, s);
			break;
		}
		case MIDI_CMD_CC:
		{
			Ui->SetCtrlName(IDC_IA_TYPE, ToString((AxefxCc)Cmd.Param));
			break;
		}
		case MIDI_CMD_BLOCK:
		{
			LString s;
			if (Cmd.Param >= AxefxBlockCount()-1)
				s.Printf("Invalid Param");
			else
				s.Printf("Block: %s", AxefxGetBlocks()[Cmd.Param].Name);
			Ui->SetCtrlName(IDC_IA_TYPE, s);
			break;
		}
	}
	
	if (!Cur.AutoSelectMode)
		Cur.AutoSelectMode = IDC_NONE;
	OnAutoSelectParam(Cur.AutoSelectMode);

	if (!Cur.BlockAction)
		Cur.BlockAction = BlockAction_OnOff;
	OnBlockAction(Cur.BlockAction);

	if (Cur.LedColour.IsTransparent())
		Ui->SetCtrlName(IDC_IA_COLOUR, "None");
	else
		Ui->SetCtrlName(IDC_IA_COLOUR, Cur.LedColour.GetStr());

	return false;
}

void IaConfigDlg::OnAutoSelectParam(int Id)
{
	// Turn non-selected buttons...
	int Ctrls[] = { IDC_NONE, IDC_EXTERN_12, IDC_EXTERN_34, IDC_EXTERN_56, IDC_EXTERN_78 };
	for (int i=0; i<CountOf(Ctrls); i++)
	{
		LView *v;
		if (Ui->GetViewById(Ctrls[i], v))
		{
			int64 sel = Ctrls[i] == Id;
			if (v->Value() != sel)
				v->Value(sel);
		}
	}

	// Save new value to the preset...
	bool IsDirty = false;
	{
		auto Sh = Shared->Lock(_FL);
		auto &Cur = Sh->Preset.GetIa(EditRef);
		// LgiTrace("Cur=%p %i %i\n", &Cur, Cur.AutoSelectMode, Id);
		if (Cur.AutoSelectMode != Id)
		{
			Cur.AutoSelectMode = Id;
			IsDirty = true;
			Sh->Preset.SetDirty();
		}
	}

	if (IsDirty)
		Update();
}

void IaConfigDlg::OnBlockAction(IaBlockAction action)
{
	// Turn non-selected buttons...
	Ui->SetCtrlValue(IDC_BLOCK_ON, action == BlockAction_OnOff);
	Ui->SetCtrlValue(IDC_BLOCK_XY, action == BlockAction_SelectXY);

	// Save new value to the preset...
	bool IsDirty = false;
	{
		auto Sh = Shared->Lock(_FL);
		auto &Cur = Sh->Preset.GetIa(EditRef);
		if (Cur.BlockAction != action)
		{
			Cur.BlockAction = action;
			IsDirty = true;
			Sh->Preset.SetDirty();
		}
	}

	if (IsDirty)
		Update();
}

int IaConfigDlg::OnNotify(LViewI *Ctrl, LNotification n)
{
	switch (Ctrl->GetId())
	{
		// IA setup page
		case IDC_SET_TYPE:
			new IaTypeDlg(Ui, EditRef);
			return true;
		
		case IDC_NONE:
		case IDC_EXTERN_12:
		case IDC_EXTERN_34:
		case IDC_EXTERN_56:
		case IDC_EXTERN_78:
			OnAutoSelectParam(Ctrl->GetId());
			return true;
		
		case IDC_BLOCK_ON:
			OnBlockAction(BlockAction_OnOff);
			return true;
		case IDC_BLOCK_XY:
			OnBlockAction(BlockAction_SelectXY);
			return true;

		case IDC_SET_COLOUR:
			new ColourSelectDlg(Ui, EditRef);
			return true;
	}

	return Mc4Dialog::OnNotify(Ctrl, n);
}		
