#include "ArcingDragoonMicroBehaviour.h"
#include "BaseModel.h"

ArcingDragoonMicroBehaviour::ArcingDragoonMicroBehaviour( UnitModel* sub , UnitManager* u )
{
	subject = sub;
	unitManager = u;
	attacking = false;
	moving = false;
	fallBackLocation = Positions::None;
	targettingSystem = new TargetEvaluator(sub);
	currentTarget = NULL;
	sftPtr = 0;
	closestEnemyPos = Positions::None;
}

//************************************
// Method:    execute
// FullName:  ArcingDragoonMicroBehaviour::execute
// Access:    virtual public 
// Returns:   void
// Comments:  none
//************************************
bool ArcingDragoonMicroBehaviour::execute()
{
	bool outcome = false;

	if(subject->isStasised()) {
		return true;
	}


	// general idea:
	// cowardly dragoon moves towards some map location
	// if it sees more than one enemy, or if an enemy gets too close, it will try to move back to the nearest base
	// if it sees one enemy, it will try to attack them.

	nearbyUnits = Broodwar->getUnitsInRadius(subject->getPosition(), subject->getType().sightRange()*1.4);

	fallBackLocation = unitManager->getFriendlyMainBase()->getPosition();

	if(fallBackLocation != Positions::None) {
		////Broodwar->drawEllipseMap(fallBackLocation.x(), fallBackLocation.y(), 32, 32, BWAPI::Colors::Green, true);
	}

		bool moveToDetector = false;
		Unit* detector = NULL;
		Unit* undetected = NULL;
		int numNonDetected = 0;
		int numDetected = 0;
		for (set<Unit*>::iterator i = nearbyUnits.begin(); i != nearbyUnits.end(); ++i)
		{
			BWAPI::Unit* curUnit = *i;
			if(undetected == NULL) {
			if(curUnit->getPlayer() != Broodwar->self()){
				if(!curUnit->isDetected()) {
					numNonDetected++;
					undetected = curUnit;
				} else {
					numDetected++;
				}
			}
			}
			if(detector == NULL) {
				if(curUnit->getPlayer() == Broodwar->self()) {
					if(curUnit->getType().isDetector()) {
						detector = curUnit;
					}
				}
			}
			if(detector != NULL && undetected != NULL) {
				moveToDetector = true;
				break;
			}
		}
		if(moveToDetector == true && numNonDetected > numDetected) {
			subject->getUnit()->move(detector->getPosition());
			return true;
		}

		if(Broodwar->getFrameCount()%2 == 0 && subject->isUnderThreat()) {
			
	
			Position curSafestPoint;
			std::vector<Position> snb = findSafestNearbyPoints();
			if(subject->getRegion() != unitManager->getClosestFriendlyBaseModel(subject->getPosition())->getBaseRegion()) {
				curSafestPoint = subject->findClosestPointTowardsBase(snb, BWAPI::Positions::None, -1);
			} else {
				curSafestPoint = subject->findClosestPoint(snb, BWAPI::Positions::None, -1);
			}
				if(curSafestPoint == Positions::None) {
					curSafestPoint = subject->getPosition();
				}
			
			safestPointSmoothing[sftPtr] = curSafestPoint;
			if(sftPtr >= 4) {
				sftPtr = 0;
			} else {
				sftPtr++;
			}

			int avgX = 0;
			int avgY = 0;
			int over = 0;
			for(int i = 0; i < 5; i++) {
				if(safestPointSmoothing[i] == Positions::None) {
					safestPointSmoothing[i] = subject->getPosition();
				}
					avgX += safestPointSmoothing[i].x();
					avgY += safestPointSmoothing[i].y();
					over++;
				
			}
			if(over != 0) {
				safestPoint = Position(avgX/over, avgY/over);
			} else {
				safestPoint = subject->getPosition();
			}
		}
		////Broodwar->drawCircleMap(safestPoint.x(), safestPoint.y(), 12, BWAPI::Colors::White, true);
	if(subject->getUnit()->getGroundWeaponCooldown() >= 2 && !subject->getUnit()->isStartingAttack() && !subject->getUnit()->isAttackFrame() && subject->getPosition().getDistance(fallBackLocation) > 256) {
		// move back to the closest force concentration on the map
		if(!subject->getUnit()->isMoving()) {
			////Broodwar->drawLineMap(subject->getPosition().x(), subject->getPosition().y(), fallBackLocation.x(), fallBackLocation.y(), Colors::Green);
			
			if(safestPoint != Positions::None) {
				subject->getUnit()->move(safestPoint);
			} else {
				subject->getUnit()->move(fallBackLocation);
			}
			return true;
		}
	} 

	//////////////
	// NEW CODE //
	//////////////

	if(subject->getUnit()->isUnderStorm() || subject->getUnit()->isUnderDisruptionWeb() || subject->getUnit()->isUnderDarkSwarm()) {
			subject->getUnit()->move(safestPoint);
			closestEnemyPos = subject->getPosition();
		return true;
	} else {
	for (set<Unit*>::iterator i = nearbyUnits.begin(); i != nearbyUnits.end(); ++i)
	{
		BWAPI::Unit* curUnit = *i;
			if(curUnit->getPlayer() != Broodwar->self()) {
				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->getTarget() == subject->getUnit() || curUnit->getOrderTarget() == subject->getUnit()) || (curUnit->getPosition().getDistance(subject->getPosition()) <= 64)) {
								subject->getUnit()->move(safestPoint);
								closestEnemyPos = curUnit->getPosition();
								return true;
					}			
				}
			}
		}
	}
}



	///END NEW CODE


	//////////////





	// so now lets filter for stuff we don't care about
	int enemyCautionMeasure = 0;
	int allyConfidenceMeasure = 1;

	if(subject->getArmyOwner() != NULL) {
		allyConfidenceMeasure+=5;
	}

		allyConfidenceMeasure += Broodwar->self()->getUpgradeLevel(UpgradeTypes::Protoss_Ground_Weapons);
		allyConfidenceMeasure += Broodwar->self()->getUpgradeLevel(UpgradeTypes::Protoss_Ground_Armor);
		allyConfidenceMeasure += ((Broodwar->self()->getUpgradeLevel(UpgradeTypes::Protoss_Plasma_Shields))/2);

		if(subject->getUnit()->isUnderStorm() || subject->getUnit()->isIrradiated() || subject->getUnit()->isPlagued()){
			enemyCautionMeasure+=50;
		}

		if(subject->getPosition().getDistance(unitManager->getFriendlyMainBase()->getPosition()) < 312) {
			allyConfidenceMeasure+=10;
		}


	Unit* closestTarget = NULL;
	int highestTargetValueSoFar = -100000;
	bool found = false;

	// define what this type of unit considers an ally
	std::set<UnitType> allies;
	allies.insert(UnitTypes::Protoss_Dragoon);
	allies.insert(UnitTypes::Protoss_Zealot);
	allies.insert(UnitTypes::Protoss_Photon_Cannon);
	allies.insert(UnitTypes::Protoss_Arbiter);
	allies.insert(UnitTypes::Protoss_Reaver);
	allies.insert(UnitTypes::Protoss_Dark_Templar);
	allies.insert(UnitTypes::Protoss_Archon);
	allies.insert(UnitTypes::Protoss_Dark_Archon);
	allies.insert(UnitTypes::Protoss_High_Templar);
	// we might as well define probes, since if the unit is near any probes (ie. in a base)
	// we most likely want it to fight to the death to defend them, rather than run off somewhere.
	allies.insert(UnitTypes::Protoss_Probe);
	allies.insert(UnitTypes::Protoss_Nexus);
	bool ignoreLesserTargets = false;
	for (set<Unit*>::iterator i = nearbyUnits.begin(); i != nearbyUnits.end(); ++i)
	{
		BWAPI::Unit* curUnit = *i;
		if(!curUnit->isDetected()) {
			continue;
		}
		if(allies.count(curUnit->getType()) != 0 && curUnit != subject->getUnit()) {
			if(!curUnit->isStasised()) {
			if(curUnit->getType() == UnitTypes::Protoss_Photon_Cannon) {
				if(subject->getPosition().getDistance(curUnit->getPosition()) <= curUnit->getType().groundWeapon().maxRange()*0.55) {
					allyConfidenceMeasure+=8;
				} 
	

			} else {
				if(curUnit->getType() == UnitTypes::Protoss_Pylon) {
					allyConfidenceMeasure+=4;
					if(subject->getCurrentTaskType() == GUARD && !subject->getUnit()->isAttacking() && !subject->getUnit()->isAttackFrame() && !subject->getUnit()->isStartingAttack()) {
						if(Broodwar->getFrameCount()%512 == 0 && rand()%100 >= 50) {
							if(subject->getPosition().getDistance(curUnit->getPosition()) > 64) {
								subject->getUnit()->move(curUnit->getPosition());
							}
						}
					}
				}
				if(curUnit->getType() == UnitTypes::Protoss_Nexus) {
					allyConfidenceMeasure+=12;

				}
				if(curUnit->getType() == UnitTypes::Protoss_Arbiter) {
					allyConfidenceMeasure+=2;
					if(subject->getUnit()->isCloaked()) {
						allyConfidenceMeasure+=15;
					}
				}
				allyConfidenceMeasure++;
			}
			}
		}
		if(curUnit->getType().isWorker() || curUnit->getType().isNeutral() || curUnit->getType().isSpecialBuilding() || curUnit->getType() == UnitTypes::Zerg_Larva || curUnit->getType() == UnitTypes::Zerg_Egg) {
		} else {
				if(curUnit->getPlayer() != Broodwar->self()){
				////Broodwar->drawLineMap(subject->getPosition().x(), subject->getPosition().y(), curUnit->getPosition().x(), curUnit->getPosition().y(), BWAPI::Colors::Red);
			
				
				if(curUnit->isStasised()) {
					allyConfidenceMeasure+=2;
				} else {
					enemyCautionMeasure++;
				}

				if(closestTarget == NULL) {
					closestTarget = curUnit;
				} else {
					if(curUnit->getType() == UnitTypes::Protoss_Photon_Cannon || curUnit->getType() == UnitTypes::Terran_Bunker || curUnit->getType() == UnitTypes::Zerg_Sunken_Colony || curUnit->getType() == UnitTypes::Zerg_Spore_Colony || curUnit->getType() == UnitTypes::Zerg_Creep_Colony) {
						if(subject->getPosition().getDistance(curUnit->getPosition()) <= subject->getType().sightRange()*1.1) {
							closestTarget = curUnit;
							ignoreLesserTargets = true;
						}
					} else {					
						if(!ignoreLesserTargets) {
						int targettingPenalty = unitManager->calculateTargettingPenalty(curUnit, subject);
						int targetValue = targettingSystem->calculateValueOf(curUnit, targettingPenalty);
						if(currentTarget != NULL) {
							if(curUnit == currentTarget->getUnit()) {
								targetValue+=500;
							}
						}
						
						if(targetValue > highestTargetValueSoFar) {
							highestTargetValueSoFar = targetValue;
							closestTarget = curUnit;
						}
					}
					}

				}
			}
		}
	}
	if(enemyCautionMeasure == 0) {
		currentTarget = NULL;
	}
	if(closestTarget != NULL) {
		////Broodwar->drawEllipseMap(closestTarget->getPosition().x(), closestTarget->getPosition().y(), 32, 32, BWAPI::Colors::Red, false);
	}
///	//Broodwar->drawTextMap(subject->getPosition().x(), subject->getPosition().y()-32, "%d : %d", enemyCautionMeasure, allyConfidenceMeasure);
	// so now we know whether to attack or retreat a bit
	if(enemyCautionMeasure <= allyConfidenceMeasure && closestTarget != NULL && subject->getUnit()->getGroundWeaponCooldown() <= 1) {
		////Broodwar->drawEllipseMap(subject->getPosition().x(), subject->getPosition().y()+16, 6, 6, BWAPI::Colors::Green, true);
				if(Broodwar->getFrameCount()%4 == 0) {
					if(subject->getUnit()->isMoving()) {
						subject->getUnit()->stop();
					}
					
						subject->getUnit()->attack(closestTarget, true);
					
					currentTarget = unitManager->getUnitModelFromMap(closestTarget);
					if(currentTarget != NULL) {
						unitManager->declareTargetToMap(subject, currentTarget->getUnit());
					}
					return true;
				}
				//}
			//}

		
	} else {
			if(enemyCautionMeasure > allyConfidenceMeasure) {
				////Broodwar->drawEllipseMap(subject->getPosition().x(), subject->getPosition().y()+16, 6, 6, BWAPI::Colors::Red, true);
				// move back to the closest force concentration on the map
				
				if(!subject->getUnit()->isStartingAttack() && !subject->getUnit()->isAttacking()) {
					//	//Broodwar->drawLineMap(subject->getPosition().x(), subject->getPosition().y(), fallBackLocation.x(), fallBackLocation.y(), Colors::Cyan);
						subject->getUnit()->move(fallBackLocation);
					return true;
				}
				


			} 
			
	}
	
	return false;
}

void ArcingDragoonMicroBehaviour::nudge()
{

}

std::vector<Position> ArcingDragoonMicroBehaviour::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(closestEnemyPos != Positions::None) {
			if(cur.getDistance(closestEnemyPos) < subject->getPosition().getDistance(closestEnemyPos)) {
				continue;
			}
		}
		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;

}