#include "NudgeModule.h"
#include "UnitModel.h"


NudgeModule::NudgeModule( UnitModel* sub , UnitManager* u )
{
	movingToNudgeTarget = false;
	movingToPreNudgeTarget = false;
	nudgeTarget = Positions::None;
	preNudgePos = Positions::None;

	bubbleClearance = 32;
	if(sub->getType().size() == UnitSizeTypes::Large) {
		bubbleClearance += 32;
	}
	if(sub->getType().size() == UnitSizeTypes::Medium) {
		bubbleClearance += 16;
	}
	if(sub->getType().size() == UnitSizeTypes::Small) {
		bubbleClearance += 8;
	}
	subject = sub;
	unitManager = u;
	ableToNudge = true;
}

bool NudgeModule::execute()
{
	
	if(nudgeTarget != Positions::None) {
		if(subject->getPosition().getDistance(nudgeTarget) < 5) {
			movingToNudgeTarget = false;
			movingToPreNudgeTarget = true;
			preNudgePos = bestPointClosestTo(preNudgePos, bubbleClearance);
			subject->getUnit()->move(preNudgePos);
			return true;
		}
	}
	if(preNudgePos != Positions::None) {
		if(subject->getPosition().getDistance(preNudgePos) < 5) {
			movingToPreNudgeTarget = false;
		} 
	}
	if(subject->isFollowingPath()) {

	} else {
		if(!ableToNudge) {
			return true;
		}
	}
	/*
		I want to make sure my bubble is free of other units, firstly!
	*/
	std::vector<UnitModel*> acceptedSet;
		std::vector<UnitModel*> nearbyUnits = unitManager->getAllFriendlyUnitsInRange(subject->getPosition(), bubbleClearance);
		// having to do this is stupid
		for (std::vector<UnitModel*>::iterator i = nearbyUnits.begin(); i != nearbyUnits.end(); ++i) {
			UnitModel* cur = *i;
			if(cur == subject || cur->getType().isBuilding() || cur->getType() == UnitTypes::Protoss_Reaver || cur->getType().isFlyer()) {
				
			} else {
				acceptedSet.push_back(cur);
			}
		}
		
		// so here we already know what units are infringing on our bubble


		if(acceptedSet.empty()) {
			//Broodwar->drawEllipseMap(subject->getUnit()->getPosition().x(), subject->getUnit()->getPosition().y(), bubbleClearance, bubbleClearance, Colors::White, false);
			movingToPreNudgeTarget = false;
			movingToNudgeTarget = false;
		} else {
			//Broodwar->drawEllipseMap(subject->getUnit()->getPosition().x(), subject->getUnit()->getPosition().y(), bubbleClearance, bubbleClearance, Colors::Red, false);
			for (vector<UnitModel*>::iterator i = acceptedSet.begin(); i != acceptedSet.end(); ++i) {
				UnitModel* cur = *i;
					if(subject->getUnit()->isMoving()) {
					if(cur->isOwnedByPlayer()) {
						
						if(cur->getNudgeModule() != NULL && cur->getArmyOwner() == NULL && !cur->isFollowingPath()) {
							BWTA::Chokepoint* cpa = BWTA::getNearestChokepoint(subject->getPosition());
							BWTA::Chokepoint* cpb = BWTA::getNearestChokepoint(cur->getPosition());
							if(cpa == cpb) {
								if(cpa->getCenter().getDistance(cur->getPosition()) <= 128) {
									if(cpa->getCenter().getDistance(subject->getPosition()) <= 128) {
										cur->getNudgeModule()->permnudge();
										continue;
									}
								}
							}
							cur->getNudgeModule()->nudge();
						}						
					}			
				}
			}	
		}
	return false;
}

void NudgeModule::nudge()
{
	if(movingToPreNudgeTarget || movingToNudgeTarget || subject->getUnit()->isMoving() || subject->getUnit()->isAttacking()) {
		return;
	}

	Position bestPoint = bestPointClosestTo(subject->getPosition(), bubbleClearance);
	if(bestPoint == Positions::None) {
		////////Broodwar->sendText("oh dear, no best point");
	} else {
		if(!movingToNudgeTarget) {
			nudgeTarget = bestPoint;
			preNudgePos = bestPointClosestTo(subject->getPosition(), bubbleClearance);
			movingToNudgeTarget = true;
			subject->getUnit()->move(bestPoint);
		}
	}

}

BWAPI::Position NudgeModule::bestPointClosestTo( BWAPI::Position p, int r )
{
	int initPopSize = 64;
	int initPopVariance = 96;
	std::vector<Position> population;
	for(int i = 0; i < initPopSize; i++) {
		Position newPosition = p;
		int newX = (rand()%initPopVariance);
		int newY = (rand()%initPopVariance);
		if(rand()%100 >= 50) newX = -newX;
		if(rand()%100 >= 50) newY = -newY;
		population.push_back(Position(p.x()+newX, p.y()+newY));
	}

	// now evaluate each point - find the points that have no units in them
	// and then find the closest you can move to
	std::vector<Position> clearPoints;
	for (vector<BWAPI::Position>::iterator i = population.begin(); i != population.end(); ++i) {
		Position cur = *i;
		std::vector<UnitModel*> nearbyUnits = unitManager->getAllFriendlyUnitsInRange(cur, r);

		for (std::vector<UnitModel*>::iterator it = nearbyUnits.begin(); it != nearbyUnits.end(); ++it) {
			UnitModel* cur = *it;
			if(cur == subject || cur->getType() == UnitTypes::Protoss_Reaver || cur->getType().isFlyer()) {
				it = nearbyUnits.erase(it);
				break;
			}
		}
		if(nearbyUnits.empty()) {
			clearPoints.push_back(cur);
		}
	}
	//////////Broodwar->sendText("found %d clear points", clearPoints.size());

	// now lets find the best clear point
	Position bestPointSoFar = Positions::None;
	for (vector<BWAPI::Position>::iterator i = clearPoints.begin(); i != clearPoints.end(); ++i) {
		Position cur = *i;
		if(bestPointSoFar == Positions::None) {
			bestPointSoFar = cur;
		} else {
			if(p.getDistance(cur) < p.getDistance(bestPointSoFar)) {
				bestPointSoFar = cur;
			}
		}
	}
	return bestPointSoFar;
}

bool NudgeModule::isBeingNudged()
{
	return movingToNudgeTarget;
}

void NudgeModule::setNudgeAbility( bool a )
{
	ableToNudge =a ;
}

bool NudgeModule::getNudgeAbility()
{
	return ableToNudge;
}

void NudgeModule::permnudge()
{
	if(movingToPreNudgeTarget || movingToNudgeTarget || subject->getUnit()->isMoving() || subject->getUnit()->isAttacking()) {
		return;
	}

	Position bestPoint = bestPointClosestTo(subject->getPosition(), bubbleClearance);
	if(bestPoint == Positions::None) {
		////////Broodwar->sendText("oh dear, no best point");
	} else {
		if(!movingToNudgeTarget) {
			nudgeTarget = bestPoint;
			preNudgePos = bestPoint;
			movingToNudgeTarget = true;
			subject->getUnit()->move(bestPoint);
		}
	}

}
