/*
**	FILE:			LgiRes_Menu.cpp
**	AUTHOR:			Matthew Allen
**	DATE:			6/3/2000
**	DESCRIPTION:	Menu Resource Editor
**
**
**	Copyright (C) 1999, Matthew Allen
**		fret@memecode.com
*/

////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include "LgiResEdit.h"
#include "LgiRes_Menu.h"
#include "lgi/common/ClipBoard.h"
#include "lgi/common/Menu.h"
#include "lgi/common/ToolBar.h"
#include "lgi/common/StatusBar.h"

#define IDC_TREE				100

//////////////////////////////////////////////////////////////////
#define VAL_Separator			"Sep"
#define VAL_Enabled				"Enabled"
#define VAL_Shortcut			"Shortcut"

ResMenuItem::ResMenuItem(ResMenu *menu)
{
	Sep = false;
	Enabled = true;
	Menu = menu;
	_Str = 0;
}

ResMenuItem::~ResMenuItem()
{
	Menu->App()->OnObjDelete(this);

	if (_Str)
		Menu->GetStringGroup()->DeleteStr(_Str);
}

ResString *ResMenuItem::GetStr()
{
	return _Str;
}

bool ResMenuItem::OnNew()
{
	_Str = Menu->GetStringGroup()->CreateStr();
	if (!_Str)
		return false;

	if (!_Str->GetDefine())
	{
		int Uid = Menu->App()->GetUniqueCtrlId();
		char Def[256];
		sprintf(Def, "IDM_MENU_%i", Uid);
		_Str->SetDefine(Def);
		_Str->SetId(Uid);
	}

	return true;
}

void ResMenuItem::OnMouseClick(LMouse &m)
{
	if (m.IsContextMenu())
	{
		ResString *Str = GetStr();
		if (Str)
		{
			LSubMenu RClick;
			bool PasteData = false;
			bool PasteTranslations = false;

			{
				LClipBoard c(Tree);
				char *Clip = c.Text();
				if (Clip)
				{
					PasteTranslations = strstr(Clip, TranslationStrMagic);

					char *p = Clip;
					while (*p && strchr(" \r\n\t", *p)) p++;
					PasteData = *p == '<';
				}
			}

			RClick.AppendItem("Copy Text", IDM_COPY_TEXT, true);
			RClick.AppendItem("Paste Text", IDM_PASTE_TEXT, PasteTranslations);
			RClick.AppendSeparator();
			RClick.AppendItem("New Id", IDM_NEW_ID, true);

			if (Tree->GetMouse(m, true))
			{
				switch (RClick.Float(Tree, m.x, m.y))
				{
					case IDM_COPY_TEXT:
					{
						Str->CopyText();
						break;
					}
					case IDM_PASTE_TEXT:
					{
						Str->PasteText();
						break;
					}
					case IDM_NEW_ID:
					{
						LArray<ResMenuItem*> Sel;
						if (GetTree() &&
							GetTree()->GetSelection(Sel))
						{
							for (auto i : Sel)
								i->GetStr()->NewId();
						}
						break;
					}
				}
			}
		}
	}
}

const char *ResMenuItem::GetText(int i)
{
	if (Sep)
	{
		return "--------------";
	}

	ResString *Str = GetStr();
	char *s = Str ? Str->Get() : 0;
	return (s) ? s : "<null>";
}

bool ResMenuItem::GetFields(FieldTree &Fields)
{
	ResString *Str = GetStr();
	if (!Str)
		return false;

	Str->GetFields(Fields);
	Fields.Insert(this, DATA_BOOL, 300, VAL_Separator, "Separator");
	Fields.Insert(this, DATA_BOOL, 301, VAL_Enabled, "Enabled");
	Fields.Insert(this, DATA_STR, 301, VAL_Shortcut, "Shortcut");
	return true;
}

