#include <string>
#include <vector>
#include <iosfwd>
#include <Widgets/Main.h>
#include <Widgets/Dialogs/Message.h>

#include "Instantiation.h"
#include "Assets/Load.h"
#include "Graphics/Map.h"
#include "Mechanics/Map.h"
#include "Mechanics/PlayerGame.h"
#include "Mechanics/Object.h"
#include "Interface/Resources.h"
#include "Interface/Infopanel.h"
#include "Interface/General.h"
#include "Interface/Buttons.h"
#include "Interface/GameLog.h"
#include "Interface/Actions.h"
#include "Interface/IngameMenu.h"
#include "Interface/EventDisplay.h"
#include "Interface/MapSelection.h"
#include "Interface/TitleMenu.h"

using namespace std;

static const int SCROLL_RANGE=10;//px

int Application(std::vector<std::string> &arguments) 
{ 
	gge::os::IconHandle icon=0;
	gge::resource::Image iconimage;
	if(iconimage.ImportPNG("Icon.png")==gge::resource::Image::NoError)
		icon=gge::os::IconFromImage(iconimage);

	gge::widgets::InitializeApplication("trinlibr", "Trouble in Libreland", 900, 690, icon);

	gge::os::window::Destroyed.RegisterLambda([]{
		gge::Main.Exit(0);
	});



	assets::loadAll("Assets");
	assets::resolve();


	graphics::Map::initialize(gge::Main.BoundingBox.GetSize());
	graphics::Map::getBaseLayer().IsVisible=false;
	gge::Main.Add(graphics::Map::current().getBaseLayer());


	//title screen
	gge::graphics::Basic2DLayer titleLayer;
	gge::Main.Add(titleLayer);
	gge::widgets::WR.Pictures("intro").DrawIn(titleLayer, gge::graphics::SizeController2D::SingleMiddleCenter);

	gge::widgets::WidgetLayer input;
	graphics::Map::getBaseLayer().Add(input);
	input.BoundingBox.SetSize(graphics::Map::getBaseLayer().BoundingBox.GetSize());


	//mechanics::PlayerGame::newGame();
	//assets::loadMap("Maps/Test");


	gge::widgets::TopLevel.SetOrder(-1);
	gge::widgets::TopLevel.Hide();

	gge::widgets::WidgetLayer toptoplayer;
	gge::Main.Add(toptoplayer, -2);
	gge::widgets::VirtualPanel toptoplevel;
	toptoplevel.LandOn(toptoplayer);
	toptoplayer.MouseCallback.SetLambda([] {return true;});
	toptoplayer.IsVisible=false;

	auto s=gge::widgets::WR.Borders.Selection->CreateAnimation(true);
	s.DrawIn(toptoplayer);
	s.DrawIn(toptoplayer);
	gge::widgets::WR.Fonts.H2.Print(toptoplayer, 0,100, gge::Main.BoundingBox.Width(), "Paused", gge::TextAlignment::Center);

	gge::widgets::WidgetLayer titlelayer;
	gge::Main.Add(titlelayer, -1);
	gge::widgets::VirtualPanel titlelevel;
	titlelevel.LandOn(titlelayer);
	//titlelevel.Hide();


	interface::initialize();
	interface::Resources	resources;
	interface::Infopanel	infopanel;
	interface::Buttons		buttons;
	interface::GameLog		gameLog;
	interface::MapSelection mapselect;
	interface::Credits		creditsWindow;



	mapselect.ClosingEvent.RegisterLambda([&]{toptoplayer.IsVisible=false; toptoplevel.Deactivate();});
	interface::showIngameMapSelection=[&] {
		mapselect.SetContainer(toptoplevel);
		toptoplayer.IsVisible=true;
		mapselect.Show();
	};
	interface::showTitleMapSelection=[&] {
		mapselect.SetContainer(titlelevel);
		mapselect.Show();
	};
	interface::closeMapSelection=[&] {
		mapselect.Close();
	};

	creditsWindow.ClosingEvent.RegisterLambda([&]{toptoplayer.IsVisible=false; toptoplevel.Deactivate();});
	interface::showIngameCredits=[&] {
		creditsWindow.SetContainer(toptoplevel);
		toptoplayer.IsVisible=true;
		creditsWindow.Show();
	};
	interface::showTitleCredits=[&] {
		creditsWindow.SetContainer(titlelevel);
		creditsWindow.Show();
	};
	interface::closeCredits=[&] {
		creditsWindow.Close();
	};



	interface::IngameMenu   ingameMenu;
	ingameMenu.SetContainer(toptoplevel);
	ingameMenu.ClosingEvent.RegisterLambda([&]{toptoplayer.IsVisible=false; toptoplevel.Deactivate();});

	interface::eventDisplay->SetContainer(toptoplevel);
	interface::eventDisplay->ShowingEvent.RegisterLambda([&]{toptoplayer.IsVisible=true; });
	interface::eventDisplay->ClosingEvent.RegisterLambda([&]{toptoplayer.IsVisible=false; toptoplevel.Deactivate();});
	int index = 0;

	interface::TitleMenu	titleMenu;
	titleMenu.SetContainer(titlelevel);
	titleMenu.Move(titlelevel.GetWidth()-titleMenu.GetWidth()-80, (titlelevel.GetHeight()-titleMenu.GetHeight())/2);


	
	float SCROLL_AMOUNT = 500.f;
	float SCROLL_AMOUNT_BIG = 6000.f;

	gge::input::keyboard::Events.RegisterLambda([&](gge::input::keyboard::Event event) -> bool
	{
		if(event.keycode==gge::input::keyboard::KeyCodes::Escape && !interface::cancelCount) {
			if(!mechanics::PlayerGame::isInGame()) return false;

			toptoplayer.IsVisible=true;
			ingameMenu.Show();
		}
		else if(event.event==event.Down) {
			if(event.keycode==gge::input::keyboard::KeyCodes::Tab && !interface::cancelCount) {
				interface::cycleSelection();
				infopanel.display(interface::selectedObject);

				return true;
			}
			//! cheat : )
#ifdef _DEBUG
			else if(event.keycode==gge::input::keyboard::KeyCodes::End) {
				mechanics::PlayerGame::current().IncreaseGold(500);
				return true;
			}
#endif
		}
		else if(event.event==event.Char) {
			if(event.keycode == 'b') {
				buttons.buildableChooser.Show();
			}
		}

		return false; 
	});

	gge::utils::Size screenSize = gge::Main.BoundingBox.GetSize();

	gge::utils::Point2D actualScroll(0,0);

	int zoomFactor = 32;

	unsigned last=gge::Main.CurrentTime;

	auto centerTown=[&] {
		gge::utils::Bounds townCenterPoint = mechanics::PlayerGame::current().townCenterLoc();
		gge::utils::Size screenSize = gge::Main.BoundingBox.GetSize();

		actualScroll.x = -screenSize.Width/2 +(townCenterPoint.Left + townCenterPoint.Right )/2 * TILE_SIZE;
		actualScroll.y = -screenSize.Height/2+(townCenterPoint.Top  + townCenterPoint.Bottom)/2 * TILE_SIZE;
	};

	mapselect.accepted.RegisterLambda([&](std::string name) {
		mechanics::PlayerGame::newGame();
		assets::loadMap("Maps/"+name);

		mapselect.Close();
		ingameMenu.Close();
		graphics::Map::getBaseLayer().IsVisible=true;
		titleLayer.IsVisible=false;
		titlelevel.Hide();
		gge::widgets::TopLevel.Show();
		input.BoundingBox.SetSize(graphics::Map::getBaseLayer().BoundingBox.GetSize());
		centerTown();
	});
	
	gge::Main.BeforeGameLoopEvent.RegisterLambda([&]{
		if(!mechanics::PlayerGame::isInGame()) return;

		if(gge::input::keyboard::Modifier::Current == gge::input::keyboard::Modifier::Ctrl) {
			if(gge::input::keyboard::PressedKeys[gge::input::keyboard::KeyCodes::Left]) {
				actualScroll.x-=SCROLL_AMOUNT_BIG*(gge::Main.CurrentTime-last)/1000.f;
			}

			if(gge::input::keyboard::PressedKeys[gge::input::keyboard::KeyCodes::Right]) {
				actualScroll.x+=SCROLL_AMOUNT_BIG*(gge::Main.CurrentTime-last)/1000.f;
			}

			if(gge::input::keyboard::PressedKeys[gge::input::keyboard::KeyCodes::Up]) {
				actualScroll.y-=SCROLL_AMOUNT_BIG*(gge::Main.CurrentTime-last)/1000.f;
			}

			if(gge::input::keyboard::PressedKeys[gge::input::keyboard::KeyCodes::Down]) {
				actualScroll.y+=SCROLL_AMOUNT_BIG*(gge::Main.CurrentTime-last)/1000.f;
			}
		}
		else{

			if(gge::input::keyboard::PressedKeys[gge::input::keyboard::KeyCodes::Left] || gge::input::mouse::CurrentPoint.x<SCROLL_RANGE) {
				actualScroll.x-=SCROLL_AMOUNT*(gge::Main.CurrentTime-last)/1000.f;
			}

			if(gge::input::keyboard::PressedKeys[gge::input::keyboard::KeyCodes::Right] || gge::input::mouse::CurrentPoint.x>gge::Main.BoundingBox.Right-SCROLL_RANGE) {
				actualScroll.x+=SCROLL_AMOUNT*(gge::Main.CurrentTime-last)/1000.f;
			}

			if(gge::input::keyboard::PressedKeys[gge::input::keyboard::KeyCodes::Up] || gge::input::mouse::CurrentPoint.y<SCROLL_RANGE) {
				actualScroll.y-=SCROLL_AMOUNT*(gge::Main.CurrentTime-last)/1000.f;
			}

			if(gge::input::keyboard::PressedKeys[gge::input::keyboard::KeyCodes::Down] || gge::input::mouse::CurrentPoint.y>gge::Main.BoundingBox.Bottom-SCROLL_RANGE) {
				actualScroll.y+=SCROLL_AMOUNT*(gge::Main.CurrentTime-last)/1000.f;
			}
		}

		if(gge::input::keyboard::PressedKeys[gge::input::keyboard::KeyCodes::PageUp]) {
			if(zoomFactor<TILE_SIZE)
				zoomFactor += 2;
		}

		if(gge::input::keyboard::PressedKeys[gge::input::keyboard::KeyCodes::PageDown]) {
			if(zoomFactor>TILE_SIZE/2)
				zoomFactor -= 2;
		}

		if(gge::input::keyboard::PressedKeys[gge::input::keyboard::KeyCodes::Delete]) {
			zoomFactor = TILE_SIZE;
		}

		if(gge::input::keyboard::PressedKeys[gge::input::keyboard::KeyCodes::Home]) {
			centerTown();
		}

		actualScroll.x=gge::utils::Limit(actualScroll.x, 0.f, (float)graphics::Map::getBaseLayer().BoundingBox.Width()-screenSize.Width);
		actualScroll.y=gge::utils::Limit(actualScroll.y, 0.f, (float)graphics::Map::getBaseLayer().BoundingBox.Height()-screenSize.Height);

		interface::update();
		infopanel.update();

		graphics::Map::current().zoom(zoomFactor);
		graphics::Map::current().scrollTo(actualScroll);
		graphics::Map::current().drawObjects();
		graphics::Map::current().drawTiles();
		graphics::Map::current().drawFog();

		last=gge::Main.CurrentTime;
	});

	gge::input::keyboard::Events.RegisterLambda([&](gge::input::keyboard::Event event) -> bool {
		if(event.keycode==gge::input::keyboard::KeyCodes::Escape && interface::isBuilding) {
			interface::cancelBuilding();

			return true;
		}

		return false;
	});



	input.MouseCallback.SetLambda([&](gge::input::mouse::Event::Type event, gge::utils::Point location, int amount) -> bool {
		using namespace gge::input::mouse;

		static gge::utils::Point lastpnt, selbegin;

		if(Event::isClick(event)) {
			location/=graphics::Map::current().zoom();

			mechanics::Object *obj=mechanics::Map::current().getPointInfoMap()(location).object;
			if(event==Event::Left_Click) {
				//if(dynamic_cast<mechanics::TownBuilding*>(obj)) {
				//	mechanics::TownBuilding *tb=dynamic_cast<mechanics::TownBuilding*>(obj);
				//	tb->destroy();
				//}
				
				if(interface::isBuilding) {
					interface::placeBuilding(location, gge::input::keyboard::Modifier::Current==gge::input::keyboard::Modifier::Shift);
				}
				else if(gge::input::keyboard::Modifier::Current==gge::input::keyboard::Modifier::Ctrl) {
					interface::addToSelection(obj);
					infopanel.display(interface::selectedObject);
				}
				else {
					interface::selectObject(obj);
					infopanel.display(obj);
				}
			}
			else if(event==Event::Right_Click) {
				interface::performAction(location);
			}
		}
		else if(event==Event::Left_Down) {
			location/=graphics::Map::current().zoom();
			lastpnt=location;
			selbegin=location;
		}
		else if(event==Event::Left_Up) {
			interface::hideSelectionArea();
		}
		if(event==Event::Move) {
			if(PressedButtons==Event::Left) {
				location/=graphics::Map::current().zoom();
				if(lastpnt!=location) {
					interface::setSelectionArea(gge::utils::Bounds(selbegin, location));
					infopanel.display(interface::selectedObject);

					lastpnt=location;
				}
			}
			else {
				location/=graphics::Map::current().zoom();
				if(interface::isBuilding) {
					interface::setBuildingFloater(location);
				}
			}
		}

		return true;
	});

	gge::widgets::WidgetLayer rightcancel;
	gge::Main.Add(rightcancel, -1);
	rightcancel.MouseCallback.SetLambda([&](gge::input::mouse::Event::Type event, gge::utils::Point location, int amount) -> bool {
		using namespace gge::input::mouse;

		if(event==Event::Right_Down && interface::cancelCount>0) {
			return true;
		}
		if(event==Event::Right_Click && interface::cancelCount>0) {
			gge::input::system::ProcessChar(gge::input::keyboard::KeyCodes::Escape);

			return true;
		}

		return false;
	});

	
	//mechanics::Item &item = *createItem("Dagger");

	//mechanics::PlayerGame::current().addItem(item);
	//centerTown();


	gge::os::window::Closing.RegisterLambda([&](bool &allow) {
		interface::askQuit();
		allow=false;
	});



	gge::Main.Run();
	return EXIT_SUCCESS;
}
