#include "ZealotGeneralMicroBehaviour.h"
#include "BaseModel.h"

ZealotGeneralMicroBehaviour::ZealotGeneralMicroBehaviour( UnitModel* sub , UnitManager* u )
{
	subject = sub;
	unitManager = u;
	attacking = false;
	moving = false;
	targettingSystem = new TargetEvaluator(sub);
	currentTarget = NULL;
	id = PROTOSS_GENERAL_ZEALOT;
}

//************************************
// Method:    execute
// FullName:  ZealotGeneralMicroBehaviour::execute
// Access:    virtual public 
// Returns:   void
// Comments:  none
//************************************
bool ZealotGeneralMicroBehaviour::execute()
{
	//Broodwar->drawEllipseMap(subject->getPosition().x(), subject->getPosition().y(), 32, 32, BWAPI::Colors::Green, false);
	std::vector<UnitModel*> units = unitManager->getAllEnemyUnitsInRange(subject->getPosition(), UnitTypes::Protoss_Zealot.sightRange()*1.2);
	if(currentTarget != NULL) {
		//Broodwar->drawLineMap(subject->getPosition().x(), subject->getPosition().y(), currentTarget->getPosition().x(), currentTarget->getPosition().y(), BWAPI::Colors::Red);
	}

	Position fallBackLocation = unitManager->getClosestFriendlyBaseModel(subject->getPosition())->getPosition();

	Position safestPoint = subject->findClosestPoint(findSafestNearbyPoints(), BWAPI::Positions::None, -1);
	
	if(subject->getUnit()->isUnderStorm() || subject->getUnit()->isUnderDisruptionWeb() || subject->getUnit()->isUnderDarkSwarm()) {

		if(subject->getUnit()->isUnderStorm()) {
			subject->getUnit()->move(fallBackLocation);
		} else {
			subject->getUnit()->move(safestPoint);
			closestEnemyPos = subject->getPosition();
		}
		return true;
	} else {
		if(subject->getQualitativeShields() < 15 && subject->getQualitativeHP() < 50) {
		for (vector<UnitModel*>::iterator i = units.begin(); i != units.end(); ++i)
		{
			UnitModel* curUnit = *i;
			if(!curUnit->isOwnedByPlayer()) {
				if(curUnit->getType() == UnitTypes::Protoss_Dark_Templar || curUnit->getType() == UnitTypes::Protoss_Zealot || curUnit->getType() == UnitTypes::Terran_Firebat || curUnit->getType() == UnitTypes::Protoss_Archon || curUnit->getType() == UnitTypes::Protoss_Dark_Archon || curUnit->getType() == UnitTypes::Zerg_Ultralisk) {
					if(curUnit->getPosition().getDistance(subject->getPosition()) < 164) {
						if((curUnit->getUnit()->getTarget() == subject->getUnit() || curUnit->getUnit()->getOrderTarget() == subject->getUnit()) || (curUnit->getPosition().getDistance(subject->getPosition()) <= 64)) {
							subject->getUnit()->move(safestPoint);
							closestEnemyPos = curUnit->getPosition();
							return true;
						}			
					}
				}
			}
		}
		}
	}




	if(subject->getArmyOwner() != NULL) {
		if(subject->getArmyOwner()->isArmyUnderAttack()) {
		if(subject->getArmyOwner()->getCentrePoint().getDistance(subject->getPosition()) > 350) {
			subject->getUnit()->move(subject->getArmyOwner()->getCentrePoint());
			return true;
		}
		}
	}



	bool usingGlobalTargetMap = false;
	if(units.empty()) {
		currentTarget = NULL;
		units = unitManager->getEnemyBuildingsInRegionOf(subject->getTilePosition());
		usingGlobalTargetMap = true;
		if(units.empty()) {
			return false;
		}
	}


	int bestScoreSoFar = 0;
	UnitModel* bestTarget = NULL;
	int bestTarThresh = 0;
	bool hasTargetsInRange = false;
	for (vector<UnitModel*>::iterator i = units.begin(); i != units.end(); ++i)
	{
		UnitModel* cur = *i;
		if(cur->getType().isFlyer() || cur->getUnit()->isLifted()) {
			continue;
		}
		if(!cur->getUnit()->isVisible() && !usingGlobalTargetMap) { 
			continue;
		}
		if(subject->getUnit()->isInWeaponRange(cur->getUnit()) || subject->getPosition().getDistance(cur->getPosition()) < 64) {
			hasTargetsInRange = true;
		} else {

		}
		if(subject->getArmyOwner() != NULL){
			if(subject->getArmyOwner()->isAlive()) {
				if(cur->getPosition().getDistance(subject->getArmyOwner()->getCentrePoint()) > 256) {
					continue;
				}
			}
		}

		//	//Broodwar->drawLineMap(subject->getPosition().x(), subject->getPosition().y(), cur->getPosition().x(), cur->getPosition().y(), BWAPI::Colors::White);

		if(bestTarget == NULL) {
			bestTarget = cur;
		} else {
			int targettingPenalty = 0;
			if(cur->getUnit()->isVisible()) {
				targettingPenalty = unitManager->calculateTargettingPenalty(cur->getUnit(), subject);
			}
			int candidateValue = targettingSystem->calculateValueOf(cur, targettingPenalty);
			std::vector<UnitModel*> enemyAllies = unitManager->getAllEnemyUnitsInRange(cur->getPosition(), 96);

			int dangerThresh = 0;

			for (vector<UnitModel*>::iterator it = enemyAllies.begin(); it != enemyAllies.end(); ++it)
			{
				UnitModel* cA = *it;
				if(cA->getType().isBuilding() && (cA->getType() != UnitTypes::Zerg_Spore_Colony && cA->getType() != UnitTypes::Zerg_Sunken_Colony && cA->getType() != UnitTypes::Protoss_Photon_Cannon && cA->getType() != UnitTypes::Terran_Bunker)) {
					continue;
				}
				if(cA->getType() == UnitTypes::Terran_Marine) {
					dangerThresh++;
				}
				if(cA->getType() == UnitTypes::Terran_Vulture) {
					dangerThresh++;
				}
				if(cA->getType() == UnitTypes::Zerg_Zergling) {
					dangerThresh++;
				}
				if(cA->getType() == UnitTypes::Zerg_Hydralisk) {
					dangerThresh++;
				}
				if(cA->getType() == UnitTypes::Protoss_Dragoon) {
					dangerThresh++;
				}
				if(cA->getType() == UnitTypes::Terran_Bunker) {
					dangerThresh+=4;
				}

			}

			if(dangerThresh > 4) {
				candidateValue -= 1000;
			}

			if(currentTarget != NULL) {
				if(cur == currentTarget && subject->getUnit()->isInWeaponRange(cur->getUnit())) {
					candidateValue+=1000;
				}
			
			}
			if(!subject->getUnit()->isInWeaponRange(cur->getUnit()) && hasTargetsInRange) {
				continue;
			}
			if(cur->getType().isBuilding() && (cur->getType() != UnitTypes::Zerg_Spore_Colony && cur->getType() != UnitTypes::Zerg_Sunken_Colony && cur->getType() != UnitTypes::Protoss_Photon_Cannon && cur->getType() != UnitTypes::Terran_Bunker)) {
				candidateValue -= 1000;
				if(subject->getUnit()->isUnderAttack()) {
					candidateValue -= 2000;
				}
			}
			
			if(candidateValue > bestScoreSoFar) {
				bestScoreSoFar = candidateValue;
				bestTarget = cur;
				bestTarThresh = dangerThresh;
			}
		}
	}


	std::vector<UnitModel*> allies = unitManager->getAllFriendlyUnitsInRange(subject->getPosition(), UnitTypes::Protoss_Zealot.sightRange()*1.2);
	int avgX = 0;
	int avgY = 0;
	int livingMembers = 0;
	for (std::vector<UnitModel*>::iterator i = allies.begin(); i != allies.end(); ++i)
	{
		UnitModel* ncur = *i;
		if(!ncur->isAlive() || ncur == subject) {
			continue;
		}
		avgX = avgX+ncur->getPosition().x();
		avgY = avgY+ncur->getPosition().y();
		livingMembers++;
	}

	if(livingMembers != 0 && bestTarget != NULL) {
		avgX = avgX/livingMembers;
		avgY = avgY/livingMembers;
		Position avgPos = Position(avgX, avgY);
		if(bestTarget->getPosition().getDistance(avgPos) > UnitTypes::Protoss_Dragoon.sightRange() && bestTarThresh >= 2) {
			bestTarget = NULL;
			subject->getUnit()->move((Position)Broodwar->self()->getStartLocation());
			return false;
		}
	}

	currentTarget = bestTarget;
	if(subject->getUnit()->isMoving() && subject->getUnit()->getTarget() != NULL) {
		return true;
	}
	if(currentTarget != NULL) {
		if(subject->getUnit()->getGroundWeaponCooldown() <= 1) {
			subject->getUnit()->attack(currentTarget->getUnit());
			unitManager->declareTargetToMap(subject, currentTarget->getUnit());
			return true;
		}

	}



	return false;
}