bool ResMenuItem::Serialize(FieldTree &Fields)
{
	ResString *Str = GetStr();
	if (!Str)
		return false;

	Str->Serialize(Fields);
	
	if (Fields.GetMode() == FieldTree::ObjToUi)
	{
		if (Str->GetRef() == 0)
			Str->SetRef(Str->GetGroup()->UniqueRef());
		
		if (ValidStr(Str->GetDefine()) && Str->GetId() == 0)
			Str->SetId(Str->GetGroup()->UniqueId(Str->GetDefine()));
	}

	Fields.Serialize(this, VAL_Separator, Sep);
	Fields.Serialize(this, VAL_Enabled, Enabled);
	Fields.Serialize(this, VAL_Shortcut, Short);

	Update();

	return true;
}

bool ResMenuItem::Read(LXmlTag *t, ResMenuItem *Parent)
{
	bool Status = false;
	if (t)
	{
		bool SubMenu = t->IsTag("submenu");
		bool MenuItem = t->IsTag("menuitem");

		if (SubMenu || MenuItem)
		{
			// Read item
			char *n = 0;
			Sep = (n = t->GetAttr("sep")) ? atoi(n) : false;
			Enabled = (n = t->GetAttr("enabled")) ? atoi(n) : true;
			Short.Reset(NewStr(t->GetAttr("shortcut")));

			if ((n = t->GetAttr("ref")) &&
				Menu &&
				Menu->Group)
			{
				int Ref = atoi(n);
				_Str = Menu->GetStringByRef(Ref);
				LAssert(_Str);
				if (!_Str)
				{
					if (!(_Str = Menu->GetStringGroup()->CreateStr()))
					{
						LAssert(!"Create str failed.");
					}
				}
			}

			// Attach item
			Status = MenuItem;
			if (Parent)
			{
				Parent->Insert(this);
			}
			else if (Menu)
			{
				Menu->Insert(this);
			}

			// Read sub items
			if (SubMenu)
			{
				for (auto c: t->Children)
				{
					ResMenuItem *i = new ResMenuItem(Menu);
					if (i && i->Read(c, this))
					{
						Status = true;
					}
					else
					{
						LAssert(0);
						DeleteObj(i);
					}
				}
			}
		}
	}

	return Status;
}

bool ResMenuItem::Write(LXmlTag *t, int Tabs)
{
	bool SubMenu = GetChild() != 0;
	t->SetTag(SubMenu ? (char*) "submenu" : (char*) "menuitem");
	if (Sep)
	{
		t->SetAttr("Sep", 1);
	}
	else
	{
		if (!Enabled) t->SetAttr("Enabled", 0);
		t->SetAttr("Ref", _Str->GetRef());
		if (Short) t->SetAttr("Shortcut", Short);
		else t->DelAttr("Shortcut");
	}

	if (SubMenu)
	{
		for (ResMenuItem *i = dynamic_cast<ResMenuItem*>(GetChild()); i; i = dynamic_cast<ResMenuItem*>(i->GetNext()))
		{
			LXmlTag *c = new LXmlTag;
			if (c && i->Write(c, 0))
			{
				t->InsertTag(c);
			}
			else
			{
				DeleteObj(c);
			}
		}
	}

	return true;
}

//////////////////////////////////////////////////////////////////
ResMenu::ResMenu(AppWnd *w, int type) :
	Resource(w, type),
	LTree(IDC_TREE, 0, 0, 200, 200, "Menu resource")
{
	Sunken(false);
	Group = new ResStringGroup(w);
}

ResMenu::~ResMenu()
{
	Empty();
	DeleteObj(Group);
}

void ResMenu::OnShowLanguages()
{
	if (Group)
	{
		Invalidate();
	}
}

void AddChildren(LTreeItem *i, List<ResMenuItem> &Items)
{
	for (LTreeItem *c = i->GetChild(); c; c = c->GetNext())
	{
		ResMenuItem *m = dynamic_cast<ResMenuItem*>(c);
		if (m)
		{
			Items.Insert(m);
		}
		AddChildren(c, Items);
	}
}

void AddChildren(LTree *i, List<ResMenuItem> &Items)
{
	for (LTreeItem *c = i->GetChild(); c; c = c->GetNext())
	{
		ResMenuItem *m = dynamic_cast<ResMenuItem*>(c);
		if (m)
		{
			Items.Insert(m);
		}
		AddChildren(c, Items);
	}
}

void ResMenu::EnumItems(List<ResMenuItem> &Items)
{
	AddChildren(this, Items);
}

void ResMenu::Create(LXmlTag *load, SerialiseContext *Ctx)
{
	Name("IDM_MENU");

	if (load)
	{
		if (Ctx)
			Read(load, *Ctx);
		else
			LAssert(0);
	}

	if (Resource::Item) Resource::Item->Update();
	Visible(true);
}

LView *ResMenu::CreateUI()
{
	return Ui = new ResMenuUi(this);
}

void ResMenu::OnItemClick(LTreeItem *Item, LMouse &m)
{
	LTree::OnItemClick(Item, m);
}

void ResMenu::OnItemBeginDrag(LTreeItem *Item, LMouse &m)
{
	LTree::OnItemBeginDrag(Item, m);
}

void ResMenu::OnItemExpand(LTreeItem *Item, bool Expand)
{
	LTree::OnItemExpand(Item, Expand);
}

void ResMenu::OnItemSelect(LTreeItem *Item)
{
	LTree::OnItemSelect(Item);

	if (AppWindow)
	{
		ResMenuItem *Item = dynamic_cast<ResMenuItem*>(Selection());
		AppWindow->OnObjSelect(Item);
	}
}

void ResMenu::OnRightClick(LSubMenu *RClick)
{
}

void ResMenu::OnCommand(int Cmd)
{
}

int ResMenu::OnCommand(int Cmd, int Event, OsView hWnd)
{
	OsView Null(NULL);

	switch (Cmd)
	{
		case IDM_NEW_SUB:
		{
			ResMenuItem *Item = dynamic_cast<ResMenuItem*>(Selection());
			LTreeItem *New = 0;

			ResMenuItem *NewItem = new ResMenuItem(this);
			if (NewItem && !NewItem->OnNew())
			{
				DeleteObj(NewItem);
				LgiMsg(GetTree(), "Unrecoverable error creating menu item (corrupt refs?)", AppName);
				return -1;
			}

			if (Item)
				New = Item->Insert(NewItem, 0);
			else
				New = Insert(NewItem, 0);

			if (New)
			{
				Select(New);
				AppWindow->SetDirty(true);
			}
			break;
		}
		case IDM_NEW_ITEM:
		{
			ResMenuItem *Item = dynamic_cast<ResMenuItem*>(Selection());
			if (Item)
			{
				auto n = Item->IndexOf();
				if (n >= 0) n++;

				LTreeItem *Parent = Item->GetParent();
				LTreeItem *New = 0;
				ResMenuItem *NewItem = new ResMenuItem(this);
				if (NewItem && !NewItem->OnNew())
				{
					DeleteObj(NewItem);
					LgiMsg(GetTree(), "Unrecoverable error creating menu item (corrupt refs?)", AppName);
					return -1;
				}

				if (Parent)
				{
					New = Parent->Insert(NewItem, n);
				}
				else
				{
					New = Insert(NewItem, n);
				}

				if (New)
				{
					Select(New);
					AppWindow->SetDirty(true);
				}
			}
			break;
		}
		case IDM_DELETE_ITEM:
		{
			ResMenuItem *Item = dynamic_cast<ResMenuItem*>(Selection());
			if (Item)
			{
				Remove(Item);
				DeleteObj(Item);
				AppWindow->SetDirty(true);
			}
			break;
		}
		case IDM_MOVE_UP:
		{
			ResMenuItem *Item = dynamic_cast<ResMenuItem*>(Selection());
			if (Item)
			{
				LTreeNode *Parent = 0;
				if (Item->GetParent())
					Parent = Item->GetParent();
				else
					Parent = Item->GetTree();

				if (Parent)
				{
					auto n = Item->IndexOf();
					if (n > 0)
					{
						Item->Remove();
						Parent->Insert(Item, n - 1);
						Item->Select(true);
						AppWindow->SetDirty(true);
					}
				}
			}
			break;
		}
		case IDM_MOVE_DOWN:
		{
			ResMenuItem *Item = dynamic_cast<ResMenuItem*>(Selection());
			if (Item)
			{
				LTreeNode *Parent = 0;
				if (Item->GetParent())
					Parent = Item->GetParent();
				else
					Parent = Item->GetTree();

				if (Parent)
				{
					auto n = Item->IndexOf();
					if (n < Parent->Length() - 1)
					{
						Item->Remove();
						Parent->Insert(Item, n + 1);
						Item->Select(true);
						AppWindow->SetDirty(true);
					}
				}
			}
			break;
		}
		case IDM_NEW_LANG:
		{
			if (Group)
			{
				Group->OnCommand(IDM_NEW_LANG, 0, Null);
				Invalidate();
				AppWindow->SetDirty(true);
			}
			break;
		}
		case IDM_DELETE_LANG:
		{
			if (Group)
			{
				Group->OnCommand(IDM_DELETE_LANG, 0, Null);
				Invalidate();
				AppWindow->SetDirty(true);
			}
			break;
		}
	}

	return 0;
}

