#include "ExpandTask.h"
#include "Overseer.h"
//#include "Windows.h"

ExpandTask::ExpandTask( BWAPI::TilePosition pos, Overseer* ov )
{
	position = pos;
	overseer = ov;
	workerMovingToLocation = false;
	workerCanBuildBase = false;
	type = Broodwar->self()->getRace().getCenter();
	initialized = false;
	executing = false;
	finished = false;
	checkReserve = true;
	checkForBase = true;
	targetBase = NULL;
	templateToAdd = NULL;
	TASK_TYPE = EXPAND;
	timer = 0;
	resourcesReserved = false;
	guardStrength = 2;
	startFrame = Broodwar->getFrameCount();
	setUrgent(false);
	posPref = false;
}

ExpandTask::ExpandTask( BWAPI::TilePosition pos, Overseer* ov, BuildOrderTemplate* bo )
{
	position = pos;
	overseer = ov;
	workerMovingToLocation = false;
	workerCanBuildBase = false;
	type = Broodwar->self()->getRace().getCenter();
	initialized = false;
	executing = false;
	finished = false;
	targetBase = NULL;
	templateToAdd = bo;
		checkReserve = true;
		checkForBase = true;
	TASK_TYPE = EXPAND;
		timer = 0;
			resourcesReserved = false;
			subject = NULL;
			guardStrength = 2;
			startFrame = Broodwar->getFrameCount();
				setUrgent(false);
					posPref = false;
}

ExpandTask::ExpandTask( Overseer* ov )
{
	position = TilePositions::None;
	overseer = ov;
	workerMovingToLocation = false;
	workerCanBuildBase = false;
	type = Broodwar->self()->getRace().getCenter();
	initialized = false;
	executing = false;
	finished = false;
	targetBase = NULL;
	templateToAdd = NULL;
		checkReserve = true;
	TASK_TYPE = EXPAND;
		timer = 0;
			resourcesReserved = false;
			checkForBase = true;
					subject = NULL;
					guardStrength = 2;
						setUrgent(false);
							posPref = false;
}

ExpandTask::ExpandTask( Overseer* ov, BuildOrderTemplate* bo )
{
	position = TilePositions::None;
	overseer = ov;
	workerMovingToLocation = false;
	workerCanBuildBase = false;
	type = Broodwar->self()->getRace().getCenter();
	initialized = false;
	executing = false;
	finished = false;
	targetBase = NULL;
		checkReserve = true;
	templateToAdd = bo;
	TASK_TYPE = EXPAND;
	checkForBase = true;
	timer = 0;
	resourcesReserved = false;
	subject = NULL;
	guardStrength = 2;
		setUrgent(false);
			posPref = false;
}


//************************************
// Method:    canInitialize
// FullName:  ExpandTask::canInitialize
// Access:    virtual public 
// Returns:   bool
// Comments:  none
//************************************
bool ExpandTask::canInitialize()
{
	////////Broodwar->sendText("trying to initialize expansion");
	// first of all, are we even able to spend resources on this?
	if(checkReserve) {
	if(overseer->canReserveResources(type.mineralPrice(), type.gasPrice())) {
	} else {
		return false;
	}
	}
	if(initialized) {
		return true;
	}

	// look for enemy bases, and look to find the next point to expand to, in this case
	if(position == TilePositions::None) {
		findExpandLocation(true);
		if(position == TilePositions::None) {
			return false;
		}
	}


	// find the base that is nearest to the point where we'd like to build (if we don't already have it)
	BaseModel* 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;
		for (vector<BaseModel*>::iterator i = bases.begin(); i != bases.end(); ++i)
		{
			BaseModel* cur = *i;
			if(cur->isAlive() && !cur->getWorkers().empty()) {
			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 {
			targetBase = closestBase;
		}


	} else {
		closestBase = targetBase;
	}
	if(closestBase->isUnderThreatMin(3)) {
	//	////OutputDebugString(TEXT("BASE IS UNDER ATTACK"));
		return false;
	}
	if(subject == NULL) {
	// if we have found a base, however, lets find a worker near it
	UnitModel* bestWorker = closestBase->findAvailableWorker();

	// if we couldn't find a worker, refuse to continue
	if(bestWorker == NULL) {
		bestWorker = overseer->getUnitManager()->findWorkerGlobal();
		if(bestWorker == NULL) {
			return false;
		}
	}
	
	// else lets hook on to this worker
	subject = bestWorker;
	}
	//////Broodwar->sendText("attempting to expand to: %d %d", position.x(), position.y());

	//path = overseer->AstarSearchPath((TilePosition)subject->getPosition(), position);



	//if(overseer->canReserveResources(type.mineralPrice(), type.gasPrice())) {
		
		overseer->reserveResources(type.mineralPrice(), type.gasPrice());
		resourcesReserved = true;
		////Broodwar->sendText("***** sending path move command");
		//subject->pathMove(position, PATHFINDING_THREATAWARE_ASTAR_GROUND_NO_OBSTACLES);
		subject->setCurrentTaskType(EXPAND);
		subject->clearMicroBehaviour();
		path = overseer->getUnitManager()->AstarSearchPathNoObstacles(closestBase->getBaseLOC()->getTilePosition(), position);
		if(path.empty() || path.begin() == path.end())  {
			////OutputDebugString(TEXT("PATH WAS BAD\n"));
			subject->getUnit()->move((Position)position);
			overseer->getUnitManager()->requestDefense(subject->getUnit(), guardStrength);
		} else {
			subject->pathMove(path);
			overseer->getUnitManager()->requestDefense(path, guardStrength);
		}

		if(subject != NULL && position != TilePositions::None) {
		workerMovingToLocation = true;
		initialized = true;
		} else {
			return false;
		}

	return initialized;
}

//************************************
// Method:    execute
// FullName:  ExpandTask::execute
// Access:    virtual public 
// Returns:   bool
// Comments:  none
//************************************
bool ExpandTask::execute()
{
	if(subject != NULL && subject->isAlive()) {
		
		//Broodwar->drawCircleMap(subject->getPosition().x(), subject->getPosition().y(), 32, BWAPI::Colors::Green, false);
		//Broodwar->drawLineMap(subject->getPosition().x(), subject->getPosition().y(), position.x(), position.y(), BWAPI::Colors::Green);
		//Broodwar->drawBoxMap(((Position)position).x(), ((Position)position).y(), ((Position)position).x()+(type.tileWidth()*32), ((Position)position).y()+(type.tileHeight()*32), BWAPI::Colors::White, true);
		subject->setCurrentTaskType(EXPAND);
	} else {

		subject = overseer->getUnitManager()->findWorkerGlobal();
		if(subject == NULL) {
			return false;
		} else {
			subject->setCurrentTaskType(EXPAND);
			if(path.empty() || path.begin() == path.end())  {
				////OutputDebugString(TEXT("PATH WAS BAD\n"));
				subject->getUnit()->move((Position)position);
			} else {
				subject->pathMove(path);
			}
		}

	}



	// first lets see if we can actually still build here...
	if(checkForBase) {
		BaseModel* bmir = overseer->getUnitManager()->getBaseModelInRegion(BWTA::getRegion(position));
		bool hasCreep = Broodwar->hasCreep(position);
		bool hasBase = false;
		if(bmir != NULL) {
			if(bmir->isAlive()) {
				hasBase = true;
			}
		}
		
		if(hasBase || hasCreep) {
				findExpandLocation(true);
				if(position == TilePositions::None) {
					return false;
				} else {
					subject->setCurrentTaskType(EXPAND);
					path = overseer->getUnitManager()->AstarSearchPathNoObstacles(targetBase->getBaseLOC()->getTilePosition(), position);
					bool hasPath = false;
					if(path.empty() || path.begin() == path.end())  {
						////OutputDebugString(TEXT("PATH WAS BAD\n"));
						subject->getUnit()->move((Position)position);
					} else {
						subject->pathMove(path);
						hasPath = true;
					}
					for(std::vector<UnitModel*>::const_iterator k = maynardedWorkers.begin(); k != maynardedWorkers.end(); ++k) {
						UnitModel* curw = *k;
						curw->pathStop();
						if(hasPath) {
							curw->getUnit()->move((Position)position);
						} else {
							curw->pathMove(path);
						}
					}

					workerMovingToLocation = true;
					workerCanBuildBase = false;
				}
			
		}
	}



	if(workerMovingToLocation) {

		
		if(subject->getPosition().getDistance((Position)position) < 128) {
			////////Broodwar->sendText("worker at location");
			workerCanBuildBase = true;
			workerMovingToLocation = false;
			subject->pathStop();
		} else {
				if(!subject->getUnit()->isMoving() || subject->getUnit()->isGatheringGas() || subject->getUnit()->isGatheringMinerals() ) {
					if(subject->isFollowingPath()) {
						
					} else {
						subject->getUnit()->move((Position)position);
					}
				return false;
				}

		}
	}

	std::set<Unit*> unitsOnTile = Broodwar->getUnitsInRectangle(((Position)position).x(), ((Position)position).y(), ((Position)position).x()+(type.tileWidth()*32), ((Position)position).y()+(type.tileHeight()*32));

	bool wait = false;
	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->getPlayer() == Broodwar->self() && !c->getType().isBuilding()) {
				//////Broodwar->sendText("Can't build, as there are dudes on the tile!!");
				Position moveTo = Positions::None;
				bool foundPosition = false;
				UnitModel* m = overseer->getUnitManager()->getUnitModelFromMap(c);
				if(m != NULL) {
					if(m->getCurrentTaskType() == GUARD) {
						m->setCurrentTaskType(NO_TASK);
					}
				}
				if(c->getType().isWorker()) {
					UnitModel* um = overseer->getUnitManager()->getUnitModelFromMap(c);
					if(um != NULL) {
						if(um->getCurrentTaskType() == EXPAND) {
							wait = true;
							continue;
						}
					}
				} 
				c->move(BWTA::getRegion(c->getPosition())->getPolygon().getNearestPoint(c->getPosition()));
				
				wait = true;
			}
		}
		
	}
	if(wait) {
		//Broodwar->drawEllipseMap(subject->getPosition().x(), subject->getPosition().y(), 6, 6, BWAPI::Colors::Red, true);
		return false;
	}

	if(workerCanBuildBase && !workerMovingToLocation) {
		//Broodwar->drawCircleMap(subject->getPosition().x(), subject->getPosition().y(), 32, BWAPI::Colors::Orange, false);

		if(subject->isAlive()) {
		bool buildSuccess = subject->getUnit()->build(position, type);	
		if(buildSuccess) {
			checkForBase = false;
		}
		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() == UnitTypes::Protoss_Nexus) {
					executing = true;
					subject->setCurrentTaskType(NO_TASK);
					//BaseModel* originalOwner = subject->getBaseOwner();
					//if(originalOwner != NULL) {
					//	if(originalOwner != overseer->getUnitManager()->getBaseModelInRegion(subject->getRegion())){
					//	originalOwner->deRegisterWorker(subject);
					//	}
					//}
					//BaseModel* newOwner = overseer->getUnitManager()->getBaseModelInRegion(subject->getRegion());
					//if(newOwner != NULL) {
					//	newOwner->registerWorker(subject);
					//}

					break;
			}
		}
		} else {
		
			subject = overseer->getUnitManager()->findWorkerGlobal();
			if(subject != NULL) {
				workerMovingToLocation = true;
				workerCanBuildBase = false;
			}
			
			
		}
	} 



	return executing;
}

//************************************
// Method:    monitor
// FullName:  ExpandTask::monitor
// Access:    virtual public 
// Returns:   void
// Comments:  none
//************************************
void ExpandTask::monitor()
{
	if(executing) {
		////////Broodwar->sendText("executing expansion");
		// for protoss see if the probe has stopped yet
		if(Broodwar->self()->getRace() == BWAPI::Races::Protoss && subject->getUnit()->isMoving()) {
			return;
		}

		BWAPI::Unit* currentUnit = NULL;
		// now grab the building from the tile the worker was meant to be constructing on
		set<BWAPI::Unit*> tile = Broodwar->getUnitsOnTile(position.x(), position.y());
		for (set<BWAPI::Unit*>::iterator i = tile.begin(); i != tile.end(); ++i)
		{
			BWAPI::Unit* cur = *i;
			if(cur->isBeingConstructed() && cur->getType() == type && cur->getPlayer() == Broodwar->self()) {
				currentUnit = cur;
				// relinquish the resources as soon as this thing starts to be built
				// so we keep current.
				if(resourcesReserved) {
					//////Broodwar->sendText("relinquishing expansion resources");
					overseer->relinquishResources(type.mineralPrice(), type.gasPrice()); 
					resourcesReserved = false;
				}
				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(templateToAdd != NULL) {
				BaseModel* target = overseer->getUnitManager()->getBaseModelFromMap(currentUnit);
				if(target != NULL) {
					////Broodwar->sendText("build order template attached");
					target->setBuildOrderTemplate(templateToAdd);
					executing = false;
					finished = true;

					// maynard
					std::vector<BaseModel*> bases = overseer->getUnitManager()->getFriendlyBaseModels();

					for(std::vector<BaseModel*>::const_iterator j = bases.begin(); j != bases.end(); ++j) {
						BaseModel* curb = *j;
						if(curb->mineralsExhausted()) {
							std::vector<UnitModel*> baseWorkers = curb->getWorkers();
							if(!baseWorkers.empty()) {
								curb->setWorkerManagement(false);
								for(std::vector<UnitModel*>::const_iterator k = baseWorkers.begin(); k != baseWorkers.end(); ++k) {
									UnitModel* curw = *k;
									if(curw->getUnit()->isGatheringGas() && !curb->gasExhausted()) {
										continue;
									}
									if(path.empty()) {
										curw->pathMove(path);
										maynardedWorkers.push_back(curw);
									} else {
									curw->getUnit()->move((Position)position);
									}
								}
								break;
							}
						}

					}
					
				} else {
					//////Broodwar->sendText("target null");
				}
			} else {
				//////Broodwar->sendText("template null");
				executing = false;
				finished = true;
			}
			//}
		} else {
			// err?
			subject->getUnit()->build(position, type);	
		}
	}
}

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

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

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

BWAPI::TilePosition ExpandTask::getTargetPosition()
{
	return position;
}

void ExpandTask::setGuardStrength( int g )
{
	guardStrength = g;
}

void ExpandTask::setShouldCheckReserve( bool c )
{
		checkReserve = c;
		if(c == false) {
				setUrgent(true);
		}
}

void ExpandTask::findExpandLocation(bool preferGas)
{

	std::set<BWTA::BaseLocation*> bases = BWTA::getBaseLocations();
	BWTA::BaseLocation* nearestBase = NULL;
	TilePosition bestLoc = TilePositions::None;
	BaseModel* lastBase = NULL;
	std::vector<BaseModel*> fb = overseer->getUnitManager()->getFriendlyBaseModels();
	bool gasFulfilled = false;

	Position bestLocPos = Positions::None;

	for (std::vector<BaseModel*>::reverse_iterator ri = fb.rbegin(); ri != fb.rend(); ++ri)
	{
		BaseModel* curRb = *ri;
		if(curRb->isAlive()) {
			lastBase = curRb;
			break;
		}
	}
	if(lastBase == NULL) {
		return;
	}
	for (std::set<BaseLocation*>::iterator i = bases.begin(); i != bases.end(); ++i)
	{
		BaseLocation* cur = *i;
		if(cur->isIsland()) {
			continue;
		}
		if(overseer->getUnitManager()->isBlackListed(cur->getRegion())) {
			continue;
		}
		if(preferGas) {
			if(cur->getGeysers().empty()) {
				continue;
			}
		}
		if(overseer->getUnitManager()->knownBaseInRegion((Position)cur->getTilePosition())) {
			BaseModel* bm = overseer->getUnitManager()->getBaseModelInRegion(cur->getRegion());
			if(bm->isAlive() || bm->mineralsExhausted()) {
				continue;
			}
		}

		if(!posPref) {	
			
		if(Broodwar->hasCreep(cur->getTilePosition())) {
			continue;
		}
		if(nearestBase == NULL) {
			nearestBase = cur;
			bestLoc = nearestBase->getTilePosition();
		} else {

			TilePosition startLoc = lastBase->getBaseLOC()->getTilePosition();
			TilePosition curLoc = cur->getTilePosition();

	

			int distance = BWTA::getGroundDistance(startLoc, curLoc);
			int bestDistance = BWTA::getGroundDistance(startLoc, bestLoc);
			if(distance < bestDistance && cur != lastBase->getBaseLOC()) {
				nearestBase = cur;
				bestLoc = nearestBase->getTilePosition();
			}
		}
		} else {
			if(Broodwar->hasCreep(cur->getTilePosition())) {
				continue;
			}
			if(nearestBase == NULL) {
				nearestBase = cur;
				bestLocPos = nearestBase->getPosition();
			} else {

				Position startLoc = overseer->getUnitManager()->getFriendlyMainBase()->getPosition();
				Position curLoc = cur->getPosition();

				int distance = startLoc.getDistance(curLoc);
				int bestDistance = startLoc.getDistance(bestLocPos);
				if(distance < bestDistance && cur != lastBase->getBaseLOC()) {
					nearestBase = cur;
					bestLocPos = nearestBase->getPosition();
				}
			}

		}
	}
	if(nearestBase != NULL) {
		if(preferGas) {
			if(nearestBase->getGeysers().empty()) {
				gasFulfilled = false;
			} else {
				gasFulfilled = true;
			}
		} else {
			gasFulfilled = true;
		}
	}
	if(nearestBase == NULL || !gasFulfilled) {
		// this happens if ALL the map's base points have been taken or are being taken, or if we have taken all of the gas
		findExpandLocation(false);
		return;
	}

		position = nearestBase->getTilePosition();

	


}

void ExpandTask::setPosPreference( bool p )
{
	posPref = p;
}
