#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 UI {
public:

#define eventhandler(name, ind) name.ChangeEvent.RegisterLambda([&]{ if(CurrentWildling) CurrentWildling->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();

		wildlings.SetContainer(navigation("List"));
		wildlings.SetHeight(navigation("List").GetUsableHeight()-newwildling.GetHeight()-2);
		wildlings.SetWidth(180);
		wildlings.ItemClickedEvent.Register(this,&UI::SelWildling);

		newwildling.Text="New";
		newwildling.SetContainer(navigation("List"));
		newwildling.Move(10, wildlings.GetHeight());
		newwildling.SetWidth(80);
		newwildling.ClickEvent.Register(this, &UI::NewWildling);

		delwildling.Text="Delete";
		delwildling.SetContainer(navigation("List"));
		delwildling.Move(newwildling.GetBounds().TopRight());
		delwildling.SetWidth(80);
		delwildling.ClickEvent.Register(this, &UI::DelWildling);

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

		auto nl=wildlingeditorganizer.nextline;

		wildlingeditorganizer
			<<"Name: "<<Name<<nl
			<<"XP reward: "<<XP<<nl
			<<"  + Lvl"<<XPLevel<<nl
			<<"HP: "<<HP<<nl
			<<"  + Lvl"<<HPLevel<<nl
			<<"Armor: "<<Armor<<nl
			<<"  + Lvl"<<ArmorLevel<<nl
			<<"Damage: "<<Damage<<nl
			<<"  + Lvl"<<DamageLevel<<nl
			<<"Cooldown: "<<CD<<nl
			<<"  - Lvl"<<CDLevel<<nl
			<<"Offhand CD: "<<OffhandCD<<nl
			<<"  - Lvl"<<OffhandCDLevel<<nl
			<<"Attack: "<<Attack<<nl
			<<"  + Lvl"<<AttackLevel<<nl
			<<"Dodge: "<<Dodge<<nl
			<<"  + Lvl"<<DodgeLevel<<nl
			<<"Parry: "<<Parry<<nl
			<<"  + Lvl"<<ParryLevel<<nl
			<<"Block: "<<Block<<nl
			<<"  + Lvl"<<BlockLevel<<nl
			<<"Range: "<<Range<<nl
			<<"  + Lvl"<<RangeLevel<<nl
			<<"Oversh Range: "<<Oversh<<nl
			<<"  + Lvl"<<OvershLevel<<nl
			<<"Arrow Spe: "<<ArrowSpe<<nl
			<<"Movement Speed: "<<movementSpeed<<nl
			<<"  + Lvl: "<<movementSpeedLevel<<nl
		;

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

				changed=true;
			}
		});

		eventhandler(XP, 1);
		eventhandler(XPLevel, 2);
		eventhandler(HP, 3);
		eventhandler(HPLevel, 4);
		eventhandler(Armor, 5);
		eventhandler(ArmorLevel, 6);
		eventhandler(Damage, 7);
		eventhandler(DamageLevel, 8);
		eventhandler(CD, 9);
		eventhandler(CDLevel, 10);
		eventhandler(OffhandCD, 11);
		eventhandler(OffhandCDLevel, 12);
		eventhandler(Attack, 13);
		eventhandler(AttackLevel, 14);
		eventhandler(Dodge, 15);
		eventhandler(DodgeLevel, 16);
		eventhandler(Parry, 17);
		eventhandler(ParryLevel, 18);
		eventhandler(Block, 19);
		eventhandler(BlockLevel, 20);
		eventhandler(Range, 21);
		eventhandler(RangeLevel, 22);
		eventhandler(Oversh, 23);
		eventhandler(OvershLevel, 24);
		eventhandler(ArrowSpe, 25);
		eventhandler(movementSpeed, 26);
		eventhandler(movementSpeedLevel, 27);
	}

	~UI() {
		wildlingedit.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() {
		wildlingedit.Hide();
		CurrentFile.New();
		CurrentFile.Root().Add(*new gorgon::Folder("Info"));

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

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

		CurrentWildling=NULL;
		wildlings.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:wildling").name="ID";

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

		ListWildlings();

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

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

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

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

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

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

		d.SetCaption(ss.str());
		d.Add(ss.str()).name="Name";
		d.Add(10.0f).name="XP";
		d.Add(5.0f).name="XPLevel";
		d.Add(50.0f).name="HP";
		d.Add(40.0f).name="HPLevel";
		d.Add(0.0f).name="Armor";
		d.Add(1.0f).name="ArmorLevel";
		d.Add(50.0f).name="Damage";
		d.Add(40.0f).name="DamageLevel";
		d.Add(1.0f).name="CD";
		d.Add(0.05f).name="CDLevel";
		d.Add(0.0f).name="OffhandCD";
		d.Add(0.0f).name="OffhandCDLevel";
		d.Add(40.0f).name="Attack";
		d.Add(10.0f).name="AttackLevel";
		d.Add(30.0f).name="Dodge";
		d.Add(10.0f).name="DodgeLevel";
		d.Add(30.0f).name="Parry";
		d.Add(10.0f).name="ParryLevel";
		d.Add(40.0f).name="Block";
		d.Add(15.0f).name="BlockLevel";
		d.Add(0.0f).name="Range";
		d.Add(0.0f).name="RangeLevel";
		d.Add(0.0f).name="Oversh";
		d.Add(0.0f).name="OvershLevel";
		d.Add(0.0f).name="ArrowSpe";
		d.Add(0.0f).name="MS";
		d.Add(0.0f).name="MSLevel";

		CurrentFile.Root().Add(d);

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

		changed=true;
	}

	void SelWildling() {
		if(wildlings.GetSelectedItem()) {
			wildlingedit.Show();
			CurrentWildling=&CurrentFile.Root().Get<gorgon::DataArray>(wildlings.FindLocation(wildlings.GetSelectedItem())+1);
			
			auto &ci=*CurrentWildling;
			int ind=0;

			Name.Text=ci.getString(ind++);
			XP=ci.getFloat(ind++);
			XPLevel=ci.getFloat(ind++);
			HP=ci.getFloat(ind++);
			HPLevel=ci.getFloat(ind++);
			Armor=ci.getFloat(ind++);
			ArmorLevel=ci.getFloat(ind++);
			Damage=ci.getFloat(ind++);
			DamageLevel=ci.getFloat(ind++);
			CD=ci.getFloat(ind++);
			CDLevel=ci.getFloat(ind++);
			OffhandCD=ci.getFloat(ind++);
			OffhandCDLevel=ci.getFloat(ind++);
			Attack=ci.getFloat(ind++);
			AttackLevel=ci.getFloat(ind++);
			Dodge=ci.getFloat(ind++);
			DodgeLevel=ci.getFloat(ind++);
			Parry=ci.getFloat(ind++);
			ParryLevel=ci.getFloat(ind++);
			Block=ci.getFloat(ind++);
			BlockLevel=ci.getFloat(ind++);
			Range=ci.getFloat(ind++);
			RangeLevel=ci.getFloat(ind++);
			Oversh=ci.getFloat(ind++);
			OvershLevel=ci.getFloat(ind++);
			ArrowSpe=ci.getFloat(ind++);
			movementSpeed=ci.getFloat(ind++);
			movementSpeedLevel=ci.getFloat(ind++);
		}
		else {
			CurrentWildling=NULL;
			wildlingedit.Hide();
		}
	}

	void DelWildling() {
		if(CurrentWildling)
			CurrentFile.Root().Remove(CurrentWildling);

		ListWildlings();

		changed=true;
	}

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

	widgets::Listbox<> wildlings;
	widgets::Button newwildling;
	widgets::Button delwildling;

	widgets::InlinePanel wildlingedit;
	widgets::LinearOrganizer wildlingeditorganizer;

	widgets::Textbox Name;
	widgets::Numberbox<float> XP;
	widgets::Numberbox<float> XPLevel;
	widgets::Numberbox<float> HP;
	widgets::Numberbox<float> HPLevel;
	widgets::Numberbox<float> Armor;
	widgets::Numberbox<float> ArmorLevel;
	widgets::Numberbox<float> Damage;
	widgets::Numberbox<float> DamageLevel;
	widgets::Numberbox<float> CD;
	widgets::Numberbox<float> CDLevel;
	widgets::Numberbox<float> OffhandCD;
	widgets::Numberbox<float> OffhandCDLevel;
	widgets::Numberbox<float> Attack;
	widgets::Numberbox<float> AttackLevel;
	widgets::Numberbox<float> Dodge;
	widgets::Numberbox<float> DodgeLevel;
	widgets::Numberbox<float> Parry;
	widgets::Numberbox<float> ParryLevel;
	widgets::Numberbox<float> Block;
	widgets::Numberbox<float> BlockLevel;
	widgets::Numberbox<float> Range;
	widgets::Numberbox<float> RangeLevel;
	widgets::Numberbox<float> Oversh;
	widgets::Numberbox<float> OvershLevel;
	widgets::Numberbox<float> ArrowSpe;
	widgets::Numberbox<float> movementSpeed;
	widgets::Numberbox<float> movementSpeedLevel;


	gorgon::File CurrentFile;
	gorgon::DataArray *CurrentWildling;

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