#include "Lgi.h"
#include "GDataGrid.h"
#include "Resdefs.h"
#include "LOptionsFile.h"
#include "LDocApp.h"

#define MAX_PATCH		(3 * 128)

const char AppName[] = "Set List Editor";

int Cmp(LListItem *a, LListItem *b, int d)
{
	return a->Compare(b, d);
}

class App;
class Base : public LListItem
{
	int Idx;
	LAutoString Name;

protected:
	App *app;

public:
	Base(App *a) { app = a; Idx = -1; }

	int Compare(LListItem *bi, int f)
	{
		Base *b = dynamic_cast<Base*>(bi);
		if (!b)
			return 0;

		switch (f)
		{
			case 0:
				return Idx - b->Idx;
			case 1:
				if (Name && b->Name)
					return stricmp(Name, b->Name);
				break;
		}
		return 0;
	}

	int GetIdx()
	{
		return Idx;
	}

	void SetIdx(int i)
	{
		Idx = i;
	}

	char *GetText(int Col)
	{
		switch (Col)
		{
			case 0:
			{
				static char buf[32];

				if (Idx < 0)
					return 0;

				sprintf(buf, "%i", Idx);
				return buf;
			}
			case 1:
			{
				return Name;
			}
		}

		return 0;
	}

	bool SetText(const char *s, int Col)
	{
		switch (Col)
		{
			case 0:
				Idx = atoi(s);
				break;
			case 1:
			{
				if (Idx < 0)
				{
					int Used = 0;
					List<Base> b;
					GetList()->GetAll(b);
					for (Base *c=b.First(); c; c=b.Next())
					{
						if (c != this)
							Used = max(c->Idx, Used);
					}
					Idx = Used + 1;
				}

				return Name.Reset(NewStr(s));
				break;
			}
			default:
				return false;
		}

		return true;
	}

	virtual bool IsValid()
	{
		return Idx >= 0 && Name.Get() != 0;
	}

	virtual bool Serialize(bool Write, LXmlTag *t)
	{
		if (Write)
		{
			// Check that the editbox has been serialized to the item...
			

			// Object -> LXmlTag
			t->SetAttr("Idx", Idx);
			t->SetAttr("Name", Name);
		}
		else
		{
			// LXmlTag -> Object
			Idx = t->GetAsInt("Idx");
			Name.Reset(NewStr(t->GetAttr("Name")));
		}

		return true;
	}
};

class Song : public Base
{
public:
	LArray<int> Presets;
	Song(App *a) : Base(a) {  }
	bool Serialize(bool Write, LXmlTag *t)
	{
		if (Write)
		{
			// this -> LXmlTag
			char s[32];
			for (int i=0; i<Presets.Length(); i++)
			{
				LXmlTag *c = new LXmlTag("Preset");
				sprintf(s, "%i", Presets[i]);
				c->Content = NewStr(s);
				t->InsertTag(c);
			}
		}
		else
		{
			// LXmlTag -> this
			Presets.Length(0);
			for (LXmlTag *c=t->Children.First(); c; c=t->Children.Next())
			{
				if (c->IsTag("Preset") &&
					c->Content)
				{
					Presets.Add(atoi(c->Content));
				}
			}
		}

		return Base::Serialize(Write, t);
	}
};

class SetList : public Base
{
public:
	LArray<int> Songs;
	SetList(App *a) : Base(a) { }
	bool Serialize(bool Write, LXmlTag *t)
	{
		if (Write)
		{
			// this -> LXmlTag
			char s[32];
			for (int i=0; i<Songs.Length(); i++)
			{
				LXmlTag *c = new LXmlTag("Song");
				sprintf(s, "%i", Songs[i]);
				c->Content = NewStr(s);
				t->InsertTag(c);
			}
		}
		else
		{
			// LXmlTag -> this
			Songs.Length(0);
			for (LXmlTag *c=t->Children.First(); c; c=t->Children.Next())
			{
				if (c->IsTag("Song") &&
					c->Content)
				{
					Songs.Add(atoi(c->Content));
				}
			}
		}

		return Base::Serialize(Write, t);
	}
};

LListItem *CreatePreset(void *User);
LListItem *CreateSong(void *User);
LListItem *CreateSetList(void *User);

class App : public LDocApp<LOptionsFile>, public LResourceLoad
{
	GDataGrid *Presets, *Songs, *SetLists, *SongPresets, *SetListSongs;

public:
	App() : LDocApp<LOptionsFile>("SetListEdit")
	{
		Presets = Songs = SetLists = SongPresets = SetListSongs = 0;
		Name(AppName);
		SetQuitOnClose(true);
		if (_Create())
		{
			_LoadMenu();
			LoadFromResource(IDD_MAIN, this);
			SetPos(LRect(0, 0, 1000, 800));
			MoveToCenter();
			AttachChildren();

			if (GetViewById(IDC_PRESETS, Presets))
			{
				Presets->SetDndFormats("Pre", 0);
				Presets->SetColFlag(0, GDataGrid::GDG_READONLY);
				Presets->AddColumn("Patch", 50);
				Presets->AddColumn("Name", 200);
				Presets->SetFactory(CreatePreset, this);

				List<LListItem> it;
				char s[256];
				for (int i=0; i<MAX_PATCH; i++)
				{
					LListItem *n = Presets->NewItem();
					sprintf(s, "%i", i);
					n->SetText(s);
					it.Insert(n);
				}

				Presets->Insert(it);
			}
			else LAssert(0);

			if (GetViewById(IDC_SONGS, Songs))
			{
				Songs->SetDndFormats("Song", 0);
				Songs->SetColFlag(0, GDataGrid::GDG_READONLY);
				Songs->AddColumn("Index", 50);
				Songs->AddColumn("Name", 250);
				Songs->SetFactory(CreateSong, this);
			}
			else LAssert(0);

			if (GetViewById(IDC_SONG_PRESETS, SongPresets))
			{
				SongPresets->SetDndFormats(0, "Pre");
				SongPresets->AddColumn("Index", 50);
				SongPresets->AddColumn("Preset", 150);
				SongPresets->SetColFlag(0, GDataGrid::GDG_READONLY);
				SongPresets->SetColFlag(1, GDataGrid::GDG_READONLY);
			}
			else LAssert(0);

			if (GetViewById(IDC_SETLISTS, SetLists))
			{
				SetLists->SetColFlag(0, GDataGrid::GDG_READONLY);
				SetLists->AddColumn("Index", 50);
				SetLists->AddColumn("Name", 250);
				SetLists->SetFactory(CreateSetList, this);
			}
			else LAssert(0);
			
			if (GetViewById(IDC_SETLIST_SONGS, SetListSongs))
			{
				SetListSongs->SetDndFormats(0, "Song");
				SetListSongs->AddColumn("Index", 50);
				SetListSongs->AddColumn("Song", 150);
				SetListSongs->SetColFlag(0, GDataGrid::GDG_READONLY);
				SetListSongs->SetColFlag(1, GDataGrid::GDG_READONLY);
			}
			else LAssert(0);

			CanAddRecord(true);
			Visible(true);
		}
	}

	~App()
	{
		_Destroy();
	}
	
	Base *GetPreset(int Idx)
	{
		List<LListItem>::I it = Presets->Start();
		for (LListItem *i=*it; i; i=*++it)
		{
			char *s = i->GetText(0);
			if (s)
			{
				if (atoi(s) == Idx)
					return dynamic_cast<Base*>(i);
			}
		}
		return 0;
	}

	Song *GetSong(int Idx)
	{
		List<LListItem>::I it = Songs->Start();
		for (LListItem *i=*it; i; i=*++it)
		{
			char *s = i->GetText(0);
			if (s)
			{
				if (atoi(s) == Idx)
					return dynamic_cast<Song*>(i);
			}
		}
		return 0;
	}