ResMenuItem *ResMenuItem::FindByRef(int Ref)
{
	for (auto i: Items)
	{
		ResMenuItem *m = dynamic_cast<ResMenuItem*>(i);
		if (m)
		{
			ResMenuItem *f = m->FindByRef(Ref);
			if (f)
				return f;
		}
	}

	return 0;
}

ResMenuItem *ResMenu::GetItemByRef(int Ref)
{
	for (auto i: Items)
	{
		ResMenuItem *m = dynamic_cast<ResMenuItem*>(i);
		if (m)
		{
			ResMenuItem *f = m->FindByRef(Ref);
			if (f)
				return f;
		}
	}

	return 0;
}

ResString *ResMenu::GetStringByRef(int Ref)
{
	return Group ? Group->FindRef(Ref) : 0;
}

bool ResMenu::Test(ErrorCollection *e)
{
	return Group->Test(e);
}

bool ResMenu::Read(LXmlTag *t, SerialiseContext &Ctx)
{
	bool Status = true;

	char *p = 0;
	if ((p = t->GetAttr("Name")))
	{
		Name(p);
	}

	for (auto c: t->Children)
	{
		if (c->IsTag("string-group"))
		{
			if (Group)
			{
				if (!Group->Read(c, Ctx))
				{
					Status = false;
					Ctx.Log.Print("%s:%i - Failed to read string group.\n", _FL);
				}
			}
			else
			{
				Status = false;
				Ctx.Log.Print("%s:%i - No Group to read.\n", _FL);
			}
		}
		else if (c->IsTag("submenu") ||
				 c->IsTag("menuitem"))
		{
			ResMenuItem *i = new ResMenuItem(this);
			if (i && i->Read(c))
			{
			}
			else
			{
				Status = false;
				Ctx.Log.Print("%s:%i - Failed to read menu item.\n", _FL);
				DeleteObj(i);
			}
		}
		else
		{
			Ctx.Log.Print("%s:%i - Unexpected tag.\n", _FL);
			break;
		}
	}

	return Status;
}

bool ResMenu::Write(LXmlTag *t, SerialiseContext &Ctx)
{
	bool Status = true;

	// Write start tag
	t->SetTag("menu");
	if (Name()) t->SetAttr("Name", Name());

	// Write group
	if (Group)
	{
		LXmlTag *g = new LXmlTag;
		if (g && Group->Write(g, Ctx))
		{
			t->InsertTag(g);
		}
		else
		{
			Status = false;
			DeleteObj(g);
		}
	}

	// Write submenus/items
	for (auto i: Items)
	{
		ResMenuItem *m = dynamic_cast<ResMenuItem*>(i);
		if (m)
		{
			LXmlTag *c = new LXmlTag;
			if (c && m->Write(c, 1))
			{
				t->InsertTag(c);
			}
			else
			{
				Status = false;
				DeleteObj(c);
			}
		}
	}

	return Status;
}

//////////////////////////////////////////////////////////////////////////
ResMenuUi::ResMenuUi(ResMenu *Res)
{
	Menu = Res;
	Tools = 0;
	Status = 0;
	StatusInfo = 0;
}

ResMenuUi::~ResMenuUi()
{
	if (Menu)
	{
		Menu->Ui = 0;
	}
}

void ResMenuUi::OnPaint(LSurface *pDC)
{
	LRegion Client(0, 0, X()-1, Y()-1);
	for (auto w: Children)
	{
		LRect r = w->GetPos();
		Client.Subtract(&r);
	}

	pDC->Colour(L_MED);
	for (LRect *r = Client.First(); r; r = Client.Next())
	{
		pDC->Rectangle(r);
	}
}

void ResMenuUi::PourAll()
{
	LRegion Client(GetClient());
	LRegion Update;

	for (auto v: Children)
	{
		LRect OldPos = v->GetPos();
		Update.Union(&OldPos);

		if (v->Pour(Client))
		{
			if (!v->Visible())
			{
				v->Visible(true);
			}

			if (OldPos != v->GetPos())
			{
				// position has changed update...
				v->Invalidate();
			}

			Client.Subtract(&v->GetPos());
			Update.Subtract(&v->GetPos());
		}
		else
		{
			// make the view not visible
			v->Visible(false);
		}
	}

	for (int i=0; i<Update.Length(); i++)
	{
		Invalidate(Update[i]);
	}
}

void ResMenuUi::OnPosChange()
{
	PourAll();
}

void ResMenuUi::OnCreate()
{
	Tools = new LToolBar;
	if (Tools)
	{
		auto FileName = LFindFile("_MenuIcons.gif");
		if (FileName && Tools->SetBitmap(FileName, 16, 16))
		{
			Tools->Attach(this);

			Tools->AppendButton("New child menu", IDM_NEW_SUB);
			Tools->AppendButton("New menu item", IDM_NEW_ITEM);
			Tools->AppendButton("Delete menu item", IDM_DELETE_ITEM);
			Tools->AppendButton("Move item up", IDM_MOVE_UP);
			Tools->AppendButton("Move item down", IDM_MOVE_DOWN);
			Tools->AppendSeparator();

			Tools->AppendButton("New language", IDM_NEW_LANG, TBT_PUSH, true, 6);
			Tools->AppendButton("Delete language", IDM_DELETE_LANG, TBT_PUSH, true, 7);
		}
		else
		{
			DeleteObj(Tools);
		}
	}

	Status = new LStatusBar;
	if (Status)
	{
		Status->Attach(this);
		StatusInfo = Status->AppendPane("", 2);
	}

	ResFrame *Frame = new ResFrame(Menu);
	if (Frame)
	{
		Frame->Attach(this);
	}
}

LMessage::Result ResMenuUi::OnEvent(LMessage *Msg)
{
	switch (Msg->Msg())
	{
		case M_COMMAND:
		{
			Menu->OnCommand(Msg->A()&0xffff, (int)(Msg->A()>>16), (OsView) Msg->B());
			break;
		}
		case M_DESCRIBE:
		{
			char *Text = (char*) Msg->B();
			if (Text)
			{
				StatusInfo->Name(Text);
			}
			break;
		}
	}

	return LView::OnEvent(Msg);
}

int ResMenuUi::CurrentTool()
{
	return false;
}

void ResMenuUi::SelectTool(int i)
{
}
