#include "Overseer.h"
#include "BaseModel.h"
#include "BuildOrderTemplate.h"
#include "ExpandTask.h"
//#include "Windows.h"
#include <iostream>
#include <fstream>
#include "MapData.h"
Overseer::Overseer()
{
	unitsOnly = false;
	srand (time(0));
	arbitrator = new Arbitrator();
	unitManager = new UnitManager(this);
	buildingPlacer = new BuildingPlacer();
	//buildingPlacer->setBuildDistance(1);
	//	im = new InformationManager(this);
	// first of each pair is minerals, second is gas
	effectiveResources.first = Broodwar->self()->minerals(); 
	effectiveResources.second = Broodwar->self()->gas(); 
	//upred = new UnitPredictionManager(this);
	reservedResources.first = 25;
	reservedResources.second = 25;
	//	asi = new ArmyStructureInferenceManager(this);
	initialized = false;
	om = new OffenseMonitor(this);
	dm = new DefenseMonitor(this);
	curTaskID = 0;
	scoreManager = new PastPerformanceManager();
	curStratID = -1;
}

//************************************
// Method:    update
// FullName:  Overseer::update
// Access:    public 
// Returns:   void
// Comments:  none
//************************************
void Overseer::update()
{
	if(!initialized) {
		return;
	}

	// keep the resources we don't have earmarked already up-to-date
	effectiveResources.first = Broodwar->self()->minerals()-reservedResources.first; 
	effectiveResources.second = Broodwar->self()->gas()-reservedResources.second; 

	if(!unitManager->shouldContinue()) {
		return;
	}
	unitManager->updateModel();
	//} 

	std::set<BWTA::Region*> bases = BWTA::getRegions();
	int id = 0;
	BWTA::Region* startp = BWTA::getStartLocation(Broodwar->self())->getRegion();
	/*
	for (std::set<BWTA::Region*>::iterator i = bases.begin(); i != bases.end(); ++i)
	{
		BWTA::Region* cur = *i;

		if(unitManager->isBlackListed(cur)) {
			Broodwar->drawEllipseMap(cur->getCenter().x(), cur->getCenter().y(), 32, 32, BWAPI::Colors::Red, true);
		} else {
			Broodwar->drawEllipseMap(cur->getCenter().x(), cur->getCenter().y(), 32, 32, BWAPI::Colors::Green, true);
		}
	}
	

	for (std::vector<std::vector<TilePosition>>::iterator i = testPaths.begin(); i != testPaths.end(); ++i)
	{
		std::vector<TilePosition> path = *i;
		for (std::vector<TilePosition>::iterator it = path.begin(); it != path.end(); ++it)
		{
			Position cur = (Position)(*it);
			Broodwar->drawBoxMap(cur.x(), cur.y(), cur.x()+20, cur.y()+20,BWAPI::Colors::Green, true);
		}
	}
*/
	if(unitsOnly) {
		return;
	}
	//if(Broodwar->getFrameCount()%4==0) {

	arbitrator->monitor();
	//}

	manageSupply();
	manageMinNumWorkers();

	//asi->calculateArmies();
	//asi->drawArmies();

	om->evaluate();

	dm->monitor();


	


}

//************************************
// Method:    getUnitManager
// FullName:  Overseer::getUnitManager
// Access:    public 
// Returns:   void UnitManager*
// Comments:  none
//************************************
UnitManager* Overseer::getUnitManager()
{
	return unitManager;
}

//************************************
// Method:    getArbitrator
// FullName:  Overseer::getArbitrator
// Access:    public 
// Returns:   Arbitrator*
// Comments:  none
//************************************
Arbitrator* Overseer::getArbitrator()
{
	return arbitrator;
}

//************************************
// Method:    getBuildingPlacer
// FullName:  Overseer::getBuildingPlacer
// Access:    public 
// Returns:   BuildingPlacer*
// Comments:  none
//************************************
BuildingPlacer* Overseer::getBuildingPlacer()
{
	return buildingPlacer;
}

//************************************
// Method:    canReserveResources
// FullName:  Overseer::canReserveResources
// Access:    public 
// Returns:   bool
// Parameter: int mineralAmount
// Parameter: int gasAmount
// Comments:  none
//************************************
bool Overseer::canReserveResources( int mineralAmount, int gasAmount )
{
	int usableMinerals = effectiveResources.first;
	int usableGas = effectiveResources.second;
	if(usableMinerals-mineralAmount >= 0) {
		// we can reserve this many minerals
		if(gasAmount != 0) {
			if(usableGas-gasAmount >= 0) {
				return true;
			}
		} else {
			return true;
		}
	}
	return false;
}

//************************************
// Method:    getAvailableResources
// FullName:  Overseer::getAvailableResources
// Access:    public 
// Returns:   std::pair<int,int>
// Comments:  none
//************************************
std::pair<int,int> Overseer::getAvailableResources()
{
	return effectiveResources;
}

//************************************
// Method:    reserveResources
// FullName:  Overseer::reserveResources
// Access:    public 
// Returns:   void
// Parameter: int mineralAmount
// Parameter: int gasAmount
// Comments:  Reserves a number of resources, such as to avoid situations where needed resources are consumed by the game when a 
//			  worker is moving to a construction location. This causes the worker to be unable to build.
//************************************
void Overseer::reserveResources( int mineralAmount, int gasAmount )
{
	reservedResources.first = reservedResources.first+mineralAmount;
	reservedResources.second = reservedResources.second+gasAmount;
}

//************************************
// Method:    relinquishResources
// FullName:  Overseer::relinquishResources
// Access:    public 
// Returns:   void
// Parameter: int mineralAmount
// Parameter: int gasAmount
// Comments:  Relinquishes the above resources. Called once the worker is in position and has begun construction of the building.
//************************************
void Overseer::relinquishResources( int mineralAmount, int gasAmount )
{
	// hmmm!! do we want consistency checks here?
	// shouldn't need them - this version not as complex as before
	reservedResources.first = reservedResources.first-mineralAmount;
	reservedResources.second = reservedResources.second-gasAmount;
}

//************************************
// Method:    initialize
// FullName:  Overseer::initialize
// Access:    public 
// Returns:   void
// Comments:  Sets the entire system moving by attaching build orders to the initial base. This basically defines our opener, 
//			  so we can construct buildings here, but also units. We should avoid building too many units though 
//			  since we should be defining our actual armies elsewhere. However, if we want, say, an initial dragoon or zealot
//			  to harass early on, this is probably the place to build it.
//
//************************************
void Overseer::initialize()
{
	if(initialized) {
		return;
	}
	// initialize our useful stuff
	//unitManager->initialize();
	//////Broodwar->sendText("overseer initialised");
	unitManager->getMapInformationSystem()->initialize();

	// initialize some build order
	compileStrategy();
	just_initialized = true;
	initialized = true;

}

void Overseer::testGrabArmy()
{
	unitManager->grabArmy();
}

void Overseer::manageSupply()
{
	if(Broodwar->getFrameCount()%4 == 0) {
		UnitType supplyProvider = Broodwar->self()->getRace().getSupplyProvider();
		if(arbitrator->isStagedToBeBuilt(supplyProvider) || Broodwar->self()->supplyTotal() >= 400 || unitManager->countNumUnderConstructionAtBase(UnitTypes::Protoss_Pylon, unitManager->getFriendlyMainBase()) != 0) {
			return;
		}

		int supplyProvided = supplyProvider.supplyProvided();
		int curSupply = Broodwar->self()->supplyUsed();
		int maxSupply = Broodwar->self()->supplyTotal();
		int futureSupply = arbitrator->countProjectedUnitsOfType(supplyProvider, 30);
		curSupply = curSupply+(supplyProvided*futureSupply);
		int numFacilities = unitManager->countFriendlyBuildings(UnitTypes::Protoss_Gateway);
		if(curSupply >= (maxSupply*0.85)) {

			// ok now lets look for somewhere to build
			// for now, lets try and even out our pylons over all our bases, to save putting all of our
			// eggs in one basket?
			std::vector<BaseModel*> bases = unitManager->getFriendlyBaseModels();
			BaseModel* bestSoFar = unitManager->getFriendlyMainBase();
			int bestNumSoFar = 10000;
			if(bestSoFar->countNumTypeInBase(UnitTypes::Protoss_Pylon) >= 6) {
				if(rand()%100 >= 70) {
					for (std::vector<BaseModel*>::iterator i = bases.begin(); i != bases.end(); ++i)
					{
						BaseModel* cur = *i;
						if(cur->isAlive()) {
							if(!cur->isUnderThreat() && !cur->getWorkers().empty()) {
								int pcount = cur->countNumTypeInBase(UnitTypes::Protoss_Pylon);
								if(pcount < bestNumSoFar) {
									bestSoFar = cur;
									bestNumSoFar = pcount;
								}
							}
						}
					}
				}
			}


			ConstructionTask* buildSupplyTask = new ConstructionTask(bestSoFar,supplyProvider,this);
			arbitrator->stageNewTask(buildSupplyTask);
			registerTask(buildSupplyTask);

			//bestSoFar->getBuildOrder()->addToOpeningSequence(supplyProvider);

		}
	}
}

void Overseer::testGrabScout()
{
	UnitModel* w = unitManager->getFriendlyMainBase()->findAvailableWorker();
	GroundScoutBehaviour* gs = new GroundScoutBehaviour(w, unitManager);
	w->setMicroBehaviour(gs);
}

void Overseer::testUnitPrediction()
{
	upred->calculate();
}

void Overseer::testArmyStructureInference()
{
	asi->calculateArmies();
}

void Overseer::testArmyMovement()
{
	unitManager->testMoveArmy();
}

ArmyStructureInferenceManager* Overseer::getArmyStructureManager()
{
	return asi;
}

int Overseer::getNewTaskID()
{
	curTaskID++;
	return curTaskID;
}

void Overseer::registerTask( TaskBase* t )
{
	int id = getNewTaskID();
	t->setTaskID(id);

	taskMap.insert(pair<int, TaskBase*>(id, t));
}

TaskBase* Overseer::getTaskFromMap(int id)
{
	if(taskMap.empty()) {
		return NULL;
	}
	if(taskMap.find(id) == taskMap.end()) {
		return NULL;
	} else {
		return taskMap[id];
	}
}

void Overseer::testPathing()
{
	std::set<BWTA::BaseLocation*> bases = BWTA::getBaseLocations();
	for(std::set<BWTA::BaseLocation*>::const_iterator i = bases.begin(); i != bases.end(); ++i) {
		BWTA::BaseLocation* b = *i;
		std::vector<TilePosition> path = unitManager->AstarSearchPathNoObstacles(unitManager->getFriendlyMainBase()->getSubject()->getTilePosition(), b->getTilePosition(),1);
		testPaths.push_back(path);
		//////Broodwar->sendText("found path of length %d", path.size());
	}

}

void Overseer::flushMemoryBanks()
{
	unitManager->flushMemory();
	delete unitManager;
	delete om;
	delete asi;
	delete upred;
	delete dm;
	delete buildingPlacer;
	delete arbitrator;
}

OffenseMonitor* Overseer::getOffenseMonitor()
{
	return om;
}

void Overseer::testTargetting()
{
	////Broodwar->sendText("testing targetting");
	Army* n = new Army(this);
	BWTA::Region* r = unitManager->getBelievedEnemyMainBaseRegion();
	om->registerTarget(n,r);

}

int Overseer::getTopTaskID()
{
	return curTaskID;
}

PastPerformanceManager* Overseer::getScoreManager()
{
	return scoreManager;
}

int Overseer::getCurrentHighLevelStrat()
{
	return curStratID;
}

void Overseer::compileStrategy()
{
	if(!unitsOnly) {

		int RECCOMENDED_STRAT = 667;
			RECCOMENDED_STRAT = scoreManager->getMostSuccessfulStrat(Broodwar->enemy()->getName());
		


		StrategyStructureDefinition* bo = NULL;

		switch(RECCOMENDED_STRAT) {
		case HIGHLEVEL_STRAT_PVP_ALPHA:
			{
				//OutputDebugString(TEXT("instantiating pvp alpha \n"));
				bo = new PvPAlpha(this);
				break;
			}
		case HIGHLEVEL_STRAT_PVP_BETA:
			{
				//OutputDebugString(TEXT("instantiating pvp beta \n"));
				bo = new PvPBeta(this);
				break;
			}
		case HIGHLEVEL_STRAT_PVP_GAMMA:
			{
				//OutputDebugString(TEXT("instantiating pvp gamma \n"));
				bo = new PvPGamma(this);
				break;
			}
		case HIGHLEVEL_STRAT_PVP_DELTA:
			{
				//OutputDebugString(TEXT("instantiating pvp DELTA \n"));
				bo = new PvPDelta(this);
				break;
			}
		case HIGHLEVEL_STRAT_PVT_ALPHA:
			{
				//OutputDebugString(TEXT("instantiating pvt alpha \n"));
				bo = new PvTAlpha(this);
				break;
			}
		case HIGHLEVEL_STRAT_PVT_BETA:
			{
				//OutputDebugString(TEXT("instantiating pvt BETA \n"));
				bo = new PvTBeta(this);
				break;
			}
		case HIGHLEVEL_STRAT_PVT_GAMMA:
			{
				//OutputDebugString(TEXT("instantiating pvt GAMMA \n"));
				bo = new PvTGamma(this);
				break;
			}
		case HIGHLEVEL_STRAT_PVZ_ALPHA:
			{
				//OutputDebugString(TEXT("instantiating pvz alpha \n"));
				bo = new PvZAlpha(this);
				break;
			}
		case HIGHLEVEL_STRAT_PVZ_BETA:
			{
				//OutputDebugString(TEXT("instantiating pvz beta \n"));
				bo = new PvZBeta(this);
				break;
			}
		case HIGHLEVEL_STRAT_PVZ_GAMMA:
			{
				//OutputDebugString(TEXT("instantiating pvz GAMMA \n"));
				bo = new PvZGamma(this);
				break;
			}
		}
		if(bo != NULL) { // lets be VERY paranoid

		} else {
			bo = new PvPAlpha(this);
		}
		curStratID = bo->getID();
		bo->setUpBranches();
		bo->loadOpener();

	}
}

bool Overseer::isInitialized()
{
	return initialized;
}

bool Overseer::isTaskRegisteredFor( UnitType t )
{
	for(std::map<int,TaskBase*>::const_iterator i = taskMap.begin(); i != taskMap.end(); i++) {
		std::pair<int, TaskBase*> cur = *i;
		if(cur.second->isFinished()) {
			continue;
		}
		if(cur.second->getBuildType() == t) {
			return true;
		}
	}
	return false;
}

void Overseer::manageMinNumWorkers()
{
	// this is a failsafe in case we lose all of our workers!
	int pc = unitManager->countFriendlyUnitsOfType(UnitTypes::Protoss_Probe);

	if(pc < 4) {
		TrainingTask* t = new TrainingTask(unitManager->getFriendlyMainBase(), UnitTypes::Protoss_Probe, this);
		registerTask(t);
		if(t->canInitialize()) {
			arbitrator->declareNewTask(t);

	}



}
}