#include "BuildOrderTemplate.h"
#include "ExpandTask.h"
#include "BaseModelConsistencyMonitor.h"
#include "TaskBase.h"
//#include "Windows.h"
#include "MapData.h"

BuildOrderTemplate::BuildOrderTemplate( Overseer* ov )
{
	here = NULL;
	overseer = ov;
	targetGasWorkers = 0;
	targetMineralWorkers = 0;
	bcm = new BaseModelConsistencyMonitor(ov);
	managingWorkerAcquisition = true;
}
//************************************
// Method:    update
// FullName:  BaseConfigurationTemplate::update
// Access:    public 
// Returns:   void
// Comments:  Basic idea: We basically need to make sure that the contents of the base are consistent with those specified in the build order. If not, instantiate tasks 
//			  to bring the base up to scratch.
//************************************
void BuildOrderTemplate::update()
{
	if(here == NULL || !here->isAlive()) {
		return;
	}
	/*
	First we want to look through our build order and see if we can do the next thing yet.
	If so, we want to go ahead and do it!
	*/

	if(!primeOrder.empty()) {
		//for (deque<TaskBase*>::iterator i = primeOrder.begin(); i != primeOrder.end(); ++i)
		//	{
		TaskBase* topTask = primeOrder.back();
		topTask->setTargetBase(here);
		if(topTask->isInitialized()) {
			if(topTask->getTaskType() == TRAINING) {
				if(overseer->getArbitrator()->stageNewTask(topTask)) {
					primeOrder.pop_back();
				}
			} else {
				if(overseer->getArbitrator()->declareNewTask(topTask)) {
					primeOrder.pop_back();
				}
			}
		//	return;
		} else {
		if(topTask->canInitialize()) {
			if(topTask->getTaskType() == TRAINING) {
				if(overseer->getArbitrator()->stageNewTask(topTask)) {
					primeOrder.pop_back();
				}
			} else {
				if(overseer->getArbitrator()->declareNewTask(topTask)) {
					primeOrder.pop_back();
				}
			}
			//return;
		}
		}

		// ah but wait, there's more crazy stuff we need to do
		// we need to flush the deck of training tasks, so they don't gum up the works
		// and we get our units as quickly as possible.
		for (deque<TaskBase*>::iterator i = primeOrder.begin(); i != primeOrder.end(); ++i)
		{
			TaskBase* cur = *i;
			bool shouldStage = false;
			//|| (topTask->getTaskType() == CONSTRUCTION && (topTask->getBuildType() == UnitTypes::Protoss_Photon_Cannon || topTask->getBuildType() == UnitTypes::Protoss_Pylon)
			// filter non-training tasks
			if(cur->getTaskType() == TRAINING)  {
			} else {
				continue;
			}
			
			cur->setTargetBase(here); // note: hack. this will break stuff later. note to self: it now breaks stuff ;)
			if(cur->isInitialized()) {
				shouldStage = true;
				
			} else {
				if(cur->canInitialize()) {
					shouldStage = true;
				}
			}
			if(!shouldStage) {
				continue;
			}
			
			if(overseer->getArbitrator()->stageNewTask(cur)) {
				i = primeOrder.erase(i);
				if(i == primeOrder.end()) {
					break;
				}
				break; // is this a good idea
			}

		}
	}


	if(!secondaryOrder.empty()) {
		std::set<UnitType> queuedResearch;
		for (deque<std::pair<int, TaskBase*>>::iterator i = secondaryOrder.begin(); i != secondaryOrder.end(); ++i)
		{
			std::pair<int, TaskBase*> cur = *i;
				int preReqTaskID = cur.first;
				TaskBase* task = cur.second;

				// right, so  we care about whether this prereq task has been completed
				TaskBase* preReqTask = overseer->getTaskFromMap(preReqTaskID);
				
				// not sure why this would ever happen? but eeh
				if(preReqTask == NULL) {
					continue;
				}
		
				// so if this is finished, we can push up this new task
				if(preReqTask->isFinished() || (preReqTask->isExecuting() && preReqTask->getTaskType() != EXPAND)) {
					task->setTargetBase(here); // note: hack. this will break stuff later. note to self: it now breaks stuff ;)
					bool shouldStage = false;
					if(task->isInitialized()) {
						shouldStage = true;

					} else {
						if(task->canInitialize()) {
							shouldStage = true;
						}
					}
					if(!shouldStage) {
						continue;
					}

					if(overseer->getArbitrator()->stageNewTask(task)) {
						i = secondaryOrder.erase(i);
						if(i == secondaryOrder.end()) {
							break;
						}
					}
				}

		}

	}
	if(Broodwar->getFrameCount()%24==0) {
		manageWorkerRequisitions();
	}


}

//************************************
// Method:    addToprimeOrder
// FullName:  BaseConfigurationTemplate::addToprimeOrder
// Access:    public 
// Returns:   void
// Parameter: UnitType building
// Comments:  none
//************************************
void BuildOrderTemplate::addToOpeningSequence( UnitType unit )
{
	if(here != NULL) {
	if(!here->isAlive()) {
		return;
	}
	}
	if(unit.isBuilding()) {

		ConstructionTask* c = new ConstructionTask(here, unit, overseer);
		if(!primeOrder.empty()) {	
			c->setPreReq(primeOrder.front());
		}
		overseer->registerTask(c);
		primeOrder.push_front(c);


	} else {
		TrainingTask* t = new TrainingTask(here, unit, overseer);
		overseer->registerTask(t);
		primeOrder.push_front(t);
	}
}

void BuildOrderTemplate::addToOpeningSequenceWithRally( UnitType unit, int rallyLoc )
{
	if(here != NULL) {
		if(!here->isAlive()) {
			return;
		}
	}
	if(unit.isBuilding()) {
		ConstructionTask* c = new ConstructionTask(here, unit, overseer);
		overseer->registerTask(c);
		primeOrder.push_front(c);
	} else {
		TrainingTask* t = new TrainingTask(here, unit, overseer);
		t->setRallyLocation(rallyLoc);
		overseer->registerTask(t);
		primeOrder.push_front(t);
	}
}

void BuildOrderTemplate::addToOpeningSequenceWithRally( UnitType unit, Position rallyLoc )
{
	if(here != NULL) {
		if(!here->isAlive()) {
			return;
		}
	}
	if(unit.isBuilding()) {
		ConstructionTask* c = new ConstructionTask(here, unit, overseer);
		overseer->registerTask(c);
		primeOrder.push_front(c);
	} else {
		TrainingTask* t = new TrainingTask(here, unit, overseer);
		t->setRallyLocation(rallyLoc);
		overseer->registerTask(t);
		primeOrder.push_front(t);
	}
}


void BuildOrderTemplate::addToOpeningSequence( TaskBase* task )
{
if(!primeOrder.empty()) {	
	task->setPreReq(primeOrder.front());
}
	overseer->registerTask(task);
	primeOrder.push_front(task);
}

//************************************
// Method:    addToOpeningSequence
// FullName:  primeOrderTemplate::addToOpeningSequence
// Access:    public 
// Returns:   void
// Parameter: UnitType unit
// Parameter: int behaviourModelID
// Comments:  none
//************************************
void BuildOrderTemplate::addToOpeningSequence( UnitType unit, int behaviourModelID )
{
	if(!unit.isBuilding()) {
		TrainingTask* t = new TrainingTask(here, unit, overseer);
		overseer->registerTask(t);
		primeOrder.push_front(t);
		overseer->getUnitManager()->queueBehaviour(unit, behaviourModelID);
	}
}

void BuildOrderTemplate::addToOpeningSequence( UnitType unit, BuildOrderTemplate* temp )
{
	if(unit == Broodwar->self()->getRace().getCenter()) {
		////////Broodwar->sendText("expanding");
		
		if(temp == NULL) {
		BuildOrderTemplate* rootprimeOrder = new BuildOrderTemplate(overseer);
		rootprimeOrder->setTargetMineralWorkers(16);
		rootprimeOrder->setTargetGasWorkers(3);
		//rootprimeOrder->addToOpeningSequence(UnitTypes::Protoss_Assimilator);
		ExpandTask* e = new ExpandTask(overseer, rootprimeOrder);
		here->setExpandSubject(e);
		overseer->registerTask(e);
		primeOrder.push_front(e);
		////////Broodwar->sendText("attempting to expand");
		} else {
			ExpandTask* e = new ExpandTask(overseer, temp);
			here->setExpandSubject(e);
			overseer->registerTask(e);
			primeOrder.push_front(e);
		//	//////Broodwar->sendText("attempting to expand to %d %d", nearestBase->getTilePosition().x(), nearestBase->getTilePosition().y());
		}
	}
}

void BuildOrderTemplate::addToOpeningSequence( TechType t )
{
	ResearchTask* r = new ResearchTask(t, overseer);
	overseer->registerTask(r);
	if(primeOrder.empty()) {
		primeOrder.push_front(r);
	} else {
		TaskBase* topTask;
		if(secondaryOrder.empty()) {
			topTask = (primeOrder.front());
		} else {
			topTask = (secondaryOrder.front().second);
		}
		std::pair<int, TaskBase*> tbp;
		tbp.first = topTask->getTaskID();
		tbp.second = r;
		secondaryOrder.push_front(tbp);
	}
}

void BuildOrderTemplate::addToOpeningSequence( UpgradeType t )
{
	ResearchTask* r = new ResearchTask(t, overseer);
	overseer->registerTask(r);
	if(primeOrder.empty()) {
		primeOrder.push_front(r);
	} else {
		TaskBase* topTask;
		if(secondaryOrder.empty()) {
			topTask = (primeOrder.front());
		} else {
			topTask = (secondaryOrder.front().second);
		}
		std::pair<int, TaskBase*> tbp;
		tbp.first = topTask->getTaskID();
		tbp.second = r;
		secondaryOrder.push_front(tbp);
	}
}

//************************************
// Method:    setTargetMineralWorkers
// FullName:  primeOrderTemplate::setTargetMineralWorkers
// Access:    public 
// Returns:   void
// Comments:  none
//************************************
void BuildOrderTemplate::setTargetMineralWorkers(int tar)
{
	targetMineralWorkers = tar;
}

//************************************
// Method:    setTargetGasWorkers
// FullName:  primeOrderTemplate::setTargetGasWorkers
// Access:    public 
// Returns:   void
// Comments:  none
//************************************
void BuildOrderTemplate::setTargetGasWorkers(int tar)
{
	targetGasWorkers = tar;
}

//************************************
// Method:    getTargetMineralWorkers
// FullName:  primeOrderTemplate::getTargetMineralWorkers
// Access:    public 
// Returns:   void
// Parameter: int tar
// Comments:  none
//************************************
int BuildOrderTemplate::getTargetMineralWorkers()
{
	return targetMineralWorkers;
}

//************************************
// Method:    getTargetGasWorkers
// FullName:  primeOrderTemplate::getTargetGasWorkers
// Access:    public 
// Returns:   void
// Parameter: int tar
// Comments:  none
//************************************
int BuildOrderTemplate::getTargetGasWorkers()
{
	return targetGasWorkers;
}

//************************************
// Method:    setBaseModel
// FullName:  primeOrderTemplate::setBaseModel
// Access:    public 
// Returns:   void
// Parameter: BaseModel * b
// Comments:  none
//************************************
void BuildOrderTemplate::setBaseModel( BaseModel* b )
{
	here = b;
	if(targetParent != NULL) {
		here->setParent(targetParent);
	}
	bcm->attachBaseModel(here);
}

void BuildOrderTemplate::addToOpeningSequenceWithGuardStrength( UnitType unit, int strength, BuildOrderTemplate* temp )
{
	if(unit == Broodwar->self()->getRace().getCenter()) {
		////////Broodwar->sendText("expanding");

		if(temp == NULL) {
			BuildOrderTemplate* rootprimeOrder = new BuildOrderTemplate(overseer);
			rootprimeOrder->setTargetMineralWorkers(18);
			rootprimeOrder->setTargetGasWorkers(3);
			//rootprimeOrder->addToOpeningSequence(UnitTypes::Protoss_Assimilator);
			ExpandTask* e = new ExpandTask(overseer, rootprimeOrder);
			targetParent->setExpandSubject(e);
			overseer->registerTask(e);
			primeOrder.push_front(e);
			////////Broodwar->sendText("attempting to expand");
		} else {
			ExpandTask* e = new ExpandTask(overseer, temp);
			targetParent->setExpandSubject(e);
			e->setGuardStrength(strength);
			overseer->registerTask(e);
			primeOrder.push_front(e);
			//	//////Broodwar->sendText("attempting to expand to %d %d", nearestBase->getTilePosition().x(), nearestBase->getTilePosition().y());
		}
	}
}

void BuildOrderTemplate::pushGeneralConstructionTask( UnitType unit )
{
	if(here != NULL) {
		if(!here->isAlive()) {
			return;
		}
	}
	if(unit.isBuilding()) {
		ConstructionTask* c = new ConstructionTask(here, unit, overseer);
		overseer->registerTask(c);
		primeOrder.push_front(c);
	} else {
		TrainingTask* t = new TrainingTask(here, unit, overseer);
		overseer->registerTask(t);
		primeOrder.push_front(t);
	}
}

BaseModelConsistencyMonitor* BuildOrderTemplate::getBCM()
{
	return bcm;
}

void BuildOrderTemplate::manageWorkerRequisitions()
{

	/*
	Next up, we need to make sure our worker cohort is consistent with what was requested
	*/
	if(here->mineralsExhausted()) {
		return;
	}
	if(!managingWorkerAcquisition) {
		return;
	}
	if(here->getSubject()->getType() == UnitTypes::Protoss_Nexus) {
		if(here->getSubject()->getUnit()->isBeingConstructed()) {
			return;
		}
	}

	// firstly, lets get a count of which workers are d oing what
	int workersMiningMinerals = 0;
	int workersGatheringGas = 0;
	int idleWorkers = 0;
	std::vector<UnitModel*> workers = here->getWorkers();

	for (vector<UnitModel*>::iterator i = workers.begin(); i != workers.end(); ++i)
	{
		UnitModel* curWorker = *i;
		if(!curWorker->isAlive()) {
			continue;
		}
		if(!curWorker->getUnit()->isCompleted() || curWorker->getUnit()->isConstructing()) {
			continue;
		}
		if(curWorker->getUnit()->isCarryingMinerals() || curWorker->getUnit()->isGatheringMinerals()) {
			workersMiningMinerals++;
			continue;
		}
		if(curWorker->getUnit()->isGatheringGas() || curWorker->getUnit()->isCarryingGas()) {
			workersGatheringGas++;
			continue;
		}
		idleWorkers++;
	}

	// then if this count isn't consistent with what we want... we need to find a worker...
	// but don't TRAIN a worker if this would violate GLOBAL_WORKER_CAP
	bool foundExistingWorker = false;
	int MAX_TAKE_FROM_BASE = 1;
	if( workers.empty() || (workersMiningMinerals < targetMineralWorkers)) {
		
		// and if our centre is not currently busy
		if(!here->getSubject()->getUnit()->isTraining() && (!here->mineralsExhausted() || !here->gasExhausted()) && here->getSubject()->isAlive() && !here->getSubject()->getUnit()->isBeingConstructed()) {

			std::vector<BaseModel*> bases = overseer->getUnitManager()->getFriendlyBaseModels();
			
			// before we ask to make a new worker, see if there are any existing workers at mined-out bases we can make use of!
			for(std::vector<BaseModel*>::const_iterator j = bases.begin(); j != bases.end(); ++j) {
				BaseModel* curb = *j;
				if(curb->getWorkers().size() <= 3 || curb == here || curb->getSubject()->getUnit()->isBeingConstructed()) {
					continue;
				}
				bool canTakeGas = false;
				bool canTakeMin =  false;
				bool baseExceedsCapacity = false;
				if(curb->getBuildOrder() != NULL) {
					if(curb->mineralsExhausted() || curb->getNumMineralWorkers() >= curb->getBuildOrder()->getTargetMineralWorkers()*1.25) { 
						baseExceedsCapacity = true;
						canTakeMin = true;
					}
					if(curb->gasExhausted()) {
						baseExceedsCapacity = true;
						canTakeGas = true;
					}
				}
				if(baseExceedsCapacity) {
					if(curb->getBuildOrder() != NULL) {
						if(canTakeGas == false && canTakeMin == false) {
							continue;
						}
					}
				std::vector<UnitModel*> baseWorkers = curb->getWorkers();
				int taken = 0;
				for(std::vector<UnitModel*>::const_iterator k = baseWorkers.begin(); k != baseWorkers.end(); ++k) {
					UnitModel* curw = *k;
					if(curw->getUnit()->isGatheringGas() && !curb->gasExhausted()) {
						continue;
					}
					if(curw->getCurrentTaskType() == NO_TASK) {
					
					if(!curw->isFollowingPath() && (curw->getUnit()->isIdle() && curw->getRegion() != here->getBaseRegion())) {

						if(pathMap[curb].empty()) {
							////OutputDebugString(TEXT("calcuting path\n"));

							
							std::vector<TilePosition> spth = overseer->getUnitManager()->AstarSearchPathNoObstacles(curb->getBaseLOC()->getTilePosition(), here->getBaseLOC()->getTilePosition());
							
							if(!spth.empty() && spth.begin() != spth.end() && spth.size() > 1) {
								pathMap[curb]=spth;
								////OutputDebugString(TEXT("path is good\n"));
								curw->pathMove(spth);
								////OutputDebugString(TEXT("path loaded\n"));
							} else {
								////OutputDebugString(TEXT("it was bad"));
								curw->getUnit()->move(here->getPosition());
							}
						} else {
							//////OutputDebugString(TEXT("PATH MOVING"));
							curw->pathMove(pathMap[curb]);
							//////OutputDebugString(TEXT("PATH MOVING"));
						}
						foundExistingWorker = true;
						taken++;
						if(taken >= MAX_TAKE_FROM_BASE) {
							break;
						}
					}
				}
			}

		}
	}
			if(!foundExistingWorker) {
				// ok, so we might be able to train a worker, but we need to be constrained by the global worker cap
				int numWorkers = overseer->getUnitManager()->countFriendlyUnitsOfType(Broodwar->self()->getRace().getWorker());
				if(numWorkers != 0 && numWorkers > GLOBAL_WORKER_CAP) {
					return;
				}


				TrainingTask* trainWorker = new TrainingTask(here, Broodwar->self()->getRace().getWorker(), overseer);
				if(trainWorker->canInitialize()) {
					overseer->registerTask(trainWorker);
					overseer->getArbitrator()->declareNewTask(trainWorker);
					return;
				}
			}
	}
			
			
	}
	}
		

void BuildOrderTemplate::setTargetParent( BaseModel* bm )
{
	targetParent = bm;
}

void BuildOrderTemplate::addToOpeningSequenceUrgent( UnitType unit )
{
	if(here != NULL) {
		if(!here->isAlive()) {
			return;
		}
	}
	if(unit.isBuilding()) {

		ConstructionTask* c = new ConstructionTask(here, unit, overseer);
		c->setUrgent(true);
		overseer->registerTask(c);
		primeOrder.push_back(c);
	} else {
		TrainingTask* t = new TrainingTask(here, unit, overseer);
		t->setUrgent(true);
		overseer->registerTask(t);
		primeOrder.push_back(t);
	}
}

void BuildOrderTemplate::addToOpeningSequenceUrgent( UnitType unit, BuildOrderTemplate* temp )
{

	////Broodwar->sendText("expanding urgent");

	if(temp == NULL) {
		BuildOrderTemplate* rootprimeOrder = new BuildOrderTemplate(overseer);
		rootprimeOrder->setTargetMineralWorkers(16);
		rootprimeOrder->setTargetGasWorkers(3);
		//rootprimeOrder->addToOpeningSequence(UnitTypes::Protoss_Assimilator);
		ExpandTask* e = new ExpandTask(overseer, rootprimeOrder);
		here->setExpandSubject(e);
			e->setShouldCheckReserve(false);
		overseer->registerTask(e);
		primeOrder.push_back(e);
		////////Broodwar->sendText("attempting to expand");
	} else {
		ExpandTask* e = new ExpandTask(overseer, temp);
		here->setExpandSubject(e);
		e->setShouldCheckReserve(false);
		overseer->registerTask(e);
		primeOrder.push_back(e);
		//	//////Broodwar->sendText("attempting to expand to %d %d", nearestBase->getTilePosition().x(), nearestBase->getTilePosition().y());
	}
}

deque<TaskBase*> BuildOrderTemplate::getPrimeOrder()
{
	return primeOrder;
}

void BuildOrderTemplate::setManagingWorkerAcquisition(bool s)
{
	 managingWorkerAcquisition = s;
}

bool BuildOrderTemplate::isManagingWorkerAcquisition()
{
	return managingWorkerAcquisition;
}

void BuildOrderTemplate::addToOpeningSequenceWithGuardStrengthAndPosPreference( UnitType unit, int strength, BuildOrderTemplate* temp , bool posPref )
{
	if(unit == Broodwar->self()->getRace().getCenter()) {
		////////Broodwar->sendText("expanding");

		if(temp == NULL) {
			BuildOrderTemplate* rootprimeOrder = new BuildOrderTemplate(overseer);
			rootprimeOrder->setTargetMineralWorkers(18);
			rootprimeOrder->setTargetGasWorkers(3);
			//rootprimeOrder->addToOpeningSequence(UnitTypes::Protoss_Assimilator);
			ExpandTask* e = new ExpandTask(overseer, rootprimeOrder);
			targetParent->setExpandSubject(e);
				e->setPosPreference(posPref);
				e->setGuardStrength(strength);
			overseer->registerTask(e);
			primeOrder.push_front(e);
			////////Broodwar->sendText("attempting to expand");
		} else {
			ExpandTask* e = new ExpandTask(overseer, temp);
			e->setPosPreference(posPref);
			targetParent->setExpandSubject(e);
			e->setGuardStrength(strength);
			overseer->registerTask(e);
			primeOrder.push_front(e);
			//	//////Broodwar->sendText("attempting to expand to %d %d", nearestBase->getTilePosition().x(), nearestBase->getTilePosition().y());
		}
	}
}
