#pragma once

#include <vector>
#include <string>
#include <Widgets/Main.h>
#include <Widgets/FullscreenPanel.h>
#include <Widgets/Tabpanel.h>
#include <Widgets/Listbox.h>
#include "../Common/FileHandling.h"
#include <iosfwd>
#include <Widgets/Dialogs/TextInput.h>
#include <Gorgon/Folder.h>
#include <Gorgon/Base.h>
#include <Gorgon/Game/Animation.h>
#include <Gorgon/Generic/DataArray.h>
#include <Widgets/Dialogs/Query.h>
#include <Widgets/Numberbox.h>
#include <Gorgon/Extended/Sound.h>
#include <Gorgon/Link.h>
#include <Widgets/InlinePanel.h>
#include <Widgets/Dialogs/Message.h>
#include <Utils/StringEnum.h>
#include <Widgets/Combobox.h>



class ItemType {
public:
	enum Type {
		Armor,
		Melee,
		Ranged,
		Thrust,
		Passive,
		end
	};

	static const char* Names[];
};

class ItemSlot {
public:
	enum Type {
		HeadArmor,
		BodyArmor,
		MainHand,
		OffHand,
		end
	};

	static const char* Names[];
};

typedef utils::StringEnum<ItemType> ItemTypeEnum;
typedef utils::StringEnum<ItemSlot> ItemSlotEnum;

class UI {
public:

#define eventhandler(name, ind) name.ChangeEvent.RegisterLambda([&]{ if(CurrentItem) CurrentItem->Get<resource::FloatData>(ind).value = name.Value; changed=true; });

	UI() {
		changed=false;

		CurrentFile.AddGameLoaders();
		CurrentFile.SetFileType(gorgon::GID::File::Game);

		os::window::Closing.RegisterLambda([&](bool &reply){
			if(changed) {
				AskQuit();
				reply=false;
			}
		});

		toplevel.Padding=0;


		navigation.SetContainer(toplevel);
		navigation.FillContainer();
		navigation.SetWidth(200);
		navigation.Add("Files");
		navigation.Add("List");
		navigation("List").Disable();
		navigation.reorganize();


		file.SetPanel(navigation("Files"));
		file.SetPanel(navigation("Files"));
		file.Quit.ClickEvent.RegisterLambda([&]{
			if(changed) AskQuit();
			else        Main.Exit(0);
		});
		file.New.ClickEvent.Register(this, &UI::NewFile);
		file.Load.ClickEvent.Register(this, &UI::LoadFile);
		file.Save.ClickEvent.RegisterLambda([&]{ actionafter=action_none; Save(); });
		file.SaveAs.ClickEvent.RegisterLambda([&]{ actionafter=action_none; AskSavename(); });
		file.RefreshFiles();

		items.SetContainer(navigation("List"));
		items.SetHeight(navigation("List").GetUsableHeight()-newitem.GetHeight()-2);
		items.SetWidth(180);
		items.ItemClickedEvent.Register(this,&UI::SelItem);

		newitem.Text="New";
		newitem.SetContainer(navigation("List"));
		newitem.Move(10, items.GetHeight());
		newitem.SetWidth(80);
		newitem.ClickEvent.Register(this, &UI::NewItem);

		delitem.Text="Delete";
		delitem.SetContainer(navigation("List"));
		delitem.Move(newitem.GetBounds().TopRight());
		delitem.SetWidth(80);
		delitem.ClickEvent.Register(this, &UI::DelItem);

		itemedit.SetContainer(toplevel);
		itemedit.Move(200, 30);
		itemedit.Resize(toplevel.GetUsableWidth()-200, toplevel.GetUsableHeight()-itemedit.GetY()-1);
		itemedit.SetOrganizer(itemeditorganizer);
		itemedit.Hide();

		Type.SetValue(ItemTypeEnum::end);
		Slot.SetValue(ItemSlotEnum::end);


		ItemTypeEnum::AddAll(Type);
		ItemSlotEnum::AddAll(Slot);

		auto nl=itemeditorganizer.nextline;

		itemeditorganizer
			<<"Name: "<<Name<<nl
			<<"Type: "<<Type<<nl
			<<"Slot: "<<Slot<<nl
			<<"Artisan: "<<Artisan<<nl
			<<"Buildings: "<<Buildings<<nl
			<<"Cost: "<<Cost<<"gold"<<nl
			<<"XP reward: "<<XP<<nl
			<<"*Craft time: *"<<CraftTimeBase<<"msec"<<nl
			<<"  - Spe"<<CraftTimeSpe<<nl
			<<"  - Lvl"<<CraftTimeLevel<<nl
			<<"*Stamina cost: *"<<StaminaCost<<nl
			<<"*Damage: *"<<DamageBase<<nl
			<<"  + Str"<<DamageStr<<nl
			<<"  + Dex"<<DamageDex<<nl
			<<"  + Lvl"<<DamageLevel<<nl
			<<"*Range: *"<<RangeBase<<"pix"<<nl
			<<"  + Str"<<RangeStr<<"pix"<<nl
			<<"*Cooldown: *"<<CooldownBase<<"msec"<<nl
			<<"  - Spe"<<CooldownSpe<<"msec"<<nl
			<<"  - Level"<<CooldownLevel<<"msec"<<nl
			<<"*Precision: *"<<PrecisionBase<<nl
			<<"  + Acc"<<PrecisionAcc<<nl
			<<"  + Level"<<PrecisionLevel<<nl
			<<"*Parry: *"<<ParryBase<<nl
			<<"  + Acc"<<ParryAcc<<nl
			<<"  + Int"<<ParryInt<<nl
			<<"  + Level"<<ParryLevel<<nl
			<<"*Block: *"<<BlockBase<<nl
			<<"  + Acc"<<BlockAcc<<nl
			<<"  + Level"<<BlockLevel<<nl
			<<"*A. Pierce: *"<<ArmorPierceBase<<nl
			<<"  + Acc"<<ArmorPierceAcc<<nl
			<<"  + Level"<<ArmorPierceLevel<<nl
			<<"*Critical: *"<<CriticalBase<<"att"<<nl
			<<"  - Acc"<<CriticalAcc<<"att"<<nl
			<<"  - Level"<<CriticalLevel<<"att"<<nl
			<<"*Dam. Red.: *"<<DamRed<<nl
			<<"*Acc Mod.: *"<<AccMod<<nl
			<<"*Spe Mod.: *"<<SpeMod<<nl
			<<"*CD Mod.: *"<<CooldownMod<<"-%"<<nl
			<<"*Stamina drain:*"<<StaminaDrain<<"+%"<<nl
			<<"*Move spe: *"<<MoveSpeBase<<"-%"<<nl
			<<"  - Str"<<MoveSpeStr<<"-%"<<nl
			<<"*Sight: *"<<Sightrange<<"-%"<<nl
			<<"*Stagger: *"<<StaggerBase<<"+%"<<nl
			<<"  + Str"<<StaggerStr<<"+%"<<nl
			<<"  + Level"<<StaggerLevel<<"+%"<<nl
			<<"*Stagger Red.: *"<<StaggerRed<<nl
			<<"*Overshoot range: *"<<OvershRangeBase<<"pix"<<nl
			<<"  + Str"<<OvershRangeStr<<"pix"<<nl
			<<"  + Level"<<OvershRangeLevel<<"pix"<<nl
			<<"    Min"<<OvershRangeMin<<"pix"<<nl
			<<"*Arrow spe: *"<<ArrowSpeBase<<"p/s"<<nl
			<<"  + Str"<<ArrowSpeStr<<"p/s"<<nl
			<<"    Limit"<<ArrowSpeLimit<<"p/s"<<nl
		;

		Name.ChangeEvent.RegisterLambda([&]{
			if(CurrentItem) {
				CurrentItem->SetCaption(Name.Text);
				CurrentItem->Get<resource::StringData>(0).value=Name.Text;
				items.GetItem()->Value=Name.Text;

				changed=true;
			}
		});
		Type.ChangeEvent.RegisterLambda([&]{
			if(CurrentItem) CurrentItem->Get<resource::IntegerData>(1).value=Type.GetValue();
			changed=true;
		});
		Slot.ChangeEvent.RegisterLambda([&]{
			if(CurrentItem) CurrentItem->Get<resource::IntegerData>(2).value=Slot.GetValue();
			changed=true;
		});
		Artisan.ChangeEvent.RegisterLambda([&]{
			if(CurrentItem) CurrentItem->Get<resource::StringData>(3).value=Artisan.Text;
			changed=true;
		});
		Buildings.ChangeEvent.RegisterLambda([&]{
			if(CurrentItem) CurrentItem->Get<resource::StringData>(4).value=Buildings.Text;
			changed=true;
		});
		eventhandler(Cost, 5);
		eventhandler(XP, 6);
		eventhandler(CraftTimeBase, 7);
		eventhandler(CraftTimeSpe,  8);
		eventhandler(CraftTimeLevel,9);

		eventhandler(StaminaCost,10);

		eventhandler(DamageBase,11);
		eventhandler(DamageStr,12);
		eventhandler(DamageDex,13);
		eventhandler(DamageLevel,14);

		eventhandler(RangeBase,15);
		eventhandler(RangeStr,16);

		eventhandler(CooldownBase,17);
		eventhandler(CooldownSpe,18);
		eventhandler(CooldownLevel,19);

		eventhandler(PrecisionBase,20);
		eventhandler(PrecisionAcc,21);
		eventhandler(PrecisionLevel,22);

		eventhandler(ParryBase,23);
		eventhandler(ParryAcc,24);
		eventhandler(ParryInt,25);
		eventhandler(ParryLevel,26);

		eventhandler(BlockBase,27);
		eventhandler(BlockAcc,28);
		eventhandler(BlockLevel,29);

		eventhandler(ArmorPierceBase,30);
		eventhandler(ArmorPierceAcc,31);
		eventhandler(ArmorPierceLevel,32);

		eventhandler(CriticalBase,33);
		eventhandler(CriticalAcc,34);
		eventhandler(CriticalLevel,35);

		eventhandler(DamRed,36);
		eventhandler(AccMod,37);
		eventhandler(SpeMod,38);
		eventhandler(CooldownMod,39);

		eventhandler(MoveSpeBase,40);
		eventhandler(MoveSpeStr,41);

		eventhandler(StaggerBase,42);
		eventhandler(StaggerStr,43);
		eventhandler(StaggerLevel,44);

		eventhandler(StaggerRed,45);

		eventhandler(OvershRangeBase,46);
		eventhandler(OvershRangeStr,47);
		eventhandler(OvershRangeLevel,48);
		eventhandler(OvershRangeMin,49);

		eventhandler(ArrowSpeBase,50);
		eventhandler(ArrowSpeStr,51);
		eventhandler(ArrowSpeLimit,52);
		
		eventhandler(StaminaDrain, 53);
		eventhandler(Sightrange, 54);
	}

	~UI() {
		itemedit.RemoveOrganizer();
	}


	void SetDirectory(const std::string &dir) {
		file.SetDirectory(dir);
	}

	void AskQuit() {
		auto &d=widgets::dialog::AskConfirm("You have changed the file, do you wish to save?", "Quit");
		d.RepliedEvent.RegisterLambda([&](bool reply){
			if(reply) {
				actionafter=action_quit;
				Save();
				return;
			}
			else {
				Main.Exit(0);
			}
		});
		d.ShowCancel=true;
	}

	void AskSavename() {
		widgets::dialog::AskText("Enter the name for the file.", CurrentFile.GetFilename(), 
			"Save").RepliedEvent.RegisterLambda([&](const std::string &str) {
				CurrentFile.SetFilename(file.directory.Text+"/"+str);
				Save();
		});
	}

	bool Save() {
		if(CurrentFile.GetFilename()=="") {
			AskSavename();
			return false;
		}

		try {
			CurrentFile.SaveFile(CurrentFile.GetFilename());
			file.RefreshFiles();
		}
		catch(std::exception ex) {
			widgets::dialog::ShowMessage(std::string(ex.what())+"\nCheck if file path is valid.", "Save error");
		}
		changed=false;

		if(actionafter==action_newfile) {
			NewFile();
		}
		else if(actionafter==action_loadfile) {
			LoadFile();
		}
		else if(actionafter==action_quit) {
			Main.Exit(0);
		}

		return true;
	}

	void AskNew() {
		auto &d=widgets::dialog::AskConfirm("You have changed the file, do you wish to save?", "New file");
		d.RepliedEvent.RegisterLambda([&](bool reply){
			if(reply) {
				actionafter=action_newfile;
				Save();
				return;
			}
			else {
				ForceNewFile();
			}
		});
		d.ShowCancel=true;
	}

	void NewFile() {
		if(changed) {
			AskNew();
		}
		else {
			ForceNewFile();
		}
	}

	void ForceNewFile() {
		itemedit.Hide();
		CurrentFile.New();
		CurrentFile.Root().Add(*new gorgon::Folder("Info"));

		gorgon::DataArray &dinf=*new gorgon::DataArray;
		dinf.SetCaption("Info");
		dinf.Add("trinlibr:item").name="ID";

		CurrentFile.Root().Get<gorgon::Folder>(0).Add(dinf);
		CurrentItem=NULL;
		items.Destroy();
		navigation("List").Enable();
		file.Save.Enable();
		file.SaveAs.Enable();
		navigation.reorganize();
		navigation.Activate(navigation("List"));
	}

	void AskLoad() {
		auto &d=widgets::dialog::AskConfirm("You have changed the file, do you wish to save?", "Load file");
		d.RepliedEvent.RegisterLambda([&](bool reply){
			if(reply) {
				actionafter=action_loadfile;
				Save();
				return;
			}
			else {
				ForceLoadFile();
			}
		});
		d.ShowCancel=true;
	}

	void LoadFile() {
		if(changed) {
			AskLoad();
		}
		else
			ForceLoadFile();

	}

	void ForceLoadFile() {
		std::string filename=(std::string)file.directory.Text + "/" + file.lst.GetValue().Filename;

		CurrentFile.LoadFile(filename);
		CurrentFile.Prepare();

		if(CurrentFile.Root().Get<gorgon::Folder>(0).GetCount()==0) {
			gorgon::DataArray &dinf=*new gorgon::DataArray;
			dinf.SetCaption("Info");
			dinf.Add("trinlibr:item").name="ID";

			CurrentFile.Root().Get<gorgon::Folder>(0).Add(dinf);
		}

		ListItems();

		navigation("List").Enable();
		file.Save.Enable();
		file.SaveAs.Enable();
		navigation.reorganize();
		navigation.Activate(navigation("List"));
	}

	void ListItems() {
		items.Destroy();
		for(int i=1; i<CurrentFile.Root().GetCount();i++) {
			auto &d=CurrentFile.Root().Get<gorgon::DataArray>(i);

			items.Add(d.getString(0));
		}

		if(items.GetCount())
			items[0].Accessed();
	}

	void NewItem() {
		gorgon::DataArray &d=*new gorgon::DataArray;

		std::stringstream ss;
		ss<<"Item "<<CurrentFile.Root().GetCount();

		d.SetCaption(ss.str());
		d.Add(ss.str()).name="Name";
		d.Add((int)ItemTypeEnum::Melee).name="Type";
		d.Add((int)ItemSlotEnum::MainHand).name="Slot";
		d.Add("Blacksmith").name="Artisan";
		d.Add("Forge,Anvil").name="Buildings";
		d.Add(15.f).name="Cost";
		d.Add(5.f).name="XP";
		d.Add(60.f).name="Craft time";
		d.Add(1.f).name="  - Spe";
		d.Add(1.f).name="  - Level";
		d.Add(1.f).name="Stamina cost";
		d.Add(60.f).name="Damage";
		d.Add(.5f).name="  + Str";
		d.Add(0.f).name="  + Dex";
		d.Add(5.f).name="  + Level";
		d.Add(1.f).name="Range";
		d.Add(0.f).name="  + Str";
		d.Add(1.5f).name="Cooldown";
		d.Add(.03f).name="  - Spe";
		d.Add(.05f).name="  - Level";
		d.Add(30.f).name="Precision";
		d.Add(4.0f).name="  + Acc";
		d.Add(10.f).name="  + Level";
		d.Add(10.f).name="Parry";
		d.Add(3.0f).name="  + Acc";
		d.Add(2.0f).name="  + Int";
		d.Add(6.0f).name="  + Level";
		d.Add(30.f).name="Block";
		d.Add(2.0f).name="  + Acc";
		d.Add(14.f).name="  + Level";
		d.Add(0.0f).name="AP";
		d.Add(0.0f).name="  + Acc";
		d.Add(0.0f).name="  + Level";
		d.Add(9.0f).name="Critical";
		d.Add(.04f).name="  - Acc";
		d.Add(0.4f).name="  - Level";
		d.Add(0.0f).name="Dam. Red.";
		d.Add(0.0f).name="Acc Mod.";
		d.Add(0.0f).name="Spe Mod.";
		d.Add(0.0f).name="CD Mod. -%";
		d.Add(0.0f).name="Movespeed -%";
		d.Add(0.0f).name="  - Str";
		d.Add(0.0f).name="Stagger +%";
		d.Add(.005f).name="  + Str";
		d.Add(0.0f).name="  + Level";
		d.Add(0.0f).name="Stagger Red.";
		d.Add(0.0f).name="Overshoot range";
		d.Add(0.0f).name="  + Str";
		d.Add(0.0f).name="  + Level";
		d.Add(0.0f).name="  + Min";
		d.Add(0.0f).name="Arrow spe";
		d.Add(0.0f).name="  + Str";
		d.Add(0.0f).name="  + Limit";
		d.Add(0.0f).name="Stamina drain";
		d.Add(0.0f).name="Sight -%";


		CurrentFile.Root().Add(d);

		items.Add(ss.str());
		items.GetItem(items.GetCount()-1)->Accessed();

		changed=true;
	}

	void SelItem() {
		if(items.GetSelectedItem()) {
			itemedit.Show();
			CurrentItem=&CurrentFile.Root().Get<gorgon::DataArray>(items.FindLocation(items.GetSelectedItem())+1);
			
			auto &ci=*CurrentItem;
			int ind=0;

			Name.Text=ci.getString(ind++);
			Type.SetValue((ItemTypeEnum::Type)ci.getInt(ind++));
			Slot.SetValue((ItemSlotEnum::Type)ci.getInt(ind++));
			Artisan.Text=ci.getString(ind++);
			Buildings.Text=ci.getString(ind++);
			Cost.Value=ci.getFloat(ind++);
			XP.Value=ci.getFloat(ind++);

			CraftTimeBase.Value=ci.getFloat(ind++);
			CraftTimeSpe.Value=ci.getFloat(ind++);
			CraftTimeLevel.Value=ci.getFloat(ind++);

			StaminaCost.Value=ci.getFloat(ind++);

			DamageBase.Value=ci.getFloat(ind++);
			DamageStr.Value=ci.getFloat(ind++);
			DamageDex.Value=ci.getFloat(ind++);
			DamageLevel.Value=ci.getFloat(ind++);

			RangeBase.Value=ci.getFloat(ind++);
			RangeStr.Value=ci.getFloat(ind++);

			CooldownBase.Value=ci.getFloat(ind++);
			CooldownSpe.Value=ci.getFloat(ind++);
			CooldownLevel.Value=ci.getFloat(ind++);

			PrecisionBase.Value=ci.getFloat(ind++);
			PrecisionAcc.Value=ci.getFloat(ind++);
			PrecisionLevel.Value=ci.getFloat(ind++);

			ParryBase.Value=ci.getFloat(ind++);
			ParryAcc.Value=ci.getFloat(ind++);
			ParryInt.Value=ci.getFloat(ind++);
			ParryLevel.Value=ci.getFloat(ind++);

			BlockBase.Value=ci.getFloat(ind++);
			BlockAcc.Value=ci.getFloat(ind++);
			BlockLevel.Value=ci.getFloat(ind++);

			ArmorPierceBase.Value=ci.getFloat(ind++);
			ArmorPierceAcc.Value=ci.getFloat(ind++);
			ArmorPierceLevel.Value=ci.getFloat(ind++);

			CriticalBase.Value=ci.getFloat(ind++);
			CriticalAcc.Value=ci.getFloat(ind++);
			CriticalLevel.Value=ci.getFloat(ind++);

			DamRed.Value=ci.getFloat(ind++);
			AccMod.Value=ci.getFloat(ind++);
			SpeMod.Value=ci.getFloat(ind++);
			CooldownMod.Value=ci.getFloat(ind++);

			MoveSpeBase.Value=ci.getFloat(ind++);
			MoveSpeStr.Value=ci.getFloat(ind++);

			StaggerBase.Value=ci.getFloat(ind++);
			StaggerStr.Value=ci.getFloat(ind++);
			StaggerLevel.Value=ci.getFloat(ind++);

			StaggerRed.Value=ci.getFloat(ind++);

			OvershRangeBase.Value=ci.getFloat(ind++);
			OvershRangeStr.Value=ci.getFloat(ind++);
			OvershRangeLevel.Value=ci.getFloat(ind++);
			OvershRangeMin.Value=ci.getFloat(ind++);

			ArrowSpeBase.Value=ci.getFloat(ind++);
			ArrowSpeStr.Value=ci.getFloat(ind++);
			ArrowSpeLimit.Value=ci.getFloat(ind++);

			StaminaDrain.Value=ci.getFloat(ind++);
			Sightrange.Value=ci.getFloat(ind++);
		}
		else {
			CurrentItem=NULL;
			itemedit.Hide();
		}
	}

	void DelItem() {
		if(CurrentItem)
			CurrentFile.Root().Remove(CurrentItem);

		ListItems();

		changed=true;
	}

	widgets::FullscreenPanel toplevel;
	widgets::Tabpanel navigation;
	FileHandler file;

	widgets::Listbox<> items;
	widgets::Button newitem;
	widgets::Button delitem;

	widgets::InlinePanel itemedit;
	widgets::LinearOrganizer itemeditorganizer;

	widgets::Textbox Name;
	widgets::Combobox<ItemTypeEnum::Type, &ItemTypeEnum::ConvertToString> Type;
	widgets::Combobox<ItemSlotEnum::Type, &ItemSlotEnum::ConvertToString> Slot;
	widgets::Textbox Artisan;
	widgets::Textbox Buildings;
	widgets::Numberbox<float> Cost, XP;
	widgets::Numberbox<float> CraftTimeBase, CraftTimeSpe, CraftTimeLevel;
	widgets::Numberbox<float> StaminaCost;
	widgets::Numberbox<float> DamageBase, DamageStr, DamageDex, DamageLevel;
	widgets::Numberbox<float> RangeBase, RangeStr;
	widgets::Numberbox<float> CooldownBase, CooldownSpe, CooldownLevel;
	widgets::Numberbox<float> PrecisionBase, PrecisionDex, PrecisionAcc, PrecisionLevel;
	widgets::Numberbox<float> ParryBase, ParryDex, ParryAcc, ParryInt, ParryLevel;
	widgets::Numberbox<float> BlockBase, BlockDex, BlockAcc, BlockLevel;
	widgets::Numberbox<float> ArmorPierceBase, ArmorPierceDex, ArmorPierceAcc, ArmorPierceLevel;
	widgets::Numberbox<float> CriticalBase, CriticalDex, CriticalAcc, CriticalLevel;
	widgets::Numberbox<float> DamRed, DexMod, AccMod, SpeMod, CooldownMod;
	widgets::Numberbox<float> MoveSpeBase, MoveSpeStr;
	widgets::Numberbox<float> Sightrange;
	widgets::Numberbox<float> StaminaDrain;
	widgets::Numberbox<float> StaggerBase, StaggerStr, StaggerLevel;
	widgets::Numberbox<float> StaggerRed;
	widgets::Numberbox<float> OvershRangeBase, OvershRangeStr, OvershRangeLevel, OvershRangeMin;
	widgets::Numberbox<float> ArrowSpeBase, ArrowSpeStr, ArrowSpeLimit;




	gorgon::File CurrentFile;
	gorgon::DataArray *CurrentItem;

	bool changed;
	enum {
		action_none,
		action_newfile,
		action_loadfile,
		action_quit
	} actionafter;
};
