#include "TrainingTask.h"
#include "Overseer.h"
//#include "Windows.h"
#include "MapData.h"

TrainingTask::TrainingTask( BaseModel* base, BWAPI::UnitType unitType, Overseer* ov)
{
	executing = false;
	finished = false;
	initialized = false;
	targetBase = base;
	type = unitType;
	overseer = ov;
	resourcesReserved = false;
	targetConstructor = NULL;
	TASK_TYPE = TRAINING;
	rallyLoc = Positions::None;
	rallyBaseID = RALLY_NONE;
	startFrame = Broodwar->getFrameCount();
	trainingBegun = false;
}

//************************************
// Method:    initialized
// FullName:  TrainingTask::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.
//************************************
bool TrainingTask::canInitialize()
{
	// things we need to know: 
	// whether we can reserve minerals/gas to build this unit
	if(!resourcesReserved) {
		if(overseer->canReserveResources(type.mineralPrice(), type.gasPrice())) {
		} else {
			return false;
		}
	}

	// whether we have the right tech to build this unit
	// whether the target base has the correct facilities to build this unit
	if(Broodwar->canMake(NULL, type)) { // if we have the right tech, facilities and cash, we can make the unit
		// but do we have the right facilities at the requested base?

		// we assume that the call to canMake checks for the case where a unit requires
		// two or more buildings in order to be produced - ie. we assume this is taken account of
		// in the check for the correct tech. so here we only look for a building that can 
		// physically produce the unit we want.
		if(findTrainingTarget()) {
			
			if(!resourcesReserved) {
				if(overseer->canReserveResources(type.mineralPrice(), type.gasPrice())) {
					overseer->reserveResources(type.mineralPrice(), type.gasPrice());
					resourcesReserved = true;
					initialized = true;
				} else {
					return false;
				}
			}
			return true;
		} else {
			return false;
		}
	}

	// if we're here, we probably didn't find a building at this base we could use to construct the unit :(
	return false;
}

//************************************
// Method:    execute
// FullName:  TrainingTask::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 TrainingTask::execute()
{
	if(targetConstructor == NULL){
		////OutputDebugString(TEXT("SOMETHING IS HOIRRBLY WRONG \n"));
		return false;
	}


	if(!targetConstructor->getUnit()->isTraining()) {
		executing = targetConstructor->getUnit()->train(type);
		if(executing) {
				switch(rallyBaseID) {
				case RALLY_NONE:
					{
						rallyLoc = targetConstructor->getPosition();
						targetConstructor->getUnit()->setRallyPoint(targetConstructor->getPosition());
						break;
					}
				case RALLY_START_CHOKEPOINT:
					{
						Position cl = (*BWTA::getStartLocation(Broodwar->self())->getRegion()->getChokepoints().begin())->getCenter();
						ParticleGenerator* p = new ParticleGenerator(256, 128, cl);

						rallyLoc = *p->generateEmpty().begin();
						
						targetConstructor->getUnit()->setRallyPoint(rallyLoc);
						break;
					}
				case RALLY_START_CHOKEPOINT_BACK:
					{
						rallyLoc = findPointOnChokePoint(true);
						targetConstructor->getUnit()->setRallyPoint(rallyLoc);
						break;
					}
				case RALLY_START_CHOKEPOINT_FRONT:
					{
						rallyLoc = findPointOnChokePoint(false);
						targetConstructor->getUnit()->setRallyPoint(rallyLoc);
						break;
					}
				case RALLY_NATURAL:
					{
						BaseModel* start = overseer->getUnitManager()->getFriendlyMainBase();

						std::set<BWTA::Region*> rs = overseer->getOffenseMonitor()->getConnectedRegions(start->getBaseRegion());

						for (std::set<BWTA::Region*>::iterator it = rs.begin(); it != rs.end(); ++it)
						{
							if((*it) != start->getBaseRegion()) {
								rallyLoc = (*it)->getCenter();
								targetConstructor->getUnit()->setRallyPoint(rallyLoc);
									break;
							}
						}
						break;
					}
				}
		}
	}
	return executing;
}

//************************************
// Method:    monitor
// FullName:  TrainingTask::monitor
// Access:    virtual public 
// Returns:   void
// Comments:  Now we monitor our unit to ensure nothing goes wrong.
//************************************
void TrainingTask::monitor()
{
	if(rallyLoc != Positions::None) {
		//Broodwar->drawEllipseMap(rallyLoc.x(), rallyLoc.y(), 16,16, BWAPI::Colors::Green, true);
		//Broodwar->drawLineMap(rallyLoc.x(), rallyLoc.y(), targetConstructor->getPosition().x(), targetConstructor->getPosition().y(), BWAPI::Colors::Green);
	}
	
	if(!targetConstructor->isAlive()) {
		////OutputDebugString(TEXT("TARGET CONSTRUCTOR DEAD\n"));
		if(findTrainingTarget()) {
			executing =  targetConstructor->getUnit()->train(type);
		}
	}
	



	if(executing) {
		// quite straightforward
		// just monitor the unit until it finishes training
		if(!trainingBegun) {
			if(targetConstructor->getUnit()->isTraining()) {
				trainingBegun = true;
				targetConstructor->setReservedStatus(true);
				if(rallyLoc != Positions::None) {
					targetConstructor->getUnit()->setRallyPoint(rallyLoc);
				}
			}
			return;
		}
		if(trainingBegun) {
			
			if(resourcesReserved) {
				overseer->relinquishResources(type.mineralPrice(), type.gasPrice()); 
				resourcesReserved = false;
			}

			if(targetConstructor->getUnit()->getRemainingTrainTime() <= 24 || !targetConstructor->getUnit()->isTraining()) {
			
				executing = false;
				finished = true;
				targetConstructor->setReservedStatus(false);
				initialized = false;
			}
		}
	}
}

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

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

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

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

void TrainingTask::setRallyLocation( Position p )
{
	rallyLoc = p;
}

void TrainingTask::setRallyLocation( int baseID )
{
	rallyBaseID = baseID;
}


bool TrainingTask::findTrainingTarget()
{
	std::pair<UnitType, UnitType> constructors = type.whatBuilds();
	// get the contents of this base
	set<UnitModel*> potentialProducers;
	if(type.isWorker()) {
		potentialProducers = targetBase->getBaseBuildings();
	} else {
		potentialProducers = overseer->getUnitManager()->getAllFriendlyBuildingsOfType(constructors.first);
	}
	for (set<UnitModel*>::iterator i = potentialProducers.begin(); i != potentialProducers.end(); ++i)
	{
		// hopefully we only care about the first part of the pair
		UnitModel* currentBuilding = *i;
		if(!currentBuilding->isAlive()) {
			continue;
		}
		if(constructors.first == currentBuilding->getType() && !currentBuilding->getUnit()->isBeingConstructed() && currentBuilding->getUnit()->isCompleted()) {

			if(currentBuilding->getUnit()->isTraining() == true || currentBuilding->isReservedForTraining() == true) {

				continue;
			} else {
				targetConstructor = currentBuilding;
				
				return true;
			}
		}
	}
	return false;
}

//************************************
// Method:    findPointOnChokePoint
// FullName:  TrainingTask::findPointOnChokePoint
// Access:    public 
// Returns:   BWAPI::Position
// Parameter: bool frontOrBack - FALSE is the front of the choke point, TRUE is the back of the choke point
// Comments:  none
//************************************
BWAPI::Position TrainingTask::findPointOnChokePoint( bool frontOrBack )
{
	BWTA::Chokepoint* cp = BWTA::getNearestChokepoint(overseer->getUnitManager()->getFriendlyMainBase()->getPosition());
	ParticleGenerator* p = new ParticleGenerator(128, 120, cp->getCenter());
	std::vector<Position> v = p->generate();
	int CP_MIN_DISTANCE = 64;

	BaseLocation* home = overseer->getUnitManager()->getFriendlyMainBase()->getBaseLOC();
	for(std::vector<Position>::const_iterator i = v.begin(); i != v.end(); i++) {
		Position cur = *i;
		if(MapData::walkability[cur.x()/8][cur.y()/8]) {
			if(cur.getDistance(cp->getCenter()) > CP_MIN_DISTANCE) {
				BaseLocation* bl = NULL;
				BaseModel* bm = overseer->getUnitManager()->getBaseModelInRegion(BWTA::getRegion(cur));
				if(bm != NULL) {
					bl = bm->getBaseLOC();
				}
				if(bl != NULL && bl == home) {
					if(frontOrBack == true) {
						return cur;
					}
				} else {
					if(frontOrBack == false) {
						return cur;
					}
				}
			}
		}

	}
	return overseer->getUnitManager()->getFriendlyMainBase()->getPosition();
}
