#include "ArbiterGeneralMicroBehaviour.h"
#include "Army.h"

ArbiterGeneralMicroBehaviour::ArbiterGeneralMicroBehaviour( UnitModel* sub , UnitManager* u )
{
	subject = sub;
	unitManager = u;
	attacking = false;
	moving = false;
	armyEye = Positions::None;
	targettingSystem = new TargetEvaluator(sub);
	currentTarget = NULL;
	targetArmy = NULL;
	just_used_stasis = false;
	MIN_STASIS_NEIGHBOURS = 3;
	STASIS_CONFIDENCE = 0;
	STASIS_CONFIDENCE_THRESHOLD = 2;
	STASIS_COOLDOWN = 0;
	STASIS_INSPECTION_COOLDOWN = 0;
	id = PROTOSS_GENERAL_ARBITER;
}

bool ArbiterGeneralMicroBehaviour::execute()
{
	// stasis using code here
	if(!lastStasisVictims.empty()) {
		for (std::set<UnitModel*>::iterator i = lastStasisVictims.begin(); i != lastStasisVictims.end(); ++i)
		{
			UnitModel* cur = *i;
			//Broodwar->drawCircleMap(cur->getPosition().x(), cur->getPosition().y(), 16, Colors::Blue, true);
			if(!cur->isAlive()) {
				i = lastStasisVictims.erase(i);
				if(lastStasisVictims.empty() || i == lastStasisVictims.end()) {
					break;
				}
			}
		}


	}
	std::vector<UnitModel*> enemiesInRange = unitManager->getAllEnemyUnitsInRange(subject->getPosition(), 512);
	for (std::vector<UnitModel*>::iterator i = enemiesInRange.begin(); i != enemiesInRange.end(); ++i)
	{
		UnitModel* cur = *i;
		if(cur->isStasised()) {
			if((cur->getType() == UnitTypes::Terran_Vulture || cur->getType() == UnitTypes::Protoss_Dragoon || cur->getType() == UnitTypes::Protoss_Zealot || cur->getType() == UnitTypes::Terran_Marine || cur->getType() == UnitTypes::Terran_Medic || cur->getType() == UnitTypes::Zerg_Zergling || cur->getType() == UnitTypes::Zerg_Hydralisk)) {
				lastStasisVictims.insert(cur);
			}
		}
	}
	if(subject->getUnit()->getEnergy() >= 100) {
		if(subject->getUnit()->getEnergy() >= 105) {
			STASIS_CONFIDENCE_THRESHOLD = 2;
		} else {
			STASIS_CONFIDENCE_THRESHOLD = 3;
		}
		getBestStasisTarget();
		if(currentTarget != NULL && STASIS_COOLDOWN == 0) {
			if(STASIS_CONFIDENCE >= STASIS_CONFIDENCE_THRESHOLD && subject->getUnit()->getOrder() != Orders::CastStasisField) {
				preEnergy = subject->getUnit()->getEnergy();
				subject->getUnit()->useTech(TechTypes::Stasis_Field, currentTarget->getUnit());
				//subject->pathMove((TilePosition)currentTarget->getPosition(), PATHFINDING_THREATAWARE_ASTAR_AIR_FINER_GRANULARITY);
				//////Broodwar->sendText("trying to cast stasis");
				STASIS_CONFIDENCE = 0;
				moving = true; 
				return true;
			}
		}
	}
	if(STASIS_COOLDOWN > 0) {
		STASIS_COOLDOWN--;
	}
	//if(subject->getUnit()->getOrder() == Orders::CastStasisField) {
	//	return true;
	//}
	if(moving) {
		if(subject->getUnit()->getEnergy() < preEnergy) {
			moving = false;
			STASIS_COOLDOWN = 256;
			subject->pathStop();
		} else {
			if(currentTarget != NULL) {
			if(currentTarget->isAlive()) {
				//if(subject->getUnit()->useTech(TechTypes::Stasis_Field, currentTarget->getPosition())) {
				//if(subject->getPosition().getDistance(currentTarget->getPosition()) < UnitTypes::Protoss_Arbiter.sightRange()) {
					if(subject->getUnit()->useTech(TechTypes::Stasis_Field, currentTarget->getUnit())) {
						//////Broodwar->sendText("stasis launched!");
					}
				//}
				//}
				return true;
			} else {
				currentTarget = NULL;
				moving = false;
			}
			}
		}
	}



	if(targetArmy == NULL || subject->isFollowingPath()) {
		return false;
	}
	// go through the target army
	// try to find the point where you have to stand in order to maximize the number of cloaked units
	// prefer to cloak dragoons over zealots
	int avgX = 0;
	int avgY = 0;
	int livingMembers = 0;
	std::vector<UnitModel*> forces = targetArmy->getMembers();
	for (std::vector<UnitModel*>::iterator i = forces.begin(); i != forces.end(); ++i)
	{
		UnitModel* cur = *i;
		if(!cur->isAlive()) {
			continue;
		}
		//if(cur->getType() != UnitTypes::Protoss_Dragoon && cur->getType() != UnitTypes::Protoss_Reaver) {
		//	continue;
		//}
		avgX = avgX+cur->getPosition().x();
		avgY = avgY+cur->getPosition().y();
		livingMembers++;
	}

	Position avgPos = Positions::None;
	if(livingMembers == 0) {
		//////Broodwar->sendText("dead army!");
		return false;
	} else {
		avgX = avgX/livingMembers;
		avgY = avgY/livingMembers;
		avgPos = Position(avgX, avgY);
	}
	// generate a cloud around the average point of all the thing you want to cloak
	// then score each position based on your interests - ie. enemy units in weapons range of that point
	// then change that over time, so prefer safer points as you lose shields and health

	ParticleGenerator* p = new ParticleGenerator(32, 256, avgPos);
	std::vector<Position> v = p->generate();
	Position bestPointSoFar = Positions::None;
	int bestScoreSoFar = -1000;
	for(std::vector<Position>::const_iterator i = v.begin(); i != v.end(); i++) {
		Position cur = *i;

		int currentScore = 0;
		if(bestPointSoFar == Positions::None) {
			bestPointSoFar = cur;
		} else {
			// score point 1: the number of enemy units in weapons range of this point
			// ie. it's safety
			std::vector<UnitModel*> enemiesInRange = unitManager->getAllEnemyUnitsInRange(cur, UnitTypes::Protoss_Arbiter.sightRange());
			for(std::vector<UnitModel*>::const_iterator it = enemiesInRange.begin(); it != enemiesInRange.end(); it++) {
				UnitModel* ncur = *it;
				if(ncur->getType().isWorker() || ncur->getType() == UnitTypes::Terran_Firebat || ncur->getType() == UnitTypes::Protoss_Zealot || ncur->getType() == UnitTypes::Protoss_Reaver || ncur->getType() == UnitTypes::Zerg_Zergling|| ncur->getType() == UnitTypes::Zerg_Ultralisk || ncur->getType() == UnitTypes::Zerg_Overlord || ncur->getType() == UnitTypes::Zerg_Queen || ncur->getType() == UnitTypes::Protoss_Shuttle || ncur->getType() == UnitTypes::Terran_Medic || ncur->getType() == UnitTypes::Terran_Siege_Tank_Siege_Mode || ncur->getType() == UnitTypes::Terran_Siege_Tank_Tank_Mode) {
					continue;
				}

					if(ncur->getType() == UnitTypes::Terran_Missile_Turret || ncur->getType() == UnitTypes::Zerg_Spore_Colony || ncur->getType() == UnitTypes::Protoss_Photon_Cannon) {
						if(ncur->getPosition().getDistance(cur) <= ncur->getType().sightRange()*1.3) {
						currentScore -= (500+(((500-subject->getHP()+subject->getShields()))+((200-cur.getDistance(ncur->getPosition())))));
						}
					} else {
						if(ncur->getType() == UnitTypes::Terran_Goliath || ncur->getType() == UnitTypes::Terran_Valkyrie || ncur->getType() == UnitTypes::Protoss_Scout || ncur->getType() == UnitTypes::Protoss_Corsair || ncur->getType() == UnitTypes::Terran_Wraith || ncur->getType() == UnitTypes::Terran_Battlecruiser || ncur->getType() == UnitTypes::Zerg_Hydralisk || ncur->getType() == UnitTypes::Zerg_Scourge) {
							currentScore -= (10*(100-subject->getQualitativeHP()));
						} else {
							currentScore -= (5*(100-subject->getQualitativeHP()));
						}
					}
			
					
			}

			// score point 2, the number of friendly units in cloak range of this point
			// ie. it's utility
			std::vector<UnitModel*> friendliesInRange = unitManager->getFriendlyMilitaryUnitsInRange(cur, UnitTypes::Protoss_Arbiter.sightRange()*0.8);
			for(std::vector<UnitModel*>::const_iterator it = friendliesInRange.begin(); it != friendliesInRange.end(); it++) {
				UnitModel* ncur = *it;
				if(ncur == subject) {
					continue;
				}
				if(ncur->isStasised()) {
					currentScore -= 1000;
				} else {
					currentScore += 100;
				}
				if(ncur->getUnit()->isAttacking()) {
					currentScore+=500;
				} else {
					currentScore+=250;
				}
				if(ncur->getType() == UnitTypes::Protoss_Arbiter) {
					currentScore -= 125;
				}
			}

			// score point 4 how close is this point to us? mainly used to stop jittering and smooth out the movement
			currentScore += (296-subject->getPosition().getDistance(cur));

			if(currentScore > bestScoreSoFar) {
				bestPointSoFar = cur;
				bestScoreSoFar = currentScore;
			}
		}
	}
	if(bestPointSoFar != Positions::None) {
		if(Broodwar->getFrameCount()%4 == 0) {
			subject->getUnit()->move(bestPointSoFar);
		}
	}






	/*

	if(livingMembers == 0) {
	//////Broodwar->sendText("dead army!");
	return false;
	} else {
	avgX = avgX/livingMembers;
	avgY = avgY/livingMembers;
	Position avgPos = Position(avgX, avgY);
	subject->getUnit()->move(avgPos);
	return true;
	}
	*/
	return false;
}

void ArbiterGeneralMicroBehaviour::nudge()
{

}

void ArbiterGeneralMicroBehaviour::setTargetArmy( Army* a )
{
	targetArmy = a;
}

void ArbiterGeneralMicroBehaviour::clearTargetArmy()
{
	targetArmy = NULL;
}

void ArbiterGeneralMicroBehaviour::getBestStasisTarget()
{
	UnitModel* bestTarget = NULL;
	if(currentTarget != NULL) {
		bestTarget = currentTarget;
	}
	int bestNeighbourCount = 0;
	if(Broodwar->getFrameCount()%3 == 0) {
		std::vector<UnitModel*> enemiesInRange = unitManager->getAllEnemyUnitsInRange(subject->getPosition(), UnitTypes::Protoss_Arbiter.sightRange()*1.5);
		//std::vector<UnitModel*> friendliesInRange = unitManager->getAllFriendlyUnitsInRadius(subject->getPosition(), UnitTypes::Protoss_Arbiter.sightRange()*1.5);

		if(enemiesInRange.empty() || enemiesInRange.size() < 5) {
			currentTarget = NULL;
			return;
		}
		int bestNeighboursSoFar = 0;
		for(std::vector<UnitModel*>::const_iterator it = enemiesInRange.begin(); it != enemiesInRange.end(); it++) {
			UnitModel* ncur = *it;
			//Broodwar->drawLineMap(ncur->getPosition().x(), ncur->getPosition().y(), subject->getPosition().x(), subject->getPosition().y(), Colors::Green);
			if(!ncur->getUnit()->isVisible() || ncur->isStasised() || ncur->getType().isBuilding() || ncur->getType() == UnitTypes::Terran_Vulture_Spider_Mine) {
				continue;
			}

			// or was stasised, and is something we just want to kill, rather than keep locked down
			if(!lastStasisVictims.empty() && (ncur->getType() == UnitTypes::Terran_Vulture || ncur->getType() == UnitTypes::Protoss_Dragoon || ncur->getType() == UnitTypes::Protoss_Zealot || ncur->getType() == UnitTypes::Terran_Marine || ncur->getType() == UnitTypes::Terran_Medic || ncur->getType() == UnitTypes::Zerg_Zergling || ncur->getType() == UnitTypes::Zerg_Hydralisk)) {
				if((lastStasisVictims.count(ncur)) != 0) {
					continue;
				}
			}


			std::vector<UnitModel*> neighbours = unitManager->getAllEnemyUnitsInRange(ncur->getPosition(), 96);
			std::vector<UnitModel*> collatoral = unitManager->getAllFriendlyUnitsInRange(ncur->getPosition(), 96);
			if(collatoral.size() > neighbours.size()) {
				currentTarget = NULL;
				return;
			}
			int neighbourScore = 0;
			int neighbourCount = 0;
			for(std::vector<UnitModel*>::const_iterator itt = neighbours.begin(); itt != neighbours.end(); itt++) {
				UnitModel* cur = *itt;
				if(cur->isStasised() || !cur->isAlive() || cur->getType().isBuilding() || ncur->getType() == UnitTypes::Terran_Vulture_Spider_Mine) {
					itt = neighbours.erase(itt);
					if(neighbours.empty() || itt == neighbours.end()) {
						break;
					}
				}
				
					if(!lastStasisVictims.empty() && (ncur->getType() == UnitTypes::Terran_Vulture || ncur->getType() == UnitTypes::Protoss_Dragoon || ncur->getType() == UnitTypes::Protoss_Zealot || ncur->getType() == UnitTypes::Terran_Marine || ncur->getType() == UnitTypes::Terran_Medic || ncur->getType() == UnitTypes::Zerg_Zergling || ncur->getType() == UnitTypes::Zerg_Hydralisk)) {
					if((lastStasisVictims.count(ncur)) != 0) {
						itt = neighbours.erase(itt);
						if(neighbours.empty() || itt == neighbours.end()) {
							break;
						}
					}
				}
				neighbourCount++;
				if(cur->getType() == UnitTypes::Terran_Siege_Tank_Siege_Mode) {
					neighbourScore+=550;
					neighbourCount+=5; // ;)
				}
				if(cur->getType() == UnitTypes::Terran_Siege_Tank_Tank_Mode) {
					neighbourScore+=250;
					neighbourCount+=3; // ;)
				}
				if(cur->getType() == UnitTypes::Terran_Medic) {
					neighbourScore+=55;
					neighbourCount+=1;
				}
				if(cur->getType() == UnitTypes::Terran_Marine) {
					neighbourScore+=25;
					neighbourCount+=1;
				}
				if(cur->getType() == UnitTypes::Protoss_Dragoon) {
					neighbourScore+=50;
					neighbourCount+=1; // ;)
				}
				if(cur->getType() == UnitTypes::Protoss_Zealot) {
					neighbourScore+=25;
									neighbourCount+=1;
				}
				if(cur->getType() == UnitTypes::Terran_Science_Vessel) {
					neighbourScore+=250;
					neighbourCount+=1; // ;)
				}
				if(cur->getType() == UnitTypes::Terran_Battlecruiser) {
					neighbourScore+=250;
					neighbourCount+=1; // ;)
				}
				if(cur->getType() == UnitTypes::Protoss_High_Templar) {
					neighbourScore+=200;
					neighbourCount+=5; // ;)
				}
				if(cur->getType() == UnitTypes::Protoss_Reaver) {
					neighbourScore+=100;
					neighbourCount+=5; // ;)
				}
				if(cur->getType() == UnitTypes::Zerg_Mutalisk) {
					neighbourScore+=125;
				}
				neighbourScore +=100+(cur->getHP()+cur->getShields());
				neighbourScore += (10*(cur->getType().groundWeapon().damageAmount()));
			}
			if(neighbours.empty()) {
				neighbourScore-=1000; // better idea?
			}
			if(neighbourCount > bestNeighbourCount) {
				bestNeighbourCount = neighbourCount;
			}
			neighbourScore = neighbourScore - (collatoral.size()*100); 

			if(bestTarget == NULL) {
				bestTarget = ncur;
				bestNeighboursSoFar = neighbourScore;
			} else {
				if(neighbourScore > bestNeighboursSoFar) {
					bestNeighboursSoFar = neighbourScore;
					bestTarget = ncur;
				}
			}
		}
	}
	if(bestTarget != NULL) {
		//Broodwar->drawEllipseMap(bestTarget->getPosition().x(), bestTarget->getPosition().y(), 32, 32, BWAPI::Colors::Red, false);
		if(currentTarget != NULL) {
			bool modifyConfidence = false;
			//	if(bestTarget != currentTarget) {
			if(currentTarget->getPosition().getDistance(bestTarget->getPosition()) <= 96) {
				STASIS_CONFIDENCE++;
			} else {
				if(STASIS_CONFIDENCE <= 0 ) {

				} else {
					STASIS_CONFIDENCE--;
				}
			}
		}
		if(bestNeighbourCount < 3) {
			currentTarget = NULL;
		} else {
			currentTarget = bestTarget;
		}
	}

}
