#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 <Gorgon/Widgets/Line.h>
#include <Widgets/InlinePanel.h>
#include <Widgets/Dialogs/Message.h>

#include <Utils/StringEnum.h>
#include <Widgets/Combobox.h>

#include <Gaming/Map2D.h>
#include <Widgets/LayerWidget.h>
#include "../Common/ImageList.h"
#include <map>
#include <Utils/SortedCollection.h>
#include "NewFile.h"
#include "MapInfo.h"
#include "MapEvents.h"

enum Role {
	Regular,
	Single1,
	Single2,
	Single3,
	Single4,
	TopLeft,
	Top,
	TopRight,
	Left,
	Right,
	BottomLeft,
	Bottom,
	BottomRight,
	D1,D2,D3,D4,
	Random1,
	Random2,
	Random3,
	Random4,
	Random5,
	Random6,
	end
};

class Rolee {
public:
	typedef Role Type;

	static const Role end=Type::end;

	static const char* Names[];
};

typedef utils::StringEnum<Rolee> RoleEnum;


static Role rolekeys[]={Single1, BottomLeft, Bottom, BottomRight, Left, Regular, Right, TopLeft, Top, TopRight};

Role invert(Role r) {
	switch(r) {
	case TopLeft:
		return D1;
	case Top:
		return Bottom;
	case TopRight:
		return D2;
	case Left:
		return Right;
	case Right:
		return Left;
	case BottomLeft:
		return D3;
	case Bottom:
		return Top;
	case BottomRight:
		return D4;
	case D4:
		return BottomRight;
	case D3:
		return BottomLeft;
	case D2:
		return TopRight;
	case D1:
		return TopLeft;
	default:
		return Regular;
	}
}

class MapCell {
public:
	MapCell(std::string Tiletype="", Role subid=Regular, std::string Against="") : Tiletype(Tiletype), Against(Against), Subid(subid), ind(0), inverted(false), fixed(false) {}

	std::string Tiletype;
	std::string Against;
	Role Subid;
	int ind;
	bool inverted;
	bool fixed;
};

enum State {
	On, Off, Any
};

class MapRule {
public:

	State states[9]; 

	Role role;
};

static const int MapRuleCount=14;
static const MapRule MapRules[]= {
	{Any,Off,Any,
	 Off,Any,On ,
	 Any,On ,Any, 
	 TopLeft},

	{Any,Off,Any,
	 On ,Any,On ,
	 Any,On ,Any, 
	 Top},

	{Any,Off,Any,
	 On ,Any,Off,
	 Any,On ,Any, 
	 TopRight},

	{Any,On ,Any,
	 Off,Any,On ,
	 Any,On ,Any, 
	 Left},

	{Any,On ,Any,
	 On ,Any,Off,
	 Any,On ,Any, 
	 Right},

	{Any,On ,Any,
	 Off,Any,On ,
	 Any,Off,Any, 
	 BottomLeft},

	{Any,On ,Any,
	 On ,Any,On ,
	 Any,Off,Any, 
	 Bottom},

	{Any,On ,Any,
	 On ,Any,Off,
	 Any,Off,Any, 
	 BottomRight},

	{On ,On ,On ,
	 On ,Any,On ,
	 On ,On ,Off, 
	 D1},

	{On, On ,On ,
	 On ,Any,On ,
	 Off,On ,On , 
	 D2},

	{On ,On ,Off,
	 On ,Any,On ,
	 On ,On ,On , 
	 D3},

	{Off,On ,On ,
	 On ,Any,On ,
	 On ,On ,On , 
	 D4},

	{On ,On ,On ,
	 On ,Any,On ,
	 On ,On ,On , 
	 Regular},

	{Any,Any,Any,
	 Any,Off,Any,
	 Any,Any,Any, 
	 Single1},
};

bool operator <(const utils::Point &l, const utils::Point &r) {
	if(l.y<r.y) return true;
	else if(l.y>r.y) return false;
	else return l.x<r.x;
}

bool operator >=(const utils::Point &l, const utils::Point &r) {
	return !(l<r);
}

class UI {
public:

	UI() {
		changed=false;

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

		TilesFile.AddGameLoaders();
		TilesFile.LoadFile("Tiles.gor");
		TilesFile.Prepare();
		TilesFolder=&TilesFile.Root();;
		TilesFolder->Remove(TilesFolder->GetPtr<gorgon::Base>(0));

		os::filesystem::DirectoryIterator dir("Map files/", "*.gor");

		ObjectsFolder=NULL;
		ObjectsFile.AddGameLoaders();
		for(;dir.IsValid();dir.Next()) {
			int zero=0;
			if(ObjectsFolder)
				zero=ObjectsFolder->GetCount();
			ObjectsFile.LoadFile("Map files/"+*dir, false);
			if(!ObjectsFolder)
				ObjectsFolder=&ObjectsFile.Root();
			ObjectsFolder->Remove(ObjectsFolder->GetPtr<gorgon::Base>(zero));
			ObjectsFolder->Remove(ObjectsFolder->GetPtr<gorgon::Base>(zero));
		}
		ObjectsFile.Prepare();

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

		toplevel.Padding=0;


		navigation.SetContainer(toplevel);
		navigation.FillContainer();
		navigation.SetWidth(230);
		navigation.Add("Files");
		navigation.Add("Tiles");
		navigation("Tiles").Disable();
		navigation.Add("Objects");
		navigation("Objects").Disable();
		navigation.reorganize();

		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();

		maplayer.SetContainer(toplevel);
		maplayer.Move(230, 0);
		maplayer.Resize(toplevel.GetWidth()-231, toplevel.GetHeight()-1);
		maplayer.GetLayer().Add(baselayer);
		baselayer.Add(tilelayer, 5);
		baselayer.Add(objectlayer,0);
		baselayer.Add(gridlayer,0);
		baselayer.Add(inputlayer,0);
		baselayer.Add(previewlayer,0);
		maplayer.Hide();

		inputlayer.MouseEvents.RegisterLambda([&](input::mouse::Event::Type event, utils::Point location,int amount) -> bool {
			previewlayer.Clear();
			static utils::Point prev;
			static utils::Point prevdrag;
			int px=32.f*(Zoom/100.f);
			utils::Point p=location/px;

			if(event==input::mouse::Event::Right_Down) {
				prevdrag=location;

				return true;
			}
			if(event==input::mouse::Event::Move && input::mouse::PressedButtons==input::mouse::Event::Right) {
				auto p=baselayer.BoundingBox.TopLeft();
				baselayer.Move(baselayer.BoundingBox.TopLeft()+location-prevdrag);
				if(p!=baselayer.BoundingBox.TopLeft())
					RenderTileMap();

				return true;
			}

			if(event==input::mouse::Event::Left_Down && input::keyboard::Modifier::Current==input::keyboard::Modifier::Alt) {
				std::stringstream ss;
				ss<<p;
				os::SetClipboardText(ss.str());

				return true;
			}

			
			if(navigation.GetActive()==&navigation("Tiles")) {
				if(event==input::mouse::Event::Left_Down) {
					prev=utils::Point(-1,-1);
					return true;
				}
				if(event==input::mouse::Event::Move && input::mouse::PressedButtons==input::mouse::Event::Left && prev!=p) {
					if(input::keyboard::Modifier::Current==input::keyboard::Modifier::Shift) {
						SetTile(p, true);
					}
					else {
						for(int yy=-(Brushsize-1);yy<=(Brushsize-1);yy++) {
							for(int xx=-(Brushsize-1);xx<=(Brushsize-1);xx++) {
								SetTile(p+utils::Point(xx,yy), Fixed);
							}
						}
					}

					RenderTileMap();

					prev=p;
					return true;
				}
			}
			else if(navigation.GetActive()==&navigation("Objects")) {
				if(event==input::mouse::Event::Left_Down) {
					if(input::keyboard::Modifier::Current==input::keyboard::Modifier::Ctrl) {
						RemoveObject(p);
					}
					else {
						PlaceObject(p);
					}

					return true;
				}
				else if(event==input::mouse::Event::Move) {
					resource::Image *img=ObjectsFolder->Get<gorgon::Folder>(Objects.FindLocation(*Objects.GetSelectedItem())).Get<gorgon::Folder>(1).Get<gorgon::Animation>(0).GetFrames()[0].Image;
					utils::Point off=ObjectsFolder->Get<gorgon::Folder>(Objects.FindLocation(*Objects.GetSelectedItem())).Get<gorgon::DataArray>(0).getPoint(1);

					previewlayer.SetCurrentColor(0x80ffffff);
					img->DrawStretched(previewlayer,p*px-off*px/32,img->GetSize()*px/32);
				}
			}

			if(event==input::mouse::Event::VScroll) {
				Zoom.Value=Zoom.Value+amount*10;
			}

			return false;
		}, utils::Bounds(0,0,100000,100000), input::mouse::Event::NoDragNoOverCheck);

		input::keyboard::Events.RegisterLambda([&](input::keyboard::Event event) -> bool {
			if(event.keycode>='0' && event.keycode<='9') {
				RoleSelection.SetValue(rolekeys[event.keycode-'0']);
			}
			else if(event.keycode==input::keyboard::KeyCodes::Home) RoleSelection.SetValue(D4);
			else if(event.keycode==input::keyboard::KeyCodes::PageUp) RoleSelection.SetValue(D3);
			else if(event.keycode==input::keyboard::KeyCodes::PageDown) RoleSelection.SetValue(D1);
			else if(event.keycode==input::keyboard::KeyCodes::End) RoleSelection.SetValue(D2);
			else if(event.keycode=='+') Brushsize.Value++;
			else if(event.keycode=='-') Brushsize.Value--;

			return false;
		});

		Brushsizel.SetContainer(navigation("Tiles"));
		Brushsizel.Text="Brush size:";
		Brushsizel.Move(0,0);

		Brushsize.SetContainer(navigation("Tiles"));
		Brushsize.Value=2;
		Brushsize.Move(Brushsizel.GetBounds().TopRight());

		Zooml.SetContainer(navigation("Tiles"));
		Zooml.Text="Zoom:";
		Zooml.Move(0, Brushsize.GetBounds().Bottom);
		
		Zoom.SetContainer(navigation("Tiles"));
		Zoom.Value=100;
		Zoom.Move(Brushsize.GetBounds().BottomLeft());
		Zoom.ChangeEvent.Register(this, &UI::RenderAllMap);

		Rolel.SetContainer(navigation("Tiles"));
		Rolel.Text="Role:";
		Rolel.Move(0,Zoom.GetBounds().Bottom);

		RoleSelection.SetContainer(navigation("Tiles"));
		RoleEnum::AddAll(RoleSelection);
		RoleSelection.SetValue(Regular);
		RoleSelection.Move(Zoom.GetBounds().BottomLeft());
		//RoleSelection.ChangeEvent.Register(this, &UI::RenderAllMap);

		Fixed.SetContainer(navigation("Tiles"));
		Fixed.Text="Fixed";
		Fixed.Move(0, RoleSelection.GetBounds().Bottom);


		Tiles.SetContainer(navigation("Tiles"));
		Tiles.Move(Fixed.GetBounds().BottomLeft());
		Tiles.Resize(navigation("Tiles").GetUsableSize()-utils::Size(1,131));
		Tiles.SetItemsize(40,40);
		for(int i=0;i<TilesFolder->GetCount();i++) {
			auto &f=TilesFolder->Get<gorgon::Folder>(i);
			gorgon::Image *img=NULL;
			for(int j=1;j<f.GetCount();j++) {
				auto &f2=f.Get<gorgon::Folder>(j);
				if(f2.Get<gorgon::DataArray>(0).getInt(1)==Regular) {
					img=&f2.Get<gorgon::Image>(1);
				}
			}
			if(img)
				Tiles.Add(img);
		}

		Tiles.GotFocus.RegisterLambda([&]{
			objectlayer.Ambient=0x80ffffff;
		});


		Settings.SetContainer(navigation("Tiles"));
		Settings.Text="Settings";
		Settings.Move(Tiles.GetBounds().BottomLeft());
		Settings.ClickEvent.RegisterLambda([&]{
			mapinfo.Name=CurrentFile.Root().Get<gorgon::Folder>(0).Get<gorgon::DataArray>(0).getString(1);
			mapinfo.Author=CurrentFile.Root().Get<gorgon::Folder>(0).Get<gorgon::DataArray>(0).getString(2);
			mapinfo.Citizens=CurrentFile.Root().Get<gorgon::Folder>(0).Get<gorgon::DataArray>(0).getInt(3);
			mapinfo.Info=CurrentFile.Root().Get<gorgon::Folder>(0).Get<gorgon::DataArray>(0).getString(4);
			mapinfo.Popcap=CurrentFile.Root().Get<gorgon::Folder>(0).Get<gorgon::DataArray>(0).getInt(5);
			mapinfo.Gold=CurrentFile.Root().Get<gorgon::Folder>(0).Get<gorgon::DataArray>(0).getInt(6);
			mapinfo.StartingHour=CurrentFile.Root().Get<gorgon::Folder>(0).Get<gorgon::DataArray>(0).getInt(7);

			mapinfo.Show();
		});

		mapinfo.Ok.ClickEvent.RegisterLambda([&]{
			CurrentFile.Root().Get<gorgon::Folder>(0).Get<gorgon::DataArray>(0).Get<resource::StringData>(1).value=mapinfo.Name;
			CurrentFile.Root().Get<gorgon::Folder>(0).Get<gorgon::DataArray>(0).Get<resource::StringData>(2).value=mapinfo.Author;
			CurrentFile.Root().Get<gorgon::Folder>(0).Get<gorgon::DataArray>(0).Get<resource::IntegerData>(3).value=mapinfo.Citizens;
			CurrentFile.Root().Get<gorgon::Folder>(0).Get<gorgon::DataArray>(0).Get<resource::StringData>(4).value=mapinfo.Info;
			CurrentFile.Root().Get<gorgon::Folder>(0).Get<gorgon::DataArray>(0).Get<resource::IntegerData>(5).value=mapinfo.Popcap;
			CurrentFile.Root().Get<gorgon::Folder>(0).Get<gorgon::DataArray>(0).Get<resource::IntegerData>(6).value=mapinfo.Gold;
			CurrentFile.Root().Get<gorgon::Folder>(0).Get<gorgon::DataArray>(0).Get<resource::IntegerData>(7).value=mapinfo.StartingHour;

			mapinfo.Close();
		});

		Events.SetContainer(navigation("Tiles"));
		Events.Text="Events";
		Events.Move(Settings.GetBounds().TopRight());
		Events.ClickEvent.RegisterLambda([&]{
			mapevents.Show();
		});



		Objects.SetContainer(navigation("Objects"));
		Objects.Resize(navigation("Objects").GetUsableSize()-utils::Size(1,61));
		Objects.SetItemsize(70,100);
		for(int i=0;i<ObjectsFolder->GetCount();i++) {
			auto &f=ObjectsFolder->Get<gorgon::Folder>(i);
			gorgon::Image *img=NULL;

			for(int j=1;j<f.GetCount();j++) {
				auto &f2=f.Get<gorgon::Folder>(j);
				if(f2.Get<gorgon::Animation>(0).GetNumberofFrames()) {
					img=dynamic_cast<gorgon::Image*>(f2.Get<gorgon::Animation>(0).GetFrames()[0].Image);
				}
			}
			if(img)
				Objects.Add(img);
		}
		objectinfo.SetContainer(navigation("Objects"));
		objectinfo.Text="";
		objectinfo.Move(Objects.GetBounds().BottomLeft());


		Objects.GotFocus.RegisterLambda([&]{
			objectlayer.Ambient=0xffffffff;
		});
		Objects.ItemClickedEvent.RegisterLambda([&]{
			objectinfo.Text=ObjectsFolder->Get<gorgon::Folder>(Objects.FindLocation(*Objects.GetSelectedItem())).Get<gorgon::DataArray>(0).getString(0);
		});

		//grid lines
		gorgon::Image &caps=*new gorgon::Image(1,1);
		((graphics::RGBint*)caps.GetData())[0]=0x60000000;
		gorgon::Animation &a=*new gorgon::Animation;
		a.Add(caps);
		gridh.SetSources(a, a, a);
		gridv.SetSources(a, a, a);
		gridv.Orientation=gridv.Vertical;
		gridhl=&gridh.CreateAnimation(true);
		gridvl=&gridv.CreateAnimation(true);
		caps.Prepare(Main, TilesFile);

		newfile.Ok.ClickEvent.Register(this, &UI::CreateNewFile);
	}