	void OnSongSelected()
	{
		SongPresets->Empty();
		List<Song> s;
		if (Songs->GetSelection(s) &&
			s.Length() == 1)
		{
			Song *Cur = s.First();
			for (int i=0; i<Cur->Presets.Length(); i++)
			{
				char idx[32];
				sprintf(idx, "%i", Cur->Presets[i]);

				Base *Preset = GetPreset(Cur->Presets[i]);
				LListItem *it = SongPresets->NewItem();
				it->SetText(idx, 0);
				if (Preset)
					it->SetText(Preset->GetText(1), 1);
				SongPresets->Insert(it);
			}
		}
	}

	void OnSetListSelected()
	{
		SetListSongs->Empty();
		List<SetList> s;
		if (SetLists->GetSelection(s) &&
			s.Length() == 1)
		{
			SetList *Cur = s.First();
			for (int i=0; i<Cur->Songs.Length(); i++)
			{
				char idx[32];
				sprintf(idx, "%i", Cur->Songs[i]);

				Song *Sng = GetSong(Cur->Songs[i]);
				LListItem *it = SetListSongs->NewItem();
				it->SetText(idx, 0);
				if (Sng)
					it->SetText(Sng->GetText(1), 1);
				SetListSongs->Insert(it);
			}
		}
	}

	int OnNotify(LViewI *c, int f)
	{
		switch (c->GetId())
		{
			case IDC_SONG_PRESETS:
			{
				List<Song> s;
				Songs->GetSelection(s);
				if (s.Length() == 1 &&
					SongPresets)
				{
					Song *Cur = s.First();

					if (f == GLIST_NOTIFY_ITEMS_DROPPED)
					{
						GDataGrid::ItemArray *a = SongPresets->GetDroppedItems();
						for (int i=0; i<a->Length(); i++)
						{
							Base *b = dynamic_cast<Base*>((*a)[i]);
							if (b)
							{
								Cur->Presets.Add(b->GetIdx());
							}
						}
						OnSongSelected();
					}
					else if (f == GLIST_NOTIFY_DELETE)
					{
						GDataGrid::IndexArray *a = SongPresets->GetDeletedItems();
						if (a->Length() > 0)
						{
							LArray<int> New;
							for (int i=0; i<Cur->Presets.Length(); i++)
							{
								if (!a->HasItem(i))
									New.Add(Cur->Presets[i]);
							}
							Cur->Presets = New;
						}
					}
				}
				break;
			}
			case IDC_SONGS:
			{
				if (f == GLIST_NOTIFY_SELECT)
				{
					OnSongSelected();
				}
				break;
			}
			case IDC_SETLIST_SONGS:
			{
				List<SetList> s;
				SetLists->GetSelection(s);
				if (s.Length() == 1 &&
					SetListSongs)
				{
					SetList *Cur = s.First();

					if (f == GLIST_NOTIFY_ITEMS_DROPPED)
					{
						GDataGrid::ItemArray *a = SetListSongs->GetDroppedItems();
						for (int i=0; i<a->Length(); i++)
						{
							Song *b = dynamic_cast<Song*>((*a)[i]);
							if (b)
							{
								Cur->Songs.Add(b->GetIdx());
							}
						}
						OnSetListSelected();
					}
					else if (f == GLIST_NOTIFY_DELETE)
					{
						GDataGrid::IndexArray *a = SetListSongs->GetDeletedItems();
						if (a->Length() > 0)
						{
							LArray<int> New;
							for (int i=0; i<Cur->Songs.Length(); i++)
							{
								if (!a->HasItem(i))
									New.Add(Cur->Songs[i]);
							}
							Cur->Songs = New;
						}
					}
				}
				break;
			}
			case IDC_SETLISTS:
			{
				if (f == GLIST_NOTIFY_SELECT)
				{
					OnSetListSelected();
				}
				break;
			}
		}

		return LDocApp<LOptionsFile>::OnNotify(c, f);
	}

	void CanAddRecord(bool b)
	{
		if (Songs)
			Songs->CanAddRecord(true);
		if (SetLists)
			SetLists->CanAddRecord(true);
	}

	bool Load(LXmlTag *t, GDataGrid *d)
	{
		if (!t || !d)
			return false;

		CanAddRecord(false);
		d->Empty();
		List<LListItem> Lst;
		for (LXmlTag *c = t->Children.First(); c; c = t->Children.Next())
		{
			LListItem *i = d->NewItem();
			Base *b = dynamic_cast<Base*>(i);
			if (b)
			{
				if (b->Serialize(false, c))
					Lst.Insert(b);
			}
		}

		d->Insert(Lst);
		CanAddRecord(true);

		return true;
	}

	bool Save(char *ChildTagName, LXmlTag *t, GDataGrid *d)
	{
		List<Base> b;
		if (!d->GetAll(b))
			return false;

		for (Base *i=b.First(); i; i=b.Next())
		{
			if (i->IsValid())
			{
				LXmlTag *c = new LXmlTag(ChildTagName);
				if (!c)
					continue;

				if (i->Serialize(true, c))
					t->InsertTag(c);
			}
		}

		return true;
	}

	bool OpenFile(char *FileName, bool ReadOnly)
	{
		LFile f;
		if (f.Open(FileName, O_READ))
		{
			GXmlTree t;
			LXmlTag r;
			if (t.Read(&r, &f))
			{
				LXmlTag *Pre = r.GetTag("Presets");
				LXmlTag *Son = r.GetTag("Songs");
				LXmlTag *Set = r.GetTag("Sets");
				if (Pre && Son && Set)
				{
					if (Load(Pre, Presets) &&
						Load(Son, Songs) &&
						Load(Set, SetLists))
					{
						// Replace missing presets that are not in the saved file...
						List<Base> a;
						Presets->GetAll(a);
						GHashTbl<int,Base*> h(MAX_PATCH * 2, 0, -1);
						for (Base *b=a.First(); b; b=a.Next())
						{
							h.Add(b->GetIdx(), b);
						}
						for (int i=0; i<MAX_PATCH; i++)
						{
							if (!h.Find(i))
							{
								Base *n = dynamic_cast<Base*>(Presets->NewItem());
								n->SetIdx(i);
								Presets->Insert(n);
							}
						}
						Presets->Sort(Cmp, 0);

						return true;
					}
				}
			}
		}
		return false;
	}

	void GetFileTypes(LFileSelect *Dlg, bool Write)
	{
		Dlg->Type("Xml", "*.xml");
	}

	bool SaveFile(char *FileName)
	{
		LXmlTag r("Settings");
		LXmlTag *Pre = r.CreateTag("Presets");
		LXmlTag *Son = r.CreateTag("Songs");
		LXmlTag *Set = r.CreateTag("Sets");
		if (Save("Preset", Pre, Presets) &&
			Save("Song", Son, Songs) &&
			Save("Set", Set, SetLists))
		{
			LFile f;
			if (f.Open(FileName, O_WRITE))
			{
				GXmlTree t;
				f.SetSize(0);
				return t.Write(&r, &f);
			}
			else
			{
				LgiMsg(this, "Couldn't open '%s' for writing.", AppName, MB_OK, FileName);
			}
		}
		else
		{
			LgiMsg(this, "Error saving data to XML tags.", AppName, MB_OK);
		}

		return false;
	}
};

LListItem *CreatePreset(void *User)
{
	return new Base((App*)User);
}

LListItem *CreateSong(void *User)
{
	return new Song((App*)User);
}

LListItem *CreateSetList(void *User)
{
	return new SetList((App*)User);
}

int LgiMain(OsAppArguments &AppArgs)
{
	GApp a("application/x-set-list-editor", AppArgs);
	if (a.IsOk())
	{
		a.AppWnd = new App;
		a.Run();
	}
	return 0;
}
