#include "ConstructionTask.h"
#include "UnitManager.h"
//#include "Windows.h"
#include "BaseModelConsistencyMonitor.h"
#include "BuildOrderTemplate.h"
#include "BFSBuildingPlacer.h"
	

ConstructionTask::ConstructionTask( BWAPI::TilePosition pos, BWAPI::UnitType building, Overseer* ov)
{
	executing = false;
	finished = false;
	initialized = false;
	position = pos;
	type = building;
	targetBase = NULL;
	overseer = ov;
	resourcesReserved = false;
	TASK_TYPE = CONSTRUCTION;
	timer = 0;
	startFrame = Broodwar->getFrameCount();
	initTimer = 0;
	buildTimer = 0;
	um = NULL;
		setUrgent(false);
		preReq = NULL;
		buildNearUnit = NULL;
		cmdTmr = 0;
}

ConstructionTask::ConstructionTask( BaseModel* base, BWAPI::UnitType building, Overseer* ov)
{
	executing = false;
	finished = false;
	initialized = false;
	position = TilePositions::None;
	type = building;
	targetBase = base;
	overseer = ov;
	resourcesReserved = false;
	TASK_TYPE = CONSTRUCTION;
	timer = 0;
	startFrame = Broodwar->getFrameCount();
	initTimer = 0;
		setUrgent(false);
			um = NULL;
			preReq = NULL;
			buildNearUnit = NULL;
			cmdTmr = 0;
}

ConstructionTask::ConstructionTask( UnitModel* target, BWAPI::UnitType building, Overseer* ov )
{
	executing = false;
	finished = false;
	initialized = false;
	position = TilePositions::None;
	type = building;
	targetBase = NULL;
	overseer = ov;
	resourcesReserved = false;
	TASK_TYPE = CONSTRUCTION;
	timer = 0;
	startFrame = Broodwar->getFrameCount();
	initTimer = 0;
	setUrgent(false);
	um = NULL;
	preReq = NULL;
	buildNearUnit = target;
	cmdTmr = 0;
}

//************************************
// Method:    initialized
// FullName:  ConstructionTask::initialized
// Access:    virtual private 
// Returns:   bool
// Comments:  The initialisation method essentially executes a reactive plan that ensures all of the preconditions for pursueing the task can be met
//			  so we like to make sure that, for example, we have a free worker, we have free space to build, enough resources and so forth.
//************************************
bool ConstructionTask::canInitialize()
{
	// first of all, are we even able to spend resources on this?
	if(overseer->canReserveResources(type.mineralPrice(), type.gasPrice())) {
	} else {
		return false;
	}
	if(!type.isBuilding()) {
		return false;
	}

	if(type == UnitTypes::Protoss_Nexus) {
		if(targetBase != NULL) {
			if(targetBase->hasBuildingComplete(UnitTypes::Protoss_Nexus) || targetBase->hasBuildingUnderConstruction(UnitTypes::Protoss_Nexus)) {
				forceFinish();
			}
		}
	}

	TilePosition originalPos = position;

	// find the base that is nearest to the point where we'd like to build (if we don't already have it)
	closestBase = NULL;
	if(targetBase == NULL) { // if we have a specific point to built at, rather than a base
		vector<BaseModel*> bases = overseer->getUnitManager()->getFriendlyBaseModels();
		int bestDistance = 10000;
		if(bases.empty()) {
			// something is horribly wrong - we're probably dead
			return false;
		}
		for (vector<BaseModel*>::iterator i = bases.begin(); i != bases.end(); ++i)
		{
			BaseModel* cur = *i;
			if(!cur->isAlive()) {
				continue;
			}
			if(cur->getSubject() != NULL) {
				if(cur->getSubject()->getUnit()->isBeingConstructed()) {
					continue;
				}
			}
			int curDistance = cur->getSubject()->getTilePosition().getDistance(position);
			if(curDistance < bestDistance) {
				bestDistance = curDistance;
				closestBase = cur;
			}
		}
		// if we couldn't find anything, refuse to continue
		if(closestBase == NULL) {
			return false;
		}
	} else {
		closestBase = targetBase;
	}
	if(type.requiresPsi()) {
		if(!closestBase->hasBuildingComplete(UnitTypes::Protoss_Pylon)) {
			return false;
		}
	}

	

	bool hasPrqs = hasPreReqs();
	if(hasPrqs == false) {
		checkForRegression();
		return false;
	} else {
		missingPreReqs.clear();
	}
	


	// if we have found a base, however, lets find a worker near it
	UnitModel* bestWorker = closestBase->findAvailableWorker();
	////OutputDebugString(TEXT("LOOKING FOR WOERKER \n"));
	// if we couldn't find a worker, refuse to continue
	if(bestWorker == NULL) {
		UnitModel* newWorker = overseer->getUnitManager()->findWorkerGlobal();
		////OutputDebugString(TEXT("GOING GLOBAL \n"));
		if(newWorker == NULL) {
			return false;
		} else {
			bestWorker = newWorker;
		}
	} else {
		////OutputDebugString(TEXT("FOUND WORKER LOCALLY \n"));
	}

	// else lets hook on to this worker
	subject = bestWorker;

	/*
	So now we need to find out WHERE to build. For terran this is incomplex - for zerg and protoss however, we need to make sure
	buildings are made within the sphere of influence of a pylon, or on creep.
	For now, we just pick a random pylon that we know about to build near. But we can probably do something smarter later.
	*/

	// if we're trying to build a refinery, just look for an empty gas geyser
	if(type == Broodwar->self()->getRace().getRefinery()) {
		////////Broodwar->sendText("building a refinery");
		std::set<BWAPI::Unit*> baseGeysers = closestBase->getBaseGeysers();
		if(baseGeysers.empty()) {
			forceFinish();
			return true;
		}
		for (set<BWAPI::Unit*>::iterator i = baseGeysers.begin(); i != baseGeysers.end(); ++i)
		{
			BWAPI::Unit* cur = *i;
			// will this work?
			// no, not for base locs with multiple geysers
			if(cur->getType() == UnitTypes::Resource_Vespene_Geyser) {
				position = cur->getTilePosition();
				break;
			}
		}

	} else {
		if(Broodwar->self()->getRace() != Races::Protoss) {
			//position = overseer->getBuildingPlacer()->getBuildLocation(type, closestBase->getSubject()->getTilePosition(), subject->getUnit());

			/*
			TODO: All the Zerg and Terran-specific code we might need!
			*/
		} else {

			if(type == UnitTypes::Protoss_Photon_Cannon) {
				if(buildNearUnit == NULL) {
				
				if(position == TilePositions::None) {
					position = overseer->getBuildingPlacer()->getBuildLocationNear(closestBase->getSubject()->getTilePosition(), type,0);
				}
				if(isOnBaseLoc()) {
					position = overseer->getBuildingPlacer()->getBuildLocationNear((TilePosition)closestBase->getBaseRegion()->getCenter(), type,0);
					if(isOnBaseLoc()) {
						position = overseer->getBuildingPlacer()->getBuildLocationNear(closestBase->returnRandomPylon()->getTilePosition(), type,0);
						if(isOnBaseLoc()) {
							if(closestBase->getBaseAttackLoc() != Positions::None) {
								position = overseer->getBuildingPlacer()->getBuildLocationNear((TilePosition)closestBase->getBaseAttackLoc(), type,0);
							}
							}
					}
				}




				} else {
					position = overseer->getBuildingPlacer()->getBuildLocationNear(buildNearUnit->getTilePosition(), type);
				}
			} else {
				if(type == UnitTypes::Protoss_Pylon) {
					if(position != TilePositions::None) {
						position = overseer->getBuildingPlacer()->getBuildLocationNear(position, type, 2);	// hmm	
					} else {
						int pos = 2;
						if(closestBase->countNumTypeInBase(UnitTypes::Protoss_Pylon) == 0 && !closestBase->isMainBase()) {
							pos = 0;
							position = overseer->getBuildingPlacer()->getBuildLocationNear(closestBase->getSubject()->getTilePosition(), type, pos);	// hmm	
						} else {
						if(closestBase->countNumTypeInBase(UnitTypes::Protoss_Pylon) == 0 && closestBase->isMainBase()) {
							position = overseer->getBuildingPlacer()->getBuildLocationNear(closestBase->getSubject()->getTilePosition(), type, pos);	// hmm	

						} else {
						ParticleGenerator* pg = new ParticleGenerator(256, 768, closestBase->getBaseRegion()->getCenter());
						std::vector<Position> pv = pg->generate();
						Position best = Positions::None;
						int bestVal = 5000000;
						for (std::vector<Position>::iterator it = pv.begin(); it != pv.end(); ++it)
						{
							Position cur = *it;
							if(closestBase->getBaseRegion() != BWTA::getRegion(cur)) {
								continue;
							}
							if(best == Positions::None) {
								best = cur;
							} else {
								int val = overseer->getUnitManager()->countFriendlyBuildingsInRange(UnitTypes::Protoss_Pylon, cur, 256);
								if(val < bestVal) {
									best = cur;
									bestVal = val;
								}
								if(val == 0) {
									break;
								}
							}
						}
						position = overseer->getBuildingPlacer()->getBuildLocationNear((TilePosition)best, type, pos);	// hmm	
						}
						}
	
					
					
					
					
					}
					if(targetBase != NULL) {
						if(BWTA::getRegion(position) != targetBase->getBaseRegion()) {
							std::set<UnitModel*> bbs = closestBase->getBaseBuildings();
							for (set<UnitModel*>::iterator itt = bbs.begin(); itt != bbs.end(); ++itt)
							{
								UnitModel* cbuil = *itt;
								position = overseer->getBuildingPlacer()->getBuildLocationNear(cbuil->getTilePosition(), type, 1);	// hmm	
								if(position != TilePositions::None) {
									break;
								}
							}
						}
					}
				} else {
					if(type == UnitTypes::Protoss_Nexus) {
						position = targetBase->getSubject()->getTilePosition();
						BaseModel* bm = overseer->getUnitManager()->getBaseModelInRegion(BWTA::getRegion(position));
						if(bm != NULL) {
							if(bm->hasBuildingComplete(UnitTypes::Protoss_Nexus) || bm->hasBuildingUnderConstruction(UnitTypes::Protoss_Nexus)) {
								forceFinish();
								return false;
							}
						}
						 
					} else {
						if(type == UnitTypes::Protoss_Templar_Archives || type == UnitTypes::Protoss_Citadel_of_Adun || type == UnitTypes::Protoss_Arbiter_Tribunal){
							TilePosition pos;
							int r = rand()%100;
							if(r > 30) {
								pos = subject->getBaseOwner()->getSubject()->getTilePosition();
							} else {
								pos = (TilePosition)subject->getRegion()->getCenter();
							}
							
							position = overseer->getBuildingPlacer()->getBuildLocationNear((TilePosition)pos, type, 0);
						 
						
						} else {
							if(type == UnitTypes::Protoss_Gateway) {
								position = overseer->getBuildingPlacer()->getBuildLocationNear(subject->getTilePosition(), type, 2);
							} else {
								position = overseer->getBuildingPlacer()->getBuildLocationNear((TilePosition)subject->getRegion()->getCenter(), type, 1);
							}
						}
					}
				}
			}
		}
	}
	/*
	if(position == TilePositions::None) {
		Broodwar->sendText("derp");
		ParticleGenerator* pgen = new ParticleGenerator(16, 512, subject->getRegion()->getCenter());
		std::vector<Position> v = pgen->generate();
		for (std::vector<Position>::iterator it = v.begin(); it != v.end(); ++it)
		{
			Position c = *it;
			if(BWTA::getRegion(c) == subject->getRegion()) {
				position = overseer->getBuildingPlacer()->getBuildLocationNear((TilePosition)c, type, 1);
				if(position != TilePositions::None) {
					break;
				}
			}
		}
	}
	*/

	// initialisation is complete, we can now continue the task
	//Broodwar->drawEllipseMap(position.x(), position.y(), 32, 32, BWAPI::Colors::Purple, true);
	if(Broodwar->canBuildHere(subject->getUnit(), position, type, true)) {
		initialized = true;
		if(!resourcesReserved) {
			overseer->reserveResources(type.mineralPrice(), type.gasPrice());
			resourcesReserved = true;
		}
		subject->setCurrentTaskType(CONSTRUCTION);
		if(type != UnitTypes::Protoss_Assimilator) {
			overseer->getBuildingPlacer()->reserveTiles(position, type.tileWidth(), type.tileHeight());
		}
		return initialized;
	} else {
		initialized = false;
		position = originalPos;
		return initialized;
	}
}


//************************************
// Method:    execute
// FullName:  ConstructionTask::execute
// Access:    virtual public 
// Returns:   bool
// Comments:  Execute basically causes the correct command to be sent to the right unit that we located during initialisation.
//************************************
bool ConstructionTask::execute()
{

	if(subject == NULL || !subject->isAlive()) {
		subject = closestBase->findAvailableWorker();
		if(subject != NULL) {
			////OutputDebugString(TEXT("FOUND A WORKER AT THE CLOSEST BASE \n"));
			subject->setCurrentTaskType(CONSTRUCTION);
		} else {
			////OutputDebugString(TEXT("GOING GLOBAL \n"));
			subject = overseer->getUnitManager()->findWorkerGlobal();
			return false;
		}
	}
	bool wait = false;
	if(type != UnitTypes::Protoss_Assimilator) {
		std::set<Unit*> unitsOnTile = Broodwar->getUnitsInRectangle(((Position)position).x(), ((Position)position).y(), ((Position)position).x()+(type.tileWidth()*32), ((Position)position).y()+(type.tileHeight()*32));

		if(!unitsOnTile.empty()) {
			for(std::set<Unit*>::const_iterator i = unitsOnTile.begin(); i != unitsOnTile.end(); i++) {
				BWAPI::Unit* c = *i;
				if(c == subject->getUnit()) {
					continue;
				}
				if(c->getType().isBuilding() && c->getType() != type) {
					position = overseer->getBuildingPlacer()->getBuildLocationNear(c->getTilePosition(), type,1);
					return false; 
				}
				if(c->getPlayer() == Broodwar->self() && !c->getType().isFlyer() && !c->getType().isBuilding()) {
					//////Broodwar->sendText("Can't build, as there are dudes on the tile!!");
					std::vector<UnitModel*> buildingSet = overseer->getUnitManager()->getFriendlyBuildings();
					Position moveTo = Positions::None;
					bool foundPosition = false;
					if(!c->getType().isWorker()) {
						overseer->getUnitManager()->getUnitModelFromMap(c)->setCurrentTaskType(NO_TASK);
					}
					c->move(BWTA::getRegion(c->getPosition())->getPolygon().getNearestPoint(c->getPosition()));
					wait = true;
				}
			}
		}
		if(wait) {
			initTimer++;
			if(initTimer >= 32) {
				if(type != UnitTypes::Protoss_Nexus) {
					position = overseer->getBuildingPlacer()->getBuildLocationNear((TilePosition)closestBase->getBaseRegion()->getCenter(), type);
				} else {
					position = overseer->getBuildingPlacer()->getBuildLocationNear(position, type);
				}

			}
			return false;
		}
	}
	if(!wait) {
		subject->getUnit()->build(position, type);	
		std::set<Unit*> unitsOnTile = Broodwar->getUnitsInRectangle(((Position)position).x(), ((Position)position).y(), ((Position)position).x()+(type.tileWidth()*32), ((Position)position).y()+(type.tileHeight()*32));
		for(std::set<Unit*>::const_iterator i = unitsOnTile.begin(); i != unitsOnTile.end(); i++) {
			if((*i)->getType() == type) {
				executing = true;
				break;
			}
		}
		if(executing) {
			subject->setCurrentTaskType(NO_TASK);
			if(BWTA::getRegion(position) != closestBase->getSubject()->getRegion()) {
				subject->getUnit()->move(closestBase->getSubject()->getPosition(), true);
			}
			
			buildTimer = 0;
		} else {

			if(subject->getPosition().getDistance((Position)position) < 64) {
				buildTimer++;
			}
			if(buildTimer >= 1024+(rand()%25)) {

				buildTimer = 0;
			} 
		}
	}

	return executing;
}

//************************************
// Method:    monitor
// FullName:  ConstructionTask::monitor
// Access:    virtual public 
// Returns:   void
// Comments:  Now we monitor our unit to ensure nothing goes wrong.
//************************************
void ConstructionTask::monitor()
{
	if(executing) {


		if(Broodwar->self()->getRace() == BWAPI::Races::Protoss && !subject->getUnit()->isIdle() && subject->getUnit()->isMoving()) {
			return;
		}

		BWAPI::Unit* currentUnit = NULL;
		// now grab the building from the tile the worker was meant to be constructing on
		std::set<Unit*> unitsOnTile = Broodwar->getUnitsInRectangle(((Position)position).x()-16, ((Position)position).y()-16, ((Position)position).x()+(type.tileWidth()*32)+16, ((Position)position).y()+(type.tileHeight()*32)+16);

		for (set<BWAPI::Unit*>::iterator i = unitsOnTile.begin(); i != unitsOnTile.end(); ++i)
		{

			if(((*i)->isMorphing() || (*i)->isBeingConstructed()) && (*i)->getType() == type && (*i)->getPlayer() == Broodwar->self()) {
				currentUnit = *i;
				// relinquish the resources as soon as this thing starts to be built
				// so we keep current.
				UnitModel* u = overseer->getUnitManager()->getUnitModelFromMap(currentUnit);
				if(u == NULL) {

				} else {
					//if(type != UnitTypes::Protoss_Pylon) {
					executing = false;
					finished = true;
					initialized = true;
					um = u;
					//}
					if(resourcesReserved) {
						overseer->relinquishResources(type.mineralPrice(), type.gasPrice()); 
						resourcesReserved = false;
					}
					subject->setCurrentTaskType(NO_TASK);

					////////Broodwar->sendText("Relinquishing resources for %ss", type.getName().c_str());
					break;
				}
			}


			//}
		}
		// all we need to do from here is to watch out for it being completed
		// or we could look for other things - like the constructor being
		// attacked, interrupted, or the building being attacked as it's being
		// constructed. in which case we might want to cancel construction to
		// save resources. that's for later though!			
		if(currentUnit != NULL) {	
			if(resourcesReserved) {
				overseer->relinquishResources(type.mineralPrice(), type.gasPrice()); 
				resourcesReserved = false;
			}
			subject->setCurrentTaskType(NO_TASK);
			UnitModel* u = overseer->getUnitManager()->getUnitModelFromMap(currentUnit);
			if(u == NULL) {
				////////OutputDebugString(TEXT("null model found\n"));
			} else {
				if(u->getBaseOwner() == NULL) {
					////////OutputDebugString(TEXT("owner attached to \n"));
					u->setBaseOwner(closestBase);
					if(u->getType() == UnitTypes::Protoss_Nexus) {
						closestBase->setSubject(u);
					}
				}
			}
			int bt = currentUnit->getRemainingBuildTime();
			////////Broodwar->sendText("time left: %d", bt);
			if( bt <= 10) {
				if(timer < 128) {
					timer++;
				} else {
					//////Broodwar->sendText("unit has finished");
					executing = false;
					finished = true;
					initialized = true;
					if(resourcesReserved) {
						overseer->relinquishResources(type.mineralPrice(), type.gasPrice()); 
						resourcesReserved = false;
					}
					return;
				}
			}


		} else {
			int thresh = 512;
			if(type == UnitTypes::Protoss_Pylon) {
				thresh = 32;
			}
			if(timer < thresh) {
				timer++;
			} else {
				//////Broodwar->sendText("---- task timer run out, abandoning task");
				if(resourcesReserved) {
					overseer->relinquishResources(type.mineralPrice(), type.gasPrice()); 
					resourcesReserved = false;
				}
				executing = false;
				finished = true;
				initialized = false;

				timer = 0;
				return;
			}
		}
	}
}

//************************************
// Method:    isExecuting
// FullName:  ConstructionTask::isExecuting
// Access:    virtual public 
// Returns:   bool
// Comments:  none
//************************************
bool ConstructionTask::isExecuting()
{
	return executing;
}

//************************************
// Method:    isInitialized
// FullName:  ConstructionTask::isInitialized
// Access:    virtual public 
// Returns:   bool
// Comments:  none
//************************************
bool ConstructionTask::isInitialized()
{
	return initialized;
}

//************************************
// Method:    isFinished
// FullName:  ConstructionTask::isFinished
// Access:    virtual public 
// Returns:   bool
// Comments:  none
//************************************
bool ConstructionTask::isFinished()
{
	return finished;
}

void ConstructionTask::setTargetBase( BaseModel* b )
{
	targetBase = b;
}

bool ConstructionTask::hasPreReqs()
{
	// lets manually check the prereqs
	std::map<UnitType, int> preReqs = type.requiredUnits();
	std::map<UnitType, int>::const_iterator i;
	for(i = preReqs.begin(); i != preReqs.end(); ++i){
		// we don't care if the pre-requisite is a worker, because well DUH!
		if(preReq != NULL) {
			if(preReq->getBuildType() == (*i).first) {
				continue;
			}
		}
		if(overseer->getUnitManager()->hasBuildingComplete((*i).first) || (*i).first == Broodwar->self()->getRace().getWorker()) {
			
		} else {
			
			return false;
		}
	}
	// BWAPI bug, looks like it doesn't have stargate in the prereq list for tribunals
	if(type == UnitTypes::Protoss_Arbiter_Tribunal) {
		if(overseer->getUnitManager()->hasBuildingComplete(UnitTypes::Protoss_Stargate)) {
		} else {
			return false;
		}
	}
	return true;
}

bool ConstructionTask::checkForRegression()
{

	if(!missingPreReqs.empty()) {
		return true;
	}
	std::map<UnitType, int> preReqs = type.requiredUnits();

	for(std::map<UnitType, int>::const_iterator itt = preReqs.begin(); itt != preReqs.end(); ++itt){
		
		if(!((*itt).first).isBuilding()) {
			continue;
		}
		bool foundPrq = false;
		if(preReq != NULL) {
			if(preReq->getBuildType() == (*itt).first) {
				continue;
			}
		}

		if(((*itt).first) == UnitTypes::Protoss_Arbiter_Tribunal) {
			if(overseer->getUnitManager()->hasBuildingUnderConstruction(UnitTypes::Protoss_Stargate) || overseer->getUnitManager()->hasBuildingComplete(UnitTypes::Protoss_Stargate) || overseer->getArbitrator()->isStagedToBeBuilt(UnitTypes::Protoss_Stargate)) {
				foundPrq = true;
			}
		}

		std::vector<BaseModel*> bases = overseer->getUnitManager()->getFriendlyBaseModels();
		
		for (vector<BaseModel*>::iterator i = bases.begin(); i != bases.end(); ++i) {
			BaseModel* base = (*i);
			BuildOrderTemplate* bot = base->getBuildOrder();
			if(bot != NULL) {
				std::deque<TaskBase*> tasks = bot->getPrimeOrder();

				for (deque<TaskBase*>::iterator it = tasks.begin(); it != tasks.end(); ++it)
				{
					TaskBase* t = *it;
					if(t->getBuildType() == (*itt).first) {
						foundPrq = true;
						break;
					}
				}
				if(foundPrq) {
					break;
				}
			}
		}

		if(overseer->getArbitrator()->isStagedToBeBuilt((*itt).first) || overseer->getUnitManager()->hasBuildingComplete((*itt).first) || overseer->getUnitManager()->hasBuildingUnderConstruction((*itt).first) || overseer->isTaskRegisteredFor((*itt).first)) {
			foundPrq = true;
		}

		if(foundPrq) {
			continue;
		}
		if(!foundPrq) {
			missingPreReqs.insert((*itt).first);
		}
	}

	if(!missingPreReqs.empty()) {
		////OutputDebugString(TEXT("MISSING SOME PREREQS LOOK!!"));

		// right, now lets spawn these prereqs 
		for(std::set<UnitType>::const_iterator prqs = missingPreReqs.begin(); prqs != missingPreReqs.end(); prqs++) {
			UnitType cur = *prqs;
			if(targetBase != NULL) {
				if(targetBase->getBuildOrder() != NULL) {
					targetBase->getBuildOrder()->addToOpeningSequenceUrgent(cur);
				}
			}

		}
	}

	return true;



}

UnitModel* ConstructionTask::getEventualSubject()
{
	return um;
}

bool ConstructionTask::isOnBaseLoc()
{
	if(position != TilePositions::None) {
		BWTA::BaseLocation* nbl = BWTA::getNearestBaseLocation(position);
		TilePosition btp = nbl->getTilePosition();

		if(position.x() >= btp.x()) {
			if(position.x() <= btp.x()+UnitTypes::Protoss_Nexus.tileWidth()) {
				if(position.y() >= btp.y()) {
					if(position.y() <= btp.y()+UnitTypes::Protoss_Nexus.tileHeight()) {
						return true;
					}
				}
			}
		}
	}
	return false;
}