void ZealotGeneralMicroBehaviour::nudge()
{

}

std::vector<Position> ZealotGeneralMicroBehaviour::findSafestNearbyPoints()
{
	ParticleGenerator* p = new ParticleGenerator(128, 256, subject->getPosition());
	std::vector<Position> v = p->generate();
	std::vector<Position> filteredPoints;
	std::vector<UnitModel*> enemies = unitManager->getAllEnemyUnitsInRange(subject->getPosition(), 256);

	for(std::vector<Position>::const_iterator i = v.begin(); i != v.end(); i++) {
		Position cur = *i;
		if(cur.getDistance(subject->getPosition()) < 128 || !Broodwar->isWalkable(cur.x()/8, cur.y()/8) || !subject->getUnit()->hasPath(cur)) {
			continue;
		}
		if(!Broodwar->isBuildable((TilePosition)cur)) {
			continue;
		}

		bool dangerousPoint = false;
		for(std::vector<UnitModel*>::const_iterator it = enemies.begin(); it != enemies.end(); it++) {
			UnitModel* curEnemy = *it;
			if(cur.getDistance(curEnemy->getPosition()) <= 168) {
				dangerousPoint = true;
				break;
			}

		}
		if(!dangerousPoint) {
			filteredPoints.push_back(cur);
		}

	}

	return filteredPoints;

}