#pragma once

#include "../Graphics/Map.h"
#include "../Mechanics/PlayerGame.h"
#include "../Mechanics/Map.h"
#include "../Instantiation.h"
#include "../Graphics/Animation.h"
#include "General.h"
#include <Widgets/Dialogs/Query.h>
#include <Engine/GGEMain.h>


//for vs
#undef interface

namespace interface {

	class EventDisplay;

	extern bool isBuilding;
	extern std::string buildingID;
	extern gge::utils::Point buildingOffset;
	extern int cancelCount;
	extern EventDisplay *eventDisplay;
	extern gge::utils::Bounds selectionArea;

	extern std::function<void()> showIngameMapSelection;
	extern std::function<void()> showTitleMapSelection;
	extern std::function<void()> closeMapSelection;

	extern std::function<void()> showIngameCredits;
	extern std::function<void()> showTitleCredits;
	extern std::function<void()> closeCredits;

	void initialize();

	inline void askBuildingPlace(std::string ID) {
		if(isBuilding) cancelCount--;
		isBuilding=true;
		buildingID=ID;
		graphics::Map::current().showBuildable=true;

		graphics::Map::current().setBuildingFloater(new graphics::Animation(*assets::MilitaryBuilding::list[buildingID]->animation));

		buildingOffset=assets::MilitaryBuilding::list[buildingID]->size/2;
		cancelCount++;
	}

	inline void askQuit() {
		if(!mechanics::PlayerGame::isInGame()) {
			gge::Main.Exit(0);
		}

		gge::widgets::dialog::AskConfirm("There is no save functionality, do still wish to exit?", "Quit").RepliedEvent.RegisterLambda([&](bool reply) {
			if(reply) gge::Main.Exit(0);
		});
	}

	inline void placeBuilding(gge::utils::Point point, bool again) {
		point-=buildingOffset;
		gge::utils::Point pointFinal=point+assets::MilitaryBuilding::list[buildingID]->size;

		for(int y = point.y; y < pointFinal.y; y++) {
			for(int x = point.x; x < pointFinal.x; x++) {
				mechanics::PointInfo pi = mechanics::Map::current().getPointInfoMap()(x, y);
				if(!pi.walkable || !pi.buildable || pi.object != NULL) {
					mechanics::PlayerGame::current().AddToGameLog("cannot build to target");
					return;
				}
			}
		}
		for(auto it=assets::MilitaryBuilding::list[buildingID]->usablePoints.begin();it!=assets::MilitaryBuilding::list[buildingID]->usablePoints.end();++it) {
			mechanics::PointInfo pi = mechanics::Map::current().getPointInfoMap()(point+*it);
			if(!pi.walkable || !pi.buildable || pi.object != NULL) {
				mechanics::PlayerGame::current().AddToGameLog("cannot build to target");
				return;
			}
		}


		auto mil=mechanics::PlayerGame::current().createMilitaryBuilding(buildingID);
		if(!mil) return;

		mechanics::PlayerGame::current().addMilitaryBuilding(*mil);
		mil->locateTo(point);

		isBuilding=false;
		graphics::Map::current().showBuildable=false;
		graphics::Map::current().setBuildingFloater(NULL);

		if(again) {
			askBuildingPlace(buildingID);
		}
		else {
			buildingID="";
		}

		cancelCount--;
	}

	inline void setBuildingFloater(gge::utils::Point point) {
		graphics::Map::current().buildingFloaterPos=point-buildingOffset;

		point-=buildingOffset;
		gge::utils::Point pointFinal=point+assets::MilitaryBuilding::list[buildingID]->size;
		

		graphics::Map::current().showAsNonBuildable=false;
		for(int y = point.y; y < pointFinal.y; y++) {
			for(int x = point.x; x < pointFinal.x; x++) {
				mechanics::PointInfo pi = mechanics::Map::current().getPointInfoMap()(x, y);
				if(!pi.walkable || !pi.buildable || pi.object != NULL) {
					graphics::Map::current().showAsNonBuildable=true;
					return;
				}
			}
		}
		for(auto it=assets::MilitaryBuilding::list[buildingID]->usablePoints.begin();it!=assets::MilitaryBuilding::list[buildingID]->usablePoints.end();++it) {
			mechanics::PointInfo pi = mechanics::Map::current().getPointInfoMap()(point+*it);
			if(!pi.walkable || !pi.buildable || pi.object != NULL) {
				graphics::Map::current().showAsNonBuildable=true;
				return;
			}
		}
	}

	bool showEvent(mechanics::MapEvent &event);

	inline void update() {
		if(!mechanics::PlayerGame::isInGame()) return;

		graphics::Map::current().selectionBoundList.clear();
		graphics::Map::current().selectionOffsets.clear();


		if(selectedObject) {
			if(dynamic_cast<mechanics::Building*>(selectedObject)) {
				graphics::Map::current().selectionBoundList.push_back(gge::utils::Bounds(selectedObject->currentLocation(), dynamic_cast<mechanics::Building*>(selectedObject)->size()));
			}
			else if(dynamic_cast<mechanics::Obstacle*>(selectedObject)) {
				graphics::Map::current().selectionBoundList.push_back(gge::utils::Bounds(selectedObject->currentLocation(), dynamic_cast<mechanics::Obstacle*>(selectedObject)->provider().size));
			}
			else {
				graphics::Map::current().selectionBoundList.push_back(gge::utils::Bounds(selectedObject->currentLocation(), gge::utils::Size(1,1)));
			}

			if(dynamic_cast<mechanics::Mover*>(selectedObject)) {
				graphics::Map::current().selectionOffsets.push_back(dynamic_cast<mechanics::Mover*>(selectedObject)->moverOffset);
			}
			else {
				graphics::Map::current().selectionOffsets.push_back(gge::utils::Point(0,0));
			}
		}

		for(auto it=selectedObjects.First();it.IsValid();it.Next()) {
			if(dynamic_cast<mechanics::Building*>(it.CurrentPtr())) {
				graphics::Map::current().selectionBoundList.push_back(gge::utils::Bounds(it->currentLocation(), dynamic_cast<mechanics::Building*>(it.CurrentPtr())->size()));
			}
			else if(dynamic_cast<mechanics::Obstacle*>(it.CurrentPtr())) {
				graphics::Map::current().selectionBoundList.push_back(gge::utils::Bounds(it->currentLocation(), dynamic_cast<mechanics::Obstacle*>(it.CurrentPtr())->provider().size));
			}
			else {
				graphics::Map::current().selectionBoundList.push_back(gge::utils::Bounds(it->currentLocation(), gge::utils::Size(1,1)));
			}

			if(dynamic_cast<mechanics::Mover*>(it.CurrentPtr())) {
				graphics::Map::current().selectionOffsets.push_back(dynamic_cast<mechanics::Mover*>(it.CurrentPtr())->moverOffset);
			}
			else {
				graphics::Map::current().selectionOffsets.push_back(gge::utils::Point(0,0));
			}
		}

		if(selectionArea!=gge::utils::Bounds(-1,-1,-1,-1)) {
			graphics::Map::current().selectionBoundList.push_back(selectionArea);
			graphics::Map::current().selectionOffsets.push_back(gge::utils::Point(0,0));
		}

		for(auto it=mechanics::Map::current().timerEvents.begin();it!=mechanics::Map::current().timerEvents.end();++it) {
			if(!it->isTriggered && currentGameTime>it->target && showEvent(*it)) {
				it->isTriggered=true;
			}
		}
	}

	inline void selectObject(mechanics::Object *obj) {
		selectedObject=obj;
		selectedObjects.Clear();
		selectedObjects.Add(obj);

		update();
	}

	inline void addToSelection(mechanics::Object *obj) {
		if(selectedObject==NULL) {
			selectedObject=obj;
		}
		selectedObjects.Add(obj);

		update();
	}

	inline void cycleSelection() {
		if(!selectedObjects.GetCount()) return;

		selectedObject=selectedObjects((selectedObjects.FindLocation(selectedObject)+1)%selectedObjects.GetCount());

		update();
	}

	inline void setSelectionArea(gge::utils::Bounds area) {
		auto &m=mechanics::Map::current().getPointInfoMap();

		selectionArea=area;
		selectionArea.Left=gge::utils::Limit(selectionArea.Left, 0, m.GetWidth());
		selectionArea.Right=gge::utils::Limit(selectionArea.Right, 0, m.GetWidth());
		selectionArea.Top=gge::utils::Limit(selectionArea.Top, 0, m.GetHeight());
		selectionArea.Bottom=gge::utils::Limit(selectionArea.Bottom, 0, m.GetHeight());

		selectedObjects.Clear();
		selectedObject=NULL;

		for(int x=selectionArea.Left;x<=selectionArea.Right;x++) {
			for(int y=selectionArea.Top;y<=selectionArea.Bottom;y++) {
				if(dynamic_cast<mechanics::Soldier*>(m.Get(x,y).object)) { // select only soldiers
					if(selectedObjects.FindLocation(m.Get(x,y).object)==-1) {
						addToSelection(m.Get(x,y).object);
					}
				}				
			}
		}

		update();
	}

	inline void hideSelectionArea() {
		selectionArea=gge::utils::Bounds(-1,-1,-1,-1);

		update();
	}

	inline void performAction(gge::utils::Point location) {
		if(dynamic_cast<mechanics::Soldier*>(selectedObject)) {
			dynamic_cast<mechanics::Soldier*>(selectedObject)->doAction(location);
		}

		for(auto it=selectedObjects.First();it.IsValid();it.Next()) {
			if(dynamic_cast<mechanics::Soldier*>(it.CurrentPtr()) && it.CurrentPtr()!=selectedObject) {
				dynamic_cast<mechanics::Soldier&>(*it).doAction(location);
			}
		}
	}
	
	inline void cancelBuilding() {
		isBuilding=false;
		buildingID="";
		graphics::Map::current().showBuildable=false;
		graphics::Map::current().setBuildingFloater(NULL);
		cancelCount--;
	}

}