	~UI() {
		objectlist.Destroy();
	}

	void SetTile(utils::Point p, bool fixed=false, bool allowrandom=true) {
		if(!utils::Bounds(0,0,Map.GetWidth(),Map.GetHeight()).isInside(p)) return;

		if(!Tiles.GetSelectedItem()) return;

		std::string type=TilesFolder->Get<gorgon::Folder>(Tiles.FindLocation(*Tiles.GetSelectedItem())).GetName();

		//if(Map(p).Tiletype==type) return;

		std::string contender="";
		int againstme=Map.CheckNeighborsUsing(p.x, p.y,[&](MapCell &c) -> bool{
			if(c.Against==type)
				contender=c.Tiletype;

			return c.Against==type;
		});

		if(fixed) {
			if(RoleSelection.GetValue()==Regular)
				Map(p).Against="";
			else if(Map(p).Tiletype==type)
				Map(p).Against=Map(p).Against;
			else
				Map(p).Against=Map(p).Tiletype;

			Map(p).Subid=RoleSelection.GetValue();
			Map(p).Tiletype=type;
			Map(p).fixed=true;
		}
		else {
			std::string against;
			for(int i=0;i<MapRuleCount;i++) {
				bool fit=true;
				against="";
				for(int xx=-1;xx<=1;xx++) {
					for(int yy=-1;yy<=1;yy++) {
						if(MapRules[i].states[(yy+1)*3+xx+1]==On && Map.Get(p.x+xx, p.y+yy).Tiletype!=type) {
							fit=false;
							break;
						}
						if(MapRules[i].states[(yy+1)*3+xx+1]==Off && !(Map.Get(p.x+xx, p.y+yy).Tiletype!=type)/* && 
							(Map.Get(p.x+xx, p.y+yy).Subid==Regular || Map.Get(p.x+xx, p.y+yy).Subid>=Random1)*/) {
							fit=false;
							break;
						}
						else if(MapRules[i].states[(yy+1)*3+xx+1]==Off) {
							against=Map.Get(p.x+xx, p.y+yy).Tiletype;
						}
					}
					if(!fit) break;
				}

				if(fit) {
					if(againstme>0 && against==contender) {
						Map(p).Subid=Regular; 
						Map(p).Against="";
						Map(p).Tiletype=type;
					}
					else {
						Map(p).Subid=MapRules[i].role;
						Map(p).Against=against;
						Map(p).Tiletype=type;
						break;
					}
				}
			}
		}

		if(allowrandom) {
			if(Map(p).Subid==Regular) {
				int r=utils::Random(0,10);
				if(r==1)
					Map(p).Subid=Random1;
				else if(r==2)
					Map(p).Subid=Random2;
				else if(r==3)
					Map(p).Subid=Random3;
			}
			if(Map(p).Subid==Single1) {
				int r=utils::Random(0,1);
				if(r==0) 
					Map(p).Subid=Single2;
			}
		}

		if(!fixed) {
			CheckTile(utils::Point(p.x-1, p.y-1), allowrandom);
			CheckTile(utils::Point(p.x-0, p.y-1), allowrandom);
			CheckTile(utils::Point(p.x+1, p.y-1), allowrandom);
			CheckTile(utils::Point(p.x-1, p.y-0), allowrandom);
			CheckTile(utils::Point(p.x+1, p.y-0), allowrandom);
			CheckTile(utils::Point(p.x-1, p.y+1), allowrandom);
			CheckTile(utils::Point(p.x-0, p.y+1), allowrandom);
			CheckTile(utils::Point(p.x+1, p.y+1), allowrandom);
		}
	}

	void CheckTile(utils::Point p, bool allowrandom) {
		if(!utils::Bounds(0,0,Map.GetWidth(),Map.GetHeight()).isInside(p)) return;
		if(Map(p).fixed) return;
		std::string type=Map(p).Tiletype;
		//if(type!=TilesFolder->Get<gorgon::Folder>(Tiles.FindLocation(*Tiles.GetSelectedItem())).GetName()) return;

		std::string contender="";
		int againstme=Map.CheckNeighborsUsing(p.x, p.y,[&](MapCell &c) -> bool{
			if(c.Against==type)
				contender=c.Tiletype;

			return c.Against==type;
		});

		std::string against;
		for(int i=0;i<MapRuleCount;i++) {
			bool fit=true;
			against="";
			for(int xx=-1;xx<=1;xx++) {
				for(int yy=-1;yy<=1;yy++) {
					std::string tt=Map.Get(p.x+xx, p.y+yy).Tiletype;
					if(tt=="") tt=type;

					if(MapRules[i].states[(yy+1)*3+xx+1]==On && tt!=type) {
						fit=false;
						break;
					}
					if(MapRules[i].states[(yy+1)*3+xx+1]==Off && !(tt!=type)/* && 
						(Map.Get(p.x+xx, p.y+yy).Subid==Regular || Map.Get(p.x+xx, p.y+yy).Subid>=Random1))*/) {
						fit=false;
						break;
					}
					else if(MapRules[i].states[(yy+1)*3+xx+1]==Off) {
						against=Map.Get(p.x+xx, p.y+yy).Tiletype;
					}
				}
				if(!fit) break;
			}

			if(fit) {
				if(againstme>0 && against==contender) {
					Map(p).Subid=Regular;
					Map(p).Against="";
					Map(p).Tiletype=type;
				}
				else {
					Map(p).Subid=MapRules[i].role;
					Map(p).Against=against;
					Map(p).Tiletype=type;
					break;
				}
			}
		}

		if(allowrandom) {
			if(Map(p).Subid==Regular) {
				int r=utils::Random(0,15);
				if(r==1)
					Map(p).Subid=Random1;
				else if(r==2)
					Map(p).Subid=Random2;
				else if(r==3)
					Map(p).Subid=Random3;
			}
			if(Map(p).Subid==Single1) {
				int r=utils::Random(0,1);
				if(r==0) 
					Map(p).Subid=Single2;
			}
		}
	}

	void PlaceObject(utils::Point p) {
		if(!Objects.GetSelectedItem()) return;
		int *n=new int(Objects.FindLocation(*Objects.GetSelectedItem()));
		objectlist.Add(n, p);
		RenderObjects();
	}

	void RemoveObject(utils::Point p) {
		for(auto it=objectlist.First();it.IsValid();it.Next()) {
			if(it.GetKey()==p) {
				it.Delete();
				break;
			}
		}

		RenderObjects();
	}

	void RenderTileMap() {
		int px=32.f*(Zoom/100.f);

		tilelayer.Clear();
		int minx=std::max(0,(int)std::floor(float(-baselayer.BoundingBox.Left)/px));
		int miny=std::max(0,(int)std::floor(float(-baselayer.BoundingBox.Top)/px));
		int maxx=std::min(Map.GetWidth(), (int)std::ceil(float(-baselayer.BoundingBox.Left+maplayer.GetWidth())/px));
		int maxy=std::min(Map.GetHeight(), (int)std::ceil(float(-baselayer.BoundingBox.Top+maplayer.GetHeight())/px));

		for(int y=miny;y<maxy;y++) {
			for(int x=minx;x<maxx;x++) {
				MapCell &cell=Map(x,y);
				utils::Point p(x,y);
				auto &f=TilesFolder->Get<gorgon::Folder>(cell.Tiletype);
				bool found=false;
				cell.ind=-1;
				for(int i=1;i<f.GetCount();i++) {
					auto &f2=f.Get<gorgon::Folder>(i);
					if(f2.Get<gorgon::DataArray>(0).getInt(1)==cell.Subid) {
						if(cell.Subid==Regular || cell.Subid>=Random1 || f2.Get<gorgon::DataArray>(0).getString(2)=="" || f2.Get<gorgon::DataArray>(0).getString(2)==cell.Against) {
							f2.Get<gorgon::Image>(1).DrawStretched(tilelayer, p*px, px,px);
							cell.ind=i-1;
							cell.inverted=false;
							found=true;
							break;
						}
					}
				}
				if(!found && cell.Against!="") {
					auto &f=TilesFolder->Get<gorgon::Folder>(cell.Against);
					for(int i=1;i<f.GetCount();i++) {
						auto &f2=f.Get<gorgon::Folder>(i);
						if(f2.Get<gorgon::DataArray>(0).getInt(1)==invert(cell.Subid)) {
							if(f2.Get<gorgon::DataArray>(0).getString(2)==cell.Tiletype) {
								f2.Get<gorgon::Image>(1).DrawStretched(tilelayer, p*px,px,px);
								cell.ind=i-1;
								cell.inverted=true;
								break;
							}
						}
					}
				}
			}
		}
	}

	void CalculateIndexes() {
		Map.ForAllCells([&](const utils::Point &p, MapCell &cell) {
			auto &f=TilesFolder->Get<gorgon::Folder>(cell.Tiletype);
			bool found=false;
			cell.ind=-1;
			for(int i=1;i<f.GetCount();i++) {
				auto &f2=f.Get<gorgon::Folder>(i);
				if(f2.Get<gorgon::DataArray>(0).getInt(1)==cell.Subid) {
					if(cell.Subid==Regular || cell.Subid>=Random1 || f2.Get<gorgon::DataArray>(0).getString(2)=="" || f2.Get<gorgon::DataArray>(0).getString(2)==cell.Against) {
						cell.ind=i-1;
						cell.inverted=false;
						found=true;
						break;
					}
				}
			}
			if(!found && cell.Against!="") {
				auto &f=TilesFolder->Get<gorgon::Folder>(cell.Against);
				for(int i=1;i<f.GetCount();i++) {
					auto &f2=f.Get<gorgon::Folder>(i);
					if(f2.Get<gorgon::DataArray>(0).getInt(1)==invert(cell.Subid)) {
						if(f2.Get<gorgon::DataArray>(0).getString(2)==cell.Tiletype) {
							cell.ind=i-1;
							cell.inverted=true;
							break;
						}
					}
				}
			}
		});
	}
	
	void RenderGrid() {
		gridlayer.Clear();

		int px=32.f*(Zoom/100.f);
		for(int x=1;x<Map.GetWidth();x++) {
			gridvl->DrawIn(gridlayer, utils::Bounds(x*px, 0, x*px, Map.GetHeight()*px));
		}

		for(int y=1;y<Map.GetHeight();y++) {
			gridhl->DrawIn(gridlayer, utils::Bounds(0, y*px, Map.GetWidth()*px, y*px));
		}
	}

	void RenderObjects() {
		objectlayer.Clear();
		int px=32.f*(Zoom/100.f);

		for(auto it=objectlist.First();it.IsValid();it.Next()) {
			resource::Image *img=ObjectsFolder->Get<gorgon::Folder>(*it).Get<gorgon::Folder>(1).Get<gorgon::Animation>(0).GetFrames()[0].Image;
			utils::Point p=ObjectsFolder->Get<gorgon::Folder>(*it).Get<gorgon::DataArray>(0).getPoint(1);
			img->DrawStretched(objectlayer,it.GetKey()*px-(p*px/32),img->GetSize()*px/32);
		}
	}

	void RenderAllMap() {
		baselayer.Resize(Map.GetSize()*32);
		tilelayer.Resize(Map.GetSize()*32);
		gridlayer.Resize(Map.GetSize()*32);
		inputlayer.Resize(Map.GetSize()*32);

		RenderTileMap();
		RenderGrid();
		RenderObjects();
	}


	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();
		});
	}

	void SaveData() {
		CalculateIndexes();

		auto &mg=CurrentFile.Root().Get<gorgon::DataArray>(1);

		mg.DeleteAll();
		mg.Add(Map.GetSize()).name="Size";

		for(int y=0;y<Map.GetHeight();y++) {
			std::stringstream ss, ss2;
			for(int x=0;x<Map.GetWidth();x++) {
				if(Map(x,y).inverted)
					ss<<Map(x,y).Against<<"|"<<Map(x,y).ind<<",";
				else
					ss<<Map(x,y).Tiletype<<"|"<<Map(x,y).ind<<",";
			}
			ss2<<"Row "<<y+1;
			mg.Add(ss.str()).name=ss2.str();
		}

		auto &me=CurrentFile.Root().Get<gorgon::DataArray>(2);

		me.DeleteAll();
		me.Add(Map.GetSize()).name="Size";

		for(int y=0;y<Map.GetHeight();y++) {
			std::stringstream ss, ss2;
			for(int x=0;x<Map.GetWidth();x++) {
				ss<<Map(x,y).Tiletype<<"|"<<Map(x,y).Subid<<"|"<<Map(x,y).Against<<",";
			}
			ss2<<"Row "<<y+1;
			me.Add(ss.str()).name=ss2.str();
		}

		auto &objs=CurrentFile.Root().Get<gorgon::DataArray>(3);
		objs.DeleteAll();

		for(auto it=objectlist.First();it.IsValid();it.Next()) {
			objs.Add(ObjectsFolder->Get<gorgon::Folder>(*it).Get<gorgon::DataArray>(0).getString(0));
			objs.Add(ObjectsFolder->Get<gorgon::Folder>(*it).Get<gorgon::DataArray>(0).getInt(2));
			objs.Add(it.GetKey());
		}
	}

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

		try {
			SaveData();
			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() {
		newfile.Show(true);
	}

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

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

		gorgon::DataArray &dinf=*new gorgon::DataArray;
		dinf.SetCaption("Info");
		dinf.Add("trinlibr:map").name="ID";
		dinf.Add("New map").name="Name";
		dinf.Add("Unknown").name="Author";
		dinf.Add(0).name="Citizens";
		dinf.Add("????").name="Info";
		dinf.Add(50).name="Population cap";
		dinf.Add(200).name="Starting gold";
		dinf.Add(9).name="Starting hour";
		CurrentFile.Root().Get<gorgon::Folder>(0).Add(dinf);

		gorgon::DataArray &gamemap=*new gorgon::DataArray;
		gamemap.SetCaption("Game map");
		gamemap.Add(utils::Size(40,40)).name="Size";
		CurrentFile.Root().Add(gamemap);

		gorgon::DataArray &editmap=*new gorgon::DataArray;
		editmap.SetCaption("Edit map");
		editmap.Add(utils::Size(40,40)).name="Size";
		CurrentFile.Root().Add(editmap);

		gorgon::DataArray &objects=*new gorgon::DataArray;
		objects.SetCaption("Object list");
		CurrentFile.Root().Add(objects);

		gorgon::DataArray &events=*new gorgon::DataArray;
		events.SetCaption("Event list");
		CurrentFile.Root().Add(events);
		mapevents.target=&events;

		Map.NonPodResize(newfile.Width,newfile.Height);
		int total=newfile.Width*newfile.Height;
		Map.Clear(MapCell(newfile.BG));
		Map.SetN(total/15, 0,  MapCell(newfile.BG, Random1));
		Map.SetN(total/15, total/15, MapCell(newfile.BG, Random2));
		Map.SetN(total/15, 2*total/15, MapCell(newfile.BG, Random3));
		Map.Shuffle();

		objectlist.Destroy();

		RenderAllMap();
		maplayer.Show();

		navigation("Tiles").Enable();
		navigation("Objects").Enable();
		file.Save.Enable();
		file.SaveAs.Enable();
		navigation.reorganize();
		navigation.Activate(navigation("Tiles"));

		newfile.Close();
	}

	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 LoadData() {
		auto &me=CurrentFile.Root().Get<gorgon::DataArray>(2);

		Map.NonPodResize(me.getSize(0));

		for(int y=0;y<Map.GetHeight();y++) {
			std::stringstream ss;
			ss.str(me.getString(y+1));
			for(int x=0;x<Map.GetWidth();x++) {
				std::string s;
				std::stringstream ss2;
				int tmp;

				getline(ss, s, '|');

				Map(x,y).Tiletype=s;

				getline(ss, s, '|');
				ss2.str(s);
				ss2>>tmp;
				Map(x,y).Subid=(Role)tmp;

				getline(ss, s, ',');
				Map(x,y).Against=s;
			}
		}

		objectlist.Destroy();
		auto &objs=CurrentFile.Root().Get<gorgon::DataArray>(3);

		for(int i=0;i<objs.GetCount();i+=3) {
			std::string id=objs.getString(i);
			objectlist.Add(new int(ObjectsFolder->Subitems.FindLocation(ObjectsFolder->Get<gorgon::Folder>(id))), objs.getPoint(i+2));
		}

		RenderAllMap();
	}

	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:map").name="ID";

			CurrentFile.Root().Get<gorgon::Folder>(0).Add(dinf);
		}
		if(CurrentFile.Root().Get<gorgon::Folder>(0).Get<gorgon::DataArray>(0).GetCount()==1) {
			CurrentFile.Root().Get<gorgon::Folder>(0).Get<gorgon::DataArray>(0).Add("New map").name="Name";
			CurrentFile.Root().Get<gorgon::Folder>(0).Get<gorgon::DataArray>(0).Add("Unknown").name="Author";
			CurrentFile.Root().Get<gorgon::Folder>(0).Get<gorgon::DataArray>(0).Add(0).name="Citizens";
			CurrentFile.Root().Get<gorgon::Folder>(0).Get<gorgon::DataArray>(0).Add(50).name="Population cap";
			CurrentFile.Root().Get<gorgon::Folder>(0).Get<gorgon::DataArray>(0).Add("???").name="Info";
		}
		if(CurrentFile.Root().Get<gorgon::Folder>(0).Get<gorgon::DataArray>(0).GetCount()==6) {
			CurrentFile.Root().Get<gorgon::Folder>(0).Get<gorgon::DataArray>(0).Add(200).name="Starting gold";
			CurrentFile.Root().Get<gorgon::Folder>(0).Get<gorgon::DataArray>(0).Add(9).name="Starting hour";
		}
		if(CurrentFile.Root().GetCount()==4) {
			gorgon::DataArray &devents=*new gorgon::DataArray;
			devents.SetCaption("Events");

			CurrentFile.Root().Add(devents);
		}
		mapevents.target=&CurrentFile.Root().Get<gorgon::DataArray>(4);


		LoadData();
		maplayer.Show();

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


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

	widgets::Label Brushsizel;
	widgets::Numberbox<int> Brushsize;

	widgets::Label Zooml;
	widgets::Numberbox<int> Zoom;

	widgets::Label Rolel;
	widgets::Combobox<RoleEnum::Type, RoleEnum::ConvertToString> RoleSelection;

	widgets::Checkbox Fixed;

	widgets::Label objectinfo;

	widgets::LayerWidget maplayer;
	LayerBase baselayer;
	graphics::Basic2DLayer tilelayer;
	graphics::Colorizable2DLayer objectlayer;
	graphics::Basic2DLayer gridlayer;
	graphics::Colorizable2DLayer previewlayer;
	InputLayer inputlayer;

	widgets::Button Settings;
	widgets::Button Events;

	MapInfo mapinfo;
	MapEvents mapevents;

	gorgon::File CurrentFile;
	gorgon::File TilesFile;
	gorgon::Folder *TilesFolder;

	gorgon::File ObjectsFile;
	gorgon::Folder *ObjectsFolder;

	ImageList Tiles;
	ImageList Objects;

	gaming::Map2D<MapCell> Map;
	gorgon::widgets::Line gridh, gridv;
	widgets::Line *gridhl, *gridvl;

	utils::SortedCollection<int, utils::Point> objectlist;

	NewFileDialog newfile;

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