#include "GuardTask.h"
#include "Overseer.h"
#include "ParticleGenerator.h"

GuardTask::GuardTask( TilePosition t, Overseer* ov )
{
	Chokepoint* bestCP = NULL;
	if(t.isValid()) {

	} else {
		t = t.makeValid();
	}
	BWTA::Region* r = BWTA::getRegion(t);

	for(std::set<Chokepoint*>::const_iterator i = r->getChokepoints().begin(); i != r->getChokepoints().end(); ++i) {
		Chokepoint* cur = *i;
		if(bestCP == NULL) {
			bestCP = cur;
		} else {
			if(cur->getWidth() > bestCP->getWidth()) {
				bestCP = cur;
			}
		}
	}
	position = (TilePosition)r->getCenter();
	overseer = ov;
	type = UnitTypes::None;
	initialized = false;
	executing = false;
	finished = false;
	targetBase = NULL;
	TASK_TYPE = GUARD;
	guardPriority = 5;
	guardingUnit = false;
}

GuardTask::GuardTask( TilePosition t, Overseer* ov, int priority )
{
	Chokepoint* bestCP = NULL;
	if(t.isValid()) {

	} else {
		t = t.makeValid();
	}
	BWTA::Region* r = BWTA::getRegion(t);
	for(std::set<Chokepoint*>::const_iterator i = r->getChokepoints().begin(); i != r->getChokepoints().end(); ++i) {
		Chokepoint* cur = *i;
		if(bestCP == NULL) {
			bestCP = cur;
		} else {
			if(cur->getWidth() > bestCP->getWidth()) {
				bestCP = cur;
			}
		}
	}
	position = (TilePosition)r->getCenter();
	overseer = ov;
	type = UnitTypes::None;
	initialized = false;
	executing = false;
	finished = false;
	targetBase = NULL;
	TASK_TYPE = GUARD;
	guardPriority = priority;
	guardingUnit = false;
}

GuardTask::GuardTask( BaseModel* t, Overseer* ov, int priority )
{
	Chokepoint* bestCP = NULL;
	for(std::set<Chokepoint*>::const_iterator i = t->getSubject()->getRegion()->getChokepoints().begin(); i != t->getSubject()->getRegion()->getChokepoints().end(); ++i) {
		Chokepoint* cur = *i;
		if(bestCP == NULL) {
			bestCP = cur;
		} else {
			if(cur->getWidth() > bestCP->getWidth()) {
				bestCP = cur;
			}
		}
	}
	position = (TilePosition)t->getBaseRegion()->getCenter();
	targetBase = t;
	overseer = ov;
	type = UnitTypes::None;
	initialized = false;
	executing = false;
	finished = false;
	targetBase = NULL;
	TASK_TYPE = GUARD;
	guardPriority = priority;
	guardingUnit = false;
}

GuardTask::GuardTask( Position t, Overseer* ov )
{

	position = (TilePosition)t;
	targetBase = NULL;
	overseer = ov;
	type = UnitTypes::None;
	initialized = false;
	executing = false;
	finished = false;
	targetBase = NULL;
	TASK_TYPE = GUARD;
	guardPriority = 5;
	guardingUnit = false;
}

GuardTask::GuardTask( TilePosition t, Overseer* ov, int priority, bool holdChoke )
{
	Chokepoint* bestCP = NULL;
	if(t.isValid()) {

	} else {
		t = t.makeValid();
	}
	position = t;
	overseer = ov;
	type = UnitTypes::None;
	initialized = false;
	executing = false;
	finished = false;
	targetBase = NULL;
	TASK_TYPE = GUARD;
	guardPriority = priority;
	guardingUnit = false;
}



GuardTask::GuardTask( std::vector<TilePosition> t, Overseer* ov, int priority )
{
	Chokepoint* bestCP = NULL;
	targetPath = t;
	position = t.at(t.size()-1);
	overseer = ov;
	type = UnitTypes::None;
	initialized = false;
	executing = false;
	finished = false;
	targetBase = NULL;
	TASK_TYPE = GUARD;
	guardPriority = priority;
	guardingUnit = true;
}

GuardTask::GuardTask( Unit* u, Overseer* ov, int priority )
{
	Chokepoint* bestCP = NULL;

	position = TilePositions::None;
	overseer = ov;
	type = UnitTypes::None;
	initialized = false;
	executing = false;
	finished = false;
	targetBase = NULL;
	TASK_TYPE = GUARD;
	guardPriority = priority;
	guardingUnit = true;
	target = u;
}


bool GuardTask::canInitialize()
{

	std::vector<UnitModel*> units = overseer->getUnitManager()->getFriendlyMilitaryUnits();
	defenders.clear();
	for(std::vector<UnitModel*>::const_iterator i = units.begin(); i != units.end(); i++) {
		UnitModel* cur = *i;
		if(cur->getCurrentTaskType() == NO_TASK) {
			// probably want to also consider some other things too, like if it's attacking, being attacked, moving or whatever.
			defenders.push_back(cur);
		}
		if(cur->getCurrentTaskType() == GUARD) {
			bool jobDone = false;
			BaseModel* tar = overseer->getUnitManager()->getClosestFriendlyBaseModel(cur->getPosition());
			if(tar != NULL) {
				if(tar->isAlive() && !tar->isUnderThreat()) {
					jobDone = true;
				}
			}
			if((cur->getPosition().getDistance((Position)position) < 768) || jobDone) {
				if(!cur->isBeingTargetted() && !cur->getUnit()->isMoving()) {
					defenders.push_back(cur);
				}
			}

		}
	}
	if(!defenders.empty()) {
		initialized = true;
	}

	// ok... now based on the priority of this task, we want to kind of delete a good few of these from the set
	// a priority of 0 should mean that we delete no one and just take everything we have to go defend this base
	// a priority of 5 should mean we only take 20% of those we found able to defend

	// so, firstly, how many units is 20% going to be?
	float numToKeep = 1.0;
	switch(guardPriority) {
	case -1:
		numToKeep = 1;
		break;
	case 0:
		numToKeep = 0.9;
		break;
	case 1:
		numToKeep = 0.8;
		break;
	case 2:
		numToKeep = 0.7;
		break;
	case 3:
		numToKeep = 0.6;
		break;
	case 4:
		numToKeep = 0.3;
		break;
	case 5:
		numToKeep = 0.1;
		break;
	}
	
	int numToDelete = (defenders.size()*numToKeep);


	if(guardPriority > 0) {
		for(std::vector<UnitModel*>::const_iterator i = defenders.begin(); i != defenders.end(); i++) {
			UnitModel* cur = *i;
			if(numToDelete > 0){
				i = defenders.erase(i);
				if(i == defenders.end() || defenders.empty()) {
					break;
				}
				numToDelete--;
			}
		}
	}
	if(defenders.empty()){
		return false;
	}


	return initialized;
}

bool GuardTask::execute()
{
	if(guardPriority < 0) {
		guardPriority = 0;
	}
	if(guardPriority > 5) {
		guardPriority = 5;
	}


	for(std::vector<UnitModel*>::const_iterator i = defenders.begin(); i != defenders.end(); i++) {
		UnitModel* cur = *i;

		cur->setCurrentTaskType(GUARD, BWTA::getRegion(position));

		if(targetPath.empty()) {

		if(!guardingUnit) {
			ParticleGenerator* p = new ParticleGenerator(16, 64, (Position)position);
			Position g = cur->findClosestPoint(p->generate(), BWAPI::Positions::None, -1);
			cur->getUnit()->move((Position)position);
		} else {
			cur->getUnit()->follow(target);
		}
			
		} else {
			cur->pathMove(targetPath);
		}
	}
	executing = true;
	return executing;
}

void GuardTask::monitor()
{

	// idea - move units AROUND the point to be defended!
	if(executing) {
		for(std::vector<UnitModel*>::const_iterator i = defenders.begin(); i != defenders.end(); i++) {
			UnitModel* cur = *i;
			if(!cur->isAlive() || cur->getUnit()->isUnderAttack() || cur->getUnit()->isAttacking() || cur->getCurrentTaskType() != GUARD) {
				continue;
			}
			if(targetPath.empty()) {
			if(!guardingUnit) {
				if(cur->getPosition().getDistance((Position)position) <= cur->getType().sightRange()*0.5) {
					if(cur->getUnit()->isMoving()) {
						cur->getUnit()->stop();
					}
				} else {
					if(!cur->getUnit()->isMoving()) {
						ParticleGenerator* p = new ParticleGenerator(16, 64, (Position)position);
						//cur->pathMove((TilePosition)p->generate().at(0), PATHFINDING_THREATAWARE_ASTAR_GROUND);
						cur->getUnit()->attack(p->generate().at(0));
						return;
					}

				}
			} else {
				cur->getUnit()->follow(target);
			}
			}
		}
		executing = false;
		finished = true;
	}
}

bool GuardTask::isExecuting()
{
	return executing;
}

bool GuardTask::isFinished()
{
	return finished;
}

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

BWAPI::UnitType GuardTask::getBuildType()
{
	return type;
}

void GuardTask::setTargetBase( BaseModel* b )
{
	if(targetBase == NULL) {
		targetBase = b;
	}
}
