#pragma once
#include <BWAPI.h>
#include <iostream>
#include <sstream>
#include "UnitModel.h"
#include "MapInformationSystem.h"
#include "Army.h"
#include "Synapse.h"
#include "PathRepository.h"
#include <stack>
#include <map>
#include <set>

using namespace BWAPI;
using namespace std;
class BaseModel; //hack
class Overseer;
class ExpandTask;

class UnitManager {
public:
	UnitManager(Overseer* ov);
	void notifyCreate(Unit* newUnit);
	void notifyDestroy(Unit* deadUnit);
	void initialize();
	void updateModel();
	void drawBeliefs();
	bool hasBuildingComplete(UnitType t);
		bool hasBuildingUnderConstruction(UnitType t);
	bool hasUnit(UnitType t);
	bool hasUnit(UnitType t, int num);
	void pathMove(TilePosition t);
	void requestDefense(TilePosition t);
	void requestDefense(TilePosition t, int priority);
		void requestDefense(TilePosition t, int priority, bool guardChoke);
			void requestDefense( Unit* t, int priority );
						void requestDefense( std::vector<TilePosition> path, int priority );
	void manageExpansions();
	void alertVulnerableUnits(TilePosition t);
	Overseer* getOverseer();
	bool isRegionOwnedByMe(BWTA::Region* r);
	void setMaximumBuildConstraint(UnitType u, int num);
	int getMaximumBuildConstraint(UnitType u);
	vector<UnitModel*> getAllUnits();
	vector<UnitModel*> getEnemyBuildings();
	vector<UnitModel*> getFriendlyBuildings();
	int countFriendlyBuildings(UnitType t);
	int countProductiveBases();
		int countFriendlyUnits(UnitType t);
		int countFriendlyBuildingsInRange(UnitType t, Position p, int range);
				int countNumUnderConstructionAtBase(UnitType t, BaseModel* b);
				int countFriendlyUnitsOfType(UnitType t);
	vector<UnitModel*> getAllFriendlyUnitsOfType(UnitType t);
	set<UnitModel*> getAllFriendlyBuildingsOfType(UnitType t);
	set<UnitModel*> getAllFriendlyBuildingsOfTypeInRange(UnitType t, Position p, int range);
	vector<UnitModel*> getAllEnemyUnitsOfType(UnitType t);
	vector<BaseModel*> getFriendlyBaseModels();
	vector<BaseModel*> getEnemyBaseModels();
	BaseModel* getBaseModelInRegion(BWTA::Region* r);
		BaseModel* getEnemyBaseModelInRegion(BWTA::Region* r);
		BaseModel* getClosestFriendlyBaseModel(Position p);
				BWAPI::Position getClosestNeutralOrFriendlyBasePos(Position p);
					BWAPI::Position getClosestNeutralOrFriendlyBasePosNot(Position p, BWTA::Region* r);
			BaseModel* getClosestEnemyBaseModel(Position p);
			bool hasBeenRebuilt(Unit* u);
			void registerRebuilt(Unit* u);
	BaseModel* getFriendlyMainBase();
	BaseModel* getEnemyMainBase();
	Position getBelievedEnemyMainBasePosition();
	BWTA::Region* getBelievedEnemyMainBaseRegion();
	BWTA::Region* getBelievedEnemyMainBaseRegionNot(BWTA::Region* r);
	vector<UnitModel*> getFriendlyMilitaryUnits();
	vector<UnitModel*> getEnemyMilitaryUnits();
	vector<UnitModel*> getEnemyMilitaryUnitsInRange(Position p, int range);
	set<Unit*> getEnemyMilitaryUnitsASUNITSInRange(Position p, int range);
	vector<UnitModel*> getEnemyDetectorsInRange(Position p, int range);
	vector<UnitModel*> getFriendlyMilitaryUnitsInRange(Position p, int range);
	vector<UnitModel*> getFriendlyUnitsOfTypeInRange(Position p, int range, UnitType t);
	vector<UnitModel*> getAllUnitsInRange(Position p, int range);
	vector<UnitModel*> getAllEnemyUnitsInRange(Position p, int range);
	vector<UnitModel*> getAllFriendlyUnitsInRange(Position p, int range);
	vector<UnitModel*> getEnemyBuildingsInRegionOf(TilePosition p);
	vector<UnitModel*> getEnemyBuildingsInRange(Position p, int range);
	vector<UnitModel*> getEnemyBuildingsOfTypeInRange(Position p, int range, UnitType t);
	vector<UnitModel*> getAllEnemyUnitsInRegion(BWTA::Region* r);
	vector<UnitModel*> getAllEnemyBuildingsInRegion(BWTA::Region* r);

	vector<UnitModel*> getAllEnemyUnitsOfTypeInRegion(UnitType t, BWTA::Region* r);
	vector<UnitModel*> getAllEnemyUnitsNotInRegion(BWTA::Region* r);
	vector<UnitModel*> getNFreeUnitsOfType(UnitType r, int n);
	std::vector<BWAPI::TilePosition> AstarSearchPath(BWAPI::TilePosition start, BWAPI::TilePosition end, int granularity);
	std::vector<BWAPI::TilePosition> AstarSearchPathNoObstacles(BWAPI::TilePosition start, BWAPI::TilePosition end);
	std::vector<BWAPI::TilePosition> AstarSearchPathNoObstacles(BWAPI::TilePosition start, BWAPI::TilePosition end, int grain);

	std::vector<BWAPI::TilePosition> threatAwareAstarSearchPathGround(BWAPI::TilePosition start, BWAPI::TilePosition end);
	std::vector<BWAPI::TilePosition> threatAwareAstarSearchPathAir(BWAPI::TilePosition start, BWAPI::TilePosition end);
	std::vector<BWAPI::TilePosition> threatAwareAstarSearchPathAir(BWAPI::TilePosition start, BWAPI::TilePosition end, int granularity);
	std::vector<BWAPI::TilePosition> threatAwareAstarSearchPathGroundAvoidUnits(BWAPI::TilePosition start, BWAPI::TilePosition end);
	UnitModel* findWorkerGlobal();
	void marshallIdleMilitaryUnits();
	bool shouldContinue();
	UnitModel* getUnitModelFromMap(Unit* u);
	BaseModel* getBaseModelFromMap(Unit* u);
	MapInformationSystem* getMapInformationSystem();
	Army* grabArmy();
	Army* grabArmy(int n);

	void testFormArmy();
		void testMoveArmy();
	void manageSupply();
	map<UnitType,std::stack<int>> getPendingBehaviours();
	
	void queueBehaviour(UnitType u, int behaviourID);
	void dequeueBehaviour(UnitType u);
	bool knownBaseInRegion(Position r);
	bool hasBuildablePath(BWAPI::TilePosition start, BWAPI::TilePosition finish);
	Synapse* getSynapse();
	int calculateTargettingPenalty(Unit* target, UnitModel* attacker);
	void declareTargetToMap(UnitModel* attacker, Unit* target);
	int getNumFriendlyUnitsAttacking(UnitModel* t);
	int valueRegion(BWTA::Region* r);
	void flushMemory();
	int getGlobalExpansionTimer();
	int getGlobalExpansionThresh();
	void resetGlobalExpansionTimer();
	std::vector<Army*> getActiveArmies();
	void setPreferredDTStrat(int f);
	int getPreferredDTStrat();
	void registerSuspectedEnemyBaseLoc(Position p);
	PathRepository* getPathRepo();
	Position getSuspectedEnemyBaseLoc();
	void setUpRegionBlackList();
	bool isBlackListed(BWTA::Region* r);

private:
	vector<UnitModel*> unitModels;
	vector<BaseModel*> friendlyBaseModels;
	vector<BaseModel*> enemyBaseModels;
	vector<Army*> activeArmies;
	map<Unit*,UnitModel*> unitModelsMap;
	map<Unit*,BaseModel*> baseModelsMap;
	map<UnitType,std::stack<int>> pendingBehavioursStack;
	map<UnitModel*, std::set<UnitModel*>> globalTargettingMap;
	map<UnitType, int> maximumBuildMap;
	Overseer* overseer;
	MapInformationSystem* mis;
	//Army* testArmy;
	int pathfindingCallsThisFrame;
	int PATHFINDING_CALL_LIMIT;
	int GLOBAL_EXPANSION_TIMER;
	int GLOBAL_EXPANSION_THRESH;
	Synapse* synapseCore;
	int preferredDTStrat;
	std::set<Unit*> rebuildList;
	Position suspectLoc;
	PathRepository* pathRepo;
	std::set<BWTA::Region*> blacklist;
protected:
};