#include "UnitModel.h"
#include "MicroBehaviour.h"
#include "MarineMicroBehaviour.h"
#include "CowardlyDragoonMicroBehaviour.h"
#include "GroundScoutBehaviour.h"
#include "ArcingDragoonMicroBehaviour.h"
#include "ZealotGeneralMicroBehaviour.h"
#include "BaseBlockingDarkTemplarBehaviour.h"
#include "AerialScoutBehaviour.h"
#include "ReaverGeneralMicroBehaviour.h"
#include "RoamingScoutMicroBehaviour.h"
#include "BaseModelConsistencyMonitor.h"
#include "RoamingDarkTemplarMicroBehaviour.h"
#include "HarassingDarkTemplarBehaviour.h"
#include "ArbiterGeneralMicroBehaviour.h"
#include "SupportingObserverMicroBehaviour.h"
#include "ReaverShuttleMicroBehaviour.h"
#include "ArchonGeneralMicroBehaviour.h"
#include "PhotonCannonMicroBehaviour.h"
#include "ProbeMicroBehaviour.h"
#include "HighTemplarGeneralMicroBehaviour.h"
#include "UnitManager.h"
#include "BaseModel.h"
//#include "Windows.h"

UnitModel::UnitModel(BWAPI::Unit* target, UnitManager* uman) {
	subject = target;
	unitmanager = uman;
	lastKnownShields = target->getShields();
	lastKnownHitPoints = target->getHitPoints();
	maxHPForType = target->getType().maxHitPoints();
	if(target->getType().isBuilding() || target->getType().isFlyer() || target->getPlayer() != Broodwar->self() || target->getType() == UnitTypes::Protoss_Reaver) {
		nm = NULL;
	} else {
		nm = new NudgeModule(this, uman);
		if(target->getType().isWorker() || target->getType() == UnitTypes::Protoss_Dark_Templar || target->getType() == UnitTypes::Protoss_High_Templar || target->getType() == UnitTypes::Protoss_Archon) {
			nm->setNudgeAbility(false);
		}
	}
	lastKnownRegion = BWTA::getRegion(target->getPosition());
	BaseModel* br = uman->getBaseModelInRegion(lastKnownRegion);
	if(uman->getBaseModelInRegion(lastKnownRegion) == NULL) {
		setBaseOwner(uman->getClosestFriendlyBaseModel(subject->getPosition()));
	} else { 
		setBaseOwner(br);
	}
	

	lastKnownType = target->getType();
	lastKnownPosition = target->getPosition();
	lastKnownTilePosition = target->getTilePosition();
	lastKnownStasisStatus = false;
	behaviour = NULL;
	curBehaviourID = DEFAULT;
	owner = NULL;
	underThreat = false;
	threatLoc = Positions::None;
	stoppedPathing = false;
	microInControl = false;
	ACTIVE_TASK_TYPE = NO_TASK;
	taskTimer = -1;
	reservedForTraining = false;
	taskSwitchPosTriggerRegion = NULL;
	taskSwitchPosTrigger = Positions::None;
	armyOwner = NULL;
	alive = true;
	nextWayPoint = TilePosition(-90,-90);
	// mainly for formatting on-screen display easily and adding colours to friendly/enemy units
	if(target->getPlayer() == BWAPI::Broodwar->self()) {
		ownedByPlayer = true;
	} else {
		ownedByPlayer = false;
	}

	// bit of a hack, but oh well
	if(ownedByPlayer) {
		if(!uman->getPendingBehaviours()[lastKnownType].empty()) {
			if(compileBehaviour(uman->getPendingBehaviours()[lastKnownType].top())) {
				unitmanager->dequeueBehaviour(lastKnownType);
			}
		}
		// else this might be a unit that has a default behaviour, like zealots
		if(lastKnownType == UnitTypes::Protoss_Zealot) {
			compileBehaviour(PROTOSS_GENERAL_ZEALOT);
		}
		if(lastKnownType == UnitTypes::Protoss_Arbiter) {
			compileBehaviour(PROTOSS_GENERAL_ARBITER);
		}

		if(lastKnownType == UnitTypes::Protoss_Photon_Cannon) {
			compileBehaviour(PROTOSS_PHOTON_CANNON);
		}
		if(lastKnownType == UnitTypes::Protoss_Reaver) {
			compileBehaviour(PROTOSS_GENERAL_REAVER);
		}
		if(lastKnownType == UnitTypes::Protoss_High_Templar) {
			compileBehaviour(PROTOSS_GENERAL_HIGHTEMPLAR);
		}
		if(lastKnownType == UnitTypes::Protoss_Dragoon) {
			compileBehaviour(PROTOSS_ARC_DRAGOON);
		}
		if(lastKnownType == UnitTypes::Protoss_Archon || subject->getType() == UnitTypes::Protoss_Archon) {
			compileBehaviour(PROTOSS_GENERAL_ARCHON);
		}
		if(lastKnownType == UnitTypes::Protoss_Probe) {
			compileBehaviour(PROTOSS_PROBE);
		}
		if(lastKnownType == UnitTypes::Protoss_Scout  || lastKnownType == UnitTypes::Protoss_Corsair){
			compileBehaviour(PROTOSS_ROAMING_SCOUT);
		}
		if(lastKnownType == UnitTypes::Protoss_Dark_Templar) {
			compileBehaviour(unitmanager->getPreferredDTStrat());
		}
	}
}

//************************************
// Method:    getUnit
// FullName:  UnitModel::getUnit
// Access:    public 
// Returns:   Unit*
// Comments:  none
//************************************
BWAPI::Unit* UnitModel::getUnit() {
	return subject;
}
//************************************
// Method:    isOwnedByPlayer
// FullName:  UnitModel::isOwnedByPlayer
// Access:    public 
// Returns:   bool
// Comments:  none
//************************************
bool UnitModel::isOwnedByPlayer() {
	return ownedByPlayer;
}

//************************************
// Method:    isAlive
// FullName:  UnitModel::isAlive
// Access:    public 
// Returns:   bool
// Comments:  none
//************************************
bool UnitModel::isAlive() {

	if(lastKnownType.isRefinery() && !subject->getType().isRefinery()) {
		alive = false;
	}

		return alive;
}

//************************************
// Method:    setLastKnownPositions
// FullName:  UnitModel::setLastKnownPositions
// Access:    public 
// Returns:   void
// Parameter: BWAPI::Position pos
// Parameter: BWAPI::Region reg
// Comments:  none
//************************************
void UnitModel::setLastKnownPosition( BWAPI::Position pos)
{
	lastKnownPosition = pos;
}

//************************************
// Method:    setLastKnownHitPoints
// FullName:  UnitModel::setLastKnownHitPoints
// Access:    public 
// Returns:   void
// Parameter: int hp
// Comments:  none
//************************************
void UnitModel::setLastKnownHitPointsAndShields( int hp, int shields )
{
	lastKnownHitPoints = hp;
	lastKnownShields = shields;
}

//************************************
// Method:    setLastKnownUnitType
// FullName:  UnitModel::setLastKnownUnitType
// Access:    public 
// Returns:   void
// Parameter: UnitType t
// Comments:  none
//************************************
void UnitModel::setLastKnownUnitType(BWAPI::UnitType t) {
	lastKnownType = t;
}

//************************************
// Method:    getPosition
// FullName:  UnitModel::getPosition
// Access:    public 
// Returns:   BWAPI::Position
// Comments:  Return the last known position of a unit. If the unit is owned by the player calling the method, it will 
//			  return current and accurate information. If the unit is owned by the enemy, it will return the last observed value.
//			  i.e. the believed value.
//************************************
BWAPI::Position UnitModel::getPosition()
{
	return lastKnownPosition;
}

//************************************
// Method:    getHP
// FullName:  UnitModel::getHP
// Access:    public 
// Returns:   int
// Comments:  Return the last known HP of a unit. If the unit is owned by the player calling the method, it will 
//			  return current and accurate information. If the unit is owned by the enemy, it will return the last observed value.
//			  i.e. the believed value.
//************************************
int UnitModel::getHP()
{
	return lastKnownHitPoints;
}

//************************************
// Method:    getType
// FullName:  UnitModel::getType
// Access:    public 
// Returns:   BWAPI::UnitType
// Comments:  Returns the last known type of a unit.
//************************************
BWAPI::UnitType UnitModel::getType()
{
	return lastKnownType;
}

//************************************
// Method:    getQualitativeHP
// FullName:  UnitModel::getQualitativeHP
// Access:    public 
// Returns:   int
// Comments:  Gets HP as a percentage of the maximum for this unit type.
//************************************
int UnitModel::getQualitativeHP()
{
	float prp = 100.0/maxHPForType;
	return lastKnownHitPoints*prp;
}

//************************************
// Method:    setLastKnownRegion
// FullName:  UnitModel::setLastKnownRegion
// Access:    public 
// Returns:   void
// Parameter: BWAPI::Region r
// Comments:  Sets the region the unit was last seen in.
//************************************
void UnitModel::setLastKnownRegion( BWTA::Region* r )
{
	if(BWTA::getNearestChokepoint(subject->getPosition())->getCenter().getApproxDistance(subject->getPosition()) < 64) {
	} else {
		lastKnownRegion = r;
	}
}

//************************************
// Method:    getRegion
// FullName:  UnitModel::getRegion
// Access:    public 
// Returns:   BWAPI::Region*
// Comments:  Gets the region the unit was last seen in.
//************************************
BWTA::Region* UnitModel::getRegion()
{
	return lastKnownRegion;
}

//************************************
// Method:    getTilePosition
// FullName:  UnitModel::getTilePosition
// Access:    public 
// Returns:   BWAPI::TilePosition
// Comments:  none
//************************************
BWAPI::TilePosition UnitModel::getTilePosition()
{
	return lastKnownTilePosition;
}

//************************************
// Method:    setLastKnownTilePosition
// FullName:  UnitModel::setLastKnownTilePosition
// Access:    public 
// Returns:   void
// Parameter: BWAPI::TilePosition pos
// Comments:  none
//************************************
void UnitModel::setLastKnownTilePosition( BWAPI::TilePosition pos )
{
	lastKnownTilePosition = pos;
}

//************************************
// Method:    setMicroBehaviour
// FullName:  UnitModel::setMicroBehaviour
// Access:    public 
// Returns:   void
// Parameter: MicroBehaviour * beh
// Comments:  none
//************************************
void UnitModel::setMicroBehaviour( MicroBehaviour* beh )
{
	curBehaviourID = beh->getBehaviourID();
	behaviour = beh;
}

void UnitModel::setMicroBehaviour( int BEHAVIOUR_IDENTIFIER )
{
	curBehaviourID = BEHAVIOUR_IDENTIFIER;
	compileBehaviour(BEHAVIOUR_IDENTIFIER);
}


//************************************
// Method:    executeMicroBehaviour
// FullName:  UnitModel::executeMicroBehaviour
// Access:    public 
// Returns:   void
// Comments:  none
//************************************
void UnitModel::executeMicroBehaviour()
{
	if(!ownedByPlayer){
		return;
	}
	if(!getUnit()->isCompleted()) {
		return;
	}
	if(nm != NULL) {
	if(nm->getNudgeAbility() == true || nm->isBeingNudged()) {
		nm->execute();
	}
	}

	if(behaviour != NULL) {
		microInControl = behaviour->execute();
	} else {
		microInControl = false;
	}
	if(microInControl) {
		return;
	}
	if(taskTimer != -1) {
		if(taskTimer > 0) {
			taskTimer--;
		}

		if(taskTimer == 0 && getCurrentTaskType() != NO_TASK) {
			//////Broodwar->sendText("abandoning task!");
			setCurrentTaskType(NO_TASK);
			taskTimer = -1;
		}
	}
	if(taskSwitchPosTrigger != Positions::None) {
		if(lastKnownPosition.getDistance(taskSwitchPosTrigger) <= 300) {
			setCurrentTaskType(NO_TASK);
			taskSwitchPosTrigger = Positions::None;
		}
	}

	if(taskSwitchPosTriggerRegion != NULL) {
		if(lastKnownRegion == taskSwitchPosTriggerRegion) {
			setCurrentTaskType(NO_TASK);
			taskSwitchPosTrigger = Positions::None;
		}
	}
	if(!path.empty()) {
		for (std::vector<TilePosition>::iterator i = path.begin(); i != path.end(); ++i)
		{
			Position cur = (Position)(*i);
			if(!stoppedPathing) {
				Broodwar->drawBoxMap(cur.x(), cur.y(), cur.x()+6, cur.y()+6,BWAPI::Colors::Green, true);
			} else {
				Broodwar->drawBoxMap(cur.x(), cur.y(), cur.x()+6, cur.y()+6,BWAPI::Colors::Red, true);
			}
		}

	}
	// follow a path if we have one
	if(!path.empty() && !stoppedPathing && !(subject->isAttacking() || subject->isStartingAttack())) {
		for (std::vector<TilePosition>::iterator i = path.begin(); i != path.end(); ++i)
		{
			Position cur = (Position)(*i);
			Broodwar->drawBoxMap(cur.x(), cur.y(), cur.x()+6, cur.y()+6,BWAPI::Colors::White, true);

		}

		//if(!subject->isMoving()) {
			subject->move((Position)nextWayPoint);
		//}
		int minDist = 196;
		if(subject->getType().isFlyer()) {
			if(isUnderThreat()) {
				minDist = 128;
			} else {
				minDist = 396;
			}

		}
		if(subject->getPosition().getDistance((Position)nextWayPoint) <= minDist) {
			path.erase(path.begin());
			if(!path.empty()) {
				if(PATHFINDING_CHOICE == PATHFINDING_THREATAWARE_ASTAR_AIR) {
				//	//////Broodwar->sendText("replanning");
					//pathMove((path.at(path.size()-1)), PATHFINDING_CHOICE);
				}
				nextWayPoint = *(path.begin());
			} else {
				path.clear();
				PATHFINDING_CHOICE = -1;
				stoppedPathing = true;
				subject->move((Position)moveLoc);
			}
		}

	}



}

//************************************
// Method:    getMicroBehaviour
// FullName:  UnitModel::getMicroBehaviour
// Access:    public 
// Returns:   MicroBehaviour*
// Comments:  none
//************************************
MicroBehaviour* UnitModel::getMicroBehaviour()
{
	return behaviour;
}

void UnitModel::clearMicroBehaviour()
{
	behaviour = NULL;
	curBehaviourID = DEFAULT;
}


BWAPI::Position UnitModel::findFurthestPoint( std::vector<BWAPI::Position> points, BWAPI::Position clamp, int dist )
{
	BWAPI::Position bestPosition = Positions::None;
	for (std::vector<BWAPI::Position>::iterator i = points.begin(); i != points.end(); ++i)
	{
		BWAPI::Position cur = *i;
		if(clamp == BWAPI::Positions::None) {

		} else {
			if(cur.getDistance(clamp) > dist) {
				continue;
			}
		}
		if(bestPosition == BWAPI::Positions::None) {
			bestPosition = cur;
		} else {
			if(getPosition().getDistance(cur) > getPosition().getDistance(bestPosition)) {
					bestPosition = cur;
			}
		}
	}
	return bestPosition;
}


Position UnitModel::findClosestPoint( std::vector<BWAPI::Position> points , BWAPI::Position clamp, int dist)
{
	BWAPI::Position bestPosition = Positions::None;
	for (std::vector<BWAPI::Position>::iterator i = points.begin(); i != points.end(); ++i)
	{
		BWAPI::Position cur = *i;
		if(!cur.isValid()) {
			cur = cur.makeValid();
		}
		if(clamp == BWAPI::Positions::None) {

		} else {
			if(cur.getDistance(clamp) > dist) {
				continue;
			}
		}
		if(bestPosition == BWAPI::Positions::None) {
			bestPosition = cur;
		} else {
				if(unitmanager->hasBuildablePath(subject->getTilePosition(), (TilePosition)cur)) {
			if(getPosition().getDistance(cur) < getPosition().getDistance(bestPosition)) {
				if(getUnit()->hasPath(cur) && Broodwar->getUnitsOnTile(cur.x(), cur.y()).empty()) {
				
					bestPosition = cur;

				}
			}
				}
		}
	}
	return bestPosition;
}



void UnitModel::pathMove( TilePosition p , int PATHFINDING_TYPE)
{
	if(!path.empty()) {
	} else {
		path.clear();
		TilePosition startPathFrom = lastKnownTilePosition;
		if(nextWayPoint.x() != -90 && nextWayPoint.y() != -90) {
			startPathFrom = nextWayPoint;
		}

		// lets see if someone else registered a path 
		if(PATHFINDING_TYPE == PATHFINDING_THREATAWARE_ASTAR_GROUND_NO_OBSTACLES_DT || PATHFINDING_TYPE == PATHFINDING_THREATAWARE_ASTAR_AIR) {
			// we might already know of a path, so...
			TilePosition stp = BWTA::getNearestBaseLocation(subject->getPosition())->getTilePosition();
			TilePosition etp = p;
			BWTA::Region* start = BWTA::getRegion(stp);
			BWTA::Region* end = BWTA::getRegion(p);

			if(PATHFINDING_TYPE == PATHFINDING_THREATAWARE_ASTAR_GROUND_NO_OBSTACLES_DT) {
				if(unitmanager->getPathRepo()->hasPathGround(start, end)) {
					std::vector<TilePosition> atp = unitmanager->getPathRepo()->getPathGround(start, end);
					pathMove(atp);
					return;
				}
			}
			if(PATHFINDING_TYPE == PATHFINDING_THREATAWARE_ASTAR_AIR) {
				if(unitmanager->getPathRepo()->hasPathAir(start, end)) {
					std::vector<TilePosition> atp = unitmanager->getPathRepo()->getPathAir(start, end);
					pathMove(atp);
					return;
				}
			}

		}


		switch(PATHFINDING_TYPE) {
		case -1:
			return;
		case PATHFINDING_ASTAR: 
			path = unitmanager->AstarSearchPath(startPathFrom, p, 1);
			break;
		case PATHFINDING_THREATAWARE_ASTAR_AIR:
			path = unitmanager->threatAwareAstarSearchPathAir(lastKnownTilePosition, p, 4);
			break;
		case PATHFINDING_THREATAWARE_ASTAR_GROUND:
			path = unitmanager->threatAwareAstarSearchPathGround(startPathFrom, p);
			break;
		case PATHFINDING_THREATAWARE_ASTAR_GROUND_AVOIDUNITS:
			path = unitmanager->threatAwareAstarSearchPathGroundAvoidUnits(startPathFrom, p);
			break;
		case PATHFINDING_THREATAWARE_ASTAR_AIR_FINER_GRANULARITY:
			path = unitmanager->threatAwareAstarSearchPathAir(startPathFrom, p, 2);
			break;
		case PATHFINDING_THREATAWARE_ASTAR_GROUND_NO_OBSTACLES:
			path = unitmanager->AstarSearchPathNoObstacles(unitmanager->getClosestFriendlyBaseModel(subject->getPosition())->getBaseLOC()->getTilePosition(), p);
			break;
		case PATHFINDING_THREATAWARE_ASTAR_GROUND_NO_OBSTACLES_HIGHGRAIN:
			path = unitmanager->AstarSearchPathNoObstacles(unitmanager->getClosestFriendlyBaseModel(subject->getPosition())->getBaseLOC()->getTilePosition(), p, 5);
			break;
		case PATHFINDING_THREATAWARE_ASTAR_GROUND_NO_OBSTACLES_DT:
			path = unitmanager->AstarSearchPathNoObstacles(BWTA::getNearestBaseLocation(subject->getPosition())->getTilePosition(), p,1);
			break;
		}

		if(!path.empty()) {
			nextWayPoint = path.at(0);
			moveLoc = path.at(path.size()-1);
			PATHFINDING_CHOICE = PATHFINDING_TYPE;
			stoppedPathing = false;
			subject->move((Position)nextWayPoint);
			BWTA::Region* start = BWTA::getNearestBaseLocation(subject->getPosition())->getRegion();
			BWTA::Region* end = BWTA::getRegion(p);

			if(PATHFINDING_TYPE == PATHFINDING_THREATAWARE_ASTAR_GROUND_NO_OBSTACLES_DT) {
				if(!unitmanager->getPathRepo()->hasPathGround(start, end)) {
				unitmanager->getPathRepo()->registerPathGround(start, end, path);
			}
			}
			if(PATHFINDING_TYPE == PATHFINDING_THREATAWARE_ASTAR_AIR) {
				if(!unitmanager->getPathRepo()->hasPathAir(start, end)) {
					unitmanager->getPathRepo()->registerPathAir(start, end, path);
				}
			}

		} else {
			////Broodwar->sendText("sometiign wrong wrong");
			subject->move((Position)p);
		}
	}
}

void UnitModel::pathMove( std::vector<TilePosition> incPath )
{
	if(!incPath.empty()) {
		
		TilePosition curPos = subject->getTilePosition();
		TilePosition bestPos = TilePositions::None;
		std::vector<TilePosition>::iterator bp;
		path.clear();

		// we might already know of a path, so...
		TilePosition stp = incPath.at(0);
		TilePosition etp = incPath.at(incPath.size()-1);
		BWTA::Region* start = BWTA::getRegion(stp);
		BWTA::Region* end = BWTA::getRegion(etp);

		if(unitmanager->getPathRepo()->hasPathGround(start, end)) {
			
		} else {
			unitmanager->getPathRepo()->registerPathGround(start, end, incPath);
		}

		for(std::vector<TilePosition>::iterator i = incPath.begin(); i != incPath.end(); i++) {
			TilePosition cur = *i;
			if(bestPos == TilePositions::None) {
				bestPos = cur;
				bp = i;
			} else {
				int distToUnit = cur.getDistance(curPos);
				int bestDistToUnit = bestPos.getDistance(curPos);
				if(distToUnit < bestDistToUnit) {
					bestPos = cur;
					bp = i;
				}
			}
		}
		bool beginCopying = true;
		for(std::vector<TilePosition>::iterator it = incPath.begin(); it != incPath.end(); ++it) {
			TilePosition cur = *it;
			if(cur == bestPos) {
				beginCopying = true;
			}
			if(beginCopying) {
				path.push_back(cur);
			}
		}
	
		nextWayPoint = path.at(0);
		moveLoc = path.at(path.size()-1);
		PATHFINDING_CHOICE = PATHFINDING_THREATAWARE_ASTAR_GROUND_NO_OBSTACLES;
		stoppedPathing = false;
		subject->move((Position)nextWayPoint);
	} else {

	}
}

void UnitModel::pathStop()
{
	path.clear();
	PATHFINDING_CHOICE = -1;
	stoppedPathing = true;
	subject->stop();
}

void UnitModel::setCurrentTaskType( int TASK_TYPE )
{
	ACTIVE_TASK_TYPE = TASK_TYPE;
	taskTimer = -1;
}

void UnitModel::setCurrentTaskType( int TASK_TYPE, int duration )
{
	ACTIVE_TASK_TYPE = TASK_TYPE;
	taskTimer = duration;
}

void UnitModel::setCurrentTaskType( int TASK_TYPE, Position posTrigger )
{
	ACTIVE_TASK_TYPE = TASK_TYPE;
	taskSwitchPosTrigger = posTrigger;
}

void UnitModel::setCurrentTaskType( int TASK_TYPE, BWTA::Region* reg )
{
	ACTIVE_TASK_TYPE = TASK_TYPE;
	taskSwitchPosTriggerRegion = reg;
}

int UnitModel::getCurrentTaskType()
{
	return ACTIVE_TASK_TYPE;
}

bool UnitModel::compileBehaviour( int BEHAVIOUR_IDENTIFIER )
{

	if(lastKnownType == UnitTypes::Terran_Marine) {
		return true;
	}

	// we do this to get around the merge process
	if(lastKnownType == UnitTypes::Protoss_Probe) {
		setMicroBehaviour(new ProbeMicroBehaviour(this, unitmanager));
	}
	if(lastKnownType == UnitTypes::Protoss_Scout || lastKnownType == UnitTypes::Protoss_Corsair) {
		setMicroBehaviour(new RoamingScoutMicroBehaviour(this, unitmanager));
	}
	if(lastKnownType == UnitTypes::Protoss_Archon || subject->getType() == UnitTypes::Protoss_Archon) {
		setMicroBehaviour(new ArchonGeneralMicroBehaviour(this, unitmanager));
	}
	if(lastKnownType == UnitTypes::Protoss_High_Templar) {
		setMicroBehaviour(new HighTemplarGeneralMicroBehaviour(this, unitmanager));
	}
	if(lastKnownType == UnitTypes::Protoss_Reaver) {
		setMicroBehaviour(new ReaverGeneralMicroBehaviour(this, unitmanager));
	}
	if(lastKnownType == UnitTypes::Protoss_Photon_Cannon) {
		setMicroBehaviour(new PhotonCannonMicroBehaviour(this, unitmanager));
	}
	if(lastKnownType == UnitTypes::Protoss_Photon_Cannon) {
		setMicroBehaviour(new ProbeMicroBehaviour(this, unitmanager));
	}
	if(lastKnownType == UnitTypes::Protoss_Observer) {
		switch(BEHAVIOUR_IDENTIFIER) {
		case DEFAULT:
			//////Broodwar->sendText("no observer behaviour attached");
			break;
		case PROTOSS_SUPPORTING_OBSERVER:
			setMicroBehaviour(new SupportingObserverMicroBehaviour(this, unitmanager));
			break;
		}
	}

	if(lastKnownType == UnitTypes::Protoss_Shuttle) {
		switch(BEHAVIOUR_IDENTIFIER) {
		case DEFAULT:
			//////Broodwar->sendText("no shuttle behaviour attached");
			break;
		case PROTOSS_REAVER_CARRYING_SHUTTLE:
//			setMicroBehaviour(new ShuttleAndReaverMicroBehaviour(this, unitmanager));
			break;
		}
	}

	if(lastKnownType == UnitTypes::Protoss_Arbiter) {
		switch(BEHAVIOUR_IDENTIFIER) {
		case DEFAULT:
			//////Broodwar->sendText("no arbiter behaviour attached");
			break;
		case PROTOSS_GENERAL_ARBITER:
			setMicroBehaviour(new ArbiterGeneralMicroBehaviour(this, unitmanager));
			break;
		}
	}


	if(lastKnownType == UnitTypes::Protoss_Zealot) {
		switch(BEHAVIOUR_IDENTIFIER) {
		case DEFAULT:
			//////Broodwar->sendText("no zealot behaviour attached");
			break;
		case PROTOSS_GENERAL_ZEALOT:
			setMicroBehaviour(new ZealotGeneralMicroBehaviour(this, unitmanager));
			break;
		}
	}

	if(lastKnownType == UnitTypes::Protoss_Dragoon) {
		switch(BEHAVIOUR_IDENTIFIER) {
		case DEFAULT:
			//////Broodwar->sendText("attached no dragoon micro"); return true;
			break;
		case PROTOSS_ARC_DRAGOON:
			//////Broodwar->sendText("attached arcing dragoon micro");
			setMicroBehaviour(new ArcingDragoonMicroBehaviour(this, unitmanager)); return true;
			break;
		case PROTOSS_COWARDLY_DRAGOON:
			//////Broodwar->sendText("attached cowardly dragoon micro");
			setMicroBehaviour(new CowardlyDragoonMicroBehaviour(this, unitmanager)); return true;
			break;
		}
	}

	if(lastKnownType == UnitTypes::Protoss_Observer) {
		switch(BEHAVIOUR_IDENTIFIER) {
		case DEFAULT:
			//////Broodwar->sendText("no observer micro attached");
			break;
		case PROTOSS_SCOUTING_OBSERVER:
			setMicroBehaviour(new AerialScoutBehaviour(this, unitmanager)); return true;
			break;
		}
		
	}

	if(lastKnownType == UnitTypes::Protoss_Dark_Templar) {
		switch(BEHAVIOUR_IDENTIFIER) {
		case DEFAULT:
			//////Broodwar->sendText("attached no dt micro"); return true;
			break; 
		case PROTOSS_HARASSING_DARK_TEMPLAR:
			setMicroBehaviour(new HarassingDarkTemplarBehaviour(this, unitmanager)); return true;
			break;
		case PROTOSS_ROAMING_DARK_TEMPLAR:
			setMicroBehaviour(new RoamingDarkTemplarMicroBehaviour(this, unitmanager)); return true;
			break;
		}
	}
	return false;
}

void UnitModel::promptPathReplan( TilePosition t )
{
	if(path.empty()) {
		return;
	}
	//////Broodwar->sendText("prompting replan for a %s", subject->getType().getName().c_str());
	bool replanJustified = false;
	for (std::vector<BWAPI::TilePosition>::iterator i = path.begin(); i != path.end(); ++i)
	{
		Position cur = (Position)*i;
		if(cur.getDistance((Position)t) <= UnitTypes::Protoss_Photon_Cannon.sightRange()*1.1) {
		//	////Broodwar->sendText("yep we must repay");
			replanJustified = true;
			break;
		}
	}
	if(replanJustified) {
		if(subject->getType().isFlyer()) {
		//	path = unitmanager->threatAwareAstarSearchPathAir(getTilePosition(), (path.at(path.size()-1)));
			TilePosition target = path.at(path.size()-1);
			path.clear();
			pathMove(target, PATHFINDING_THREATAWARE_ASTAR_AIR);
		} else {
			path = unitmanager->threatAwareAstarSearchPathGround(getTilePosition(), (path.at(path.size()-1)));
		}
	}
}

bool UnitModel::isFollowingPath()
{
	if(path.empty()) {
		return false;
	} else {
		return true;
	}
}

int UnitModel::getShields()
{
	return lastKnownShields;
}

bool UnitModel::isOnSmallChokePoint()
{
	if(isOwnedByPlayer() && isAlive()) {
	BWTA::Chokepoint* cp = BWTA::getNearestChokepoint(subject->getPosition());
	if(cp != NULL) {
		if(cp->getWidth() < 100) {
			if(subject->getPosition().getDistance(cp->getCenter()) <= 36) {
					return true;
				}
			}
		}
	}
	return false;
}

bool UnitModel::microBehaviourInControl()
{
	return microInControl;
}

bool UnitModel::isStasised()
{
	return lastKnownStasisStatus;
}

void UnitModel::setLastKnownStasisStatus(bool s)
{
	lastKnownStasisStatus = s;
}

void UnitModel::setTaskTimer( int t )
{
	taskTimer = t;
}

void UnitModel::nudgeOffTile(TilePosition t)
{
	if(getUnit()->isMoving() || getUnit()->isAttacking() || getUnit()->isStartingAttack()) {
		return;
	}
	ParticleGenerator* p = new ParticleGenerator(16,64, lastKnownPosition);
	std::vector<Position> v = p->generate();
	Position moveTo = Positions::None;
	for(std::vector<Position>::const_iterator i = v.begin(); i != v.end(); i++) {
		Position cur = *i;
		if(moveTo == Positions::None) {
			moveTo = cur;
		} else {
			if(cur.getDistance(moveTo) > cur.getDistance((Position)t)) {
				moveTo = cur;
			}
		}
	}
	if(moveTo != Positions::None) {
		getUnit()->move(moveTo);
	}
}

void UnitModel::setBaseOwner( BaseModel* b )
{
	if(b != NULL) {
		owner = b;
	}
}

BaseModel* UnitModel::getBaseOwner()
{
	return owner;
}

bool UnitModel::isBeingTargetted()
{
	if(subject->isUnderAttack()) {
		return true;
	}
	std::vector<UnitModel*> unitsInRange = unitmanager->getAllEnemyUnitsInRange(lastKnownPosition, lastKnownType.sightRange()*1.2);
	for(std::vector<UnitModel*>::const_iterator i = unitsInRange.begin(); i != unitsInRange.end(); i++) {
		UnitModel* cur = *i;
		if(!cur->getUnit()->isVisible()) {
			continue;
		} 
		if(cur->getUnit()->getTarget() == subject || cur->getUnit()->getOrderTarget() == subject) {
			if(cur->getPosition().getDistance(subject->getPosition()) <= cur->getType().groundWeapon().maxRange()) {
				return true;
			}
		}
	}
	return false;
}

void UnitModel::setAlive( bool a )
{
	if( a == alive) {
		return;
	}
	alive = a;

	 
	// allow buildings to regenerate themselves
	if(isOwnedByPlayer() && !alive && !unitmanager->hasBeenRebuilt(subject)) {
		if(subject->getType().isBuilding()) {
	
			if(this->getBaseOwner() != NULL) {
				if(this->getBaseOwner()->getBuildOrder() != NULL) {
					this->getBaseOwner()->getBCM()->registerNewBuilding(subject->getType());
					
					unitmanager->registerRebuilt(subject);

				}
			}
			
		}
	}
}

bool UnitModel::isUnderThreat()
{
	return underThreat;
}

Army* UnitModel::getArmyOwner()
{
	return armyOwner;
}

void UnitModel::setArmyOwner( Army* a )
{
	armyOwner = a;
}

UnitModel::~UnitModel()
{
	if(behaviour != NULL) {
	delete behaviour;
	}
}


bool UnitModel::isReservedForTraining()
{
	return reservedForTraining;
}

void UnitModel::setReservedStatus( bool trainingReservationStatus )
{
	reservedForTraining = trainingReservationStatus;
}

void UnitModel::drawDebuggingInformation()
{



	////Broodwar->drawTextMap(subject->getPosition().x()+8, subject->getPosition().y()-16, "%d", getQualitativeHP());
	//if(lastKnownType != UnitTypes::Protoss_Gateway) {
	//	return;
	//}
	//if(isReservedForTraining() == false) {
	//	//Broodwar->drawEllipseMap(subject->getPosition().x(), subject->getPosition().y()-32, 12, 12, BWAPI::Colors::Green, true);
	//} else {
	//	//Broodwar->drawEllipseMap(subject->getPosition().x(), subject->getPosition().y()-32, 12, 12, BWAPI::Colors::Red, true);
	//}
	/*
	if(lastKnownType == UnitTypes::Protoss_Pylon) {
		//Broodwar->drawEllipseMap(lastKnownPosition.x(), lastKnownPosition.y(), UnitTypes::Protoss_Pylon.sightRange(), UnitTypes::Protoss_Pylon.sightRange()*0.6, BWAPI::Colors::Green, false);

		std::set<BWAPI::Unit*> supporters = Broodwar->getUnitsInRadius(subject->getPosition(), UnitTypes::Protoss_Pylon.sightRange());
		for (std::set<Unit*>::iterator i = supporters.begin(); i != supporters.end(); ++i)
		{
			BWAPI::Unit* cur = *i;
			int tarYpos = cur->getPosition().y();
			int pylYpos = subject->getPosition().y();
			float yRange = UnitTypes::Protoss_Pylon.sightRange()*0.6;
			if(abs(tarYpos-pylYpos) >= yRange) {
				continue;
			}
			//if(cur->getType() == UnitTypes::Protoss_Photon_Cannon) {
				//Broodwar->drawEllipseMap(cur->getPosition().x(), cur->getPosition().y(), 32, 32, BWAPI::Colors::Red, false);
			//}
		}

		


	}
*/

}

UnitModel* UnitModel::findClosestUnit( std::vector<UnitModel*> points )
{
	UnitModel* bestSoFar = NULL;
	int bestDistSoFar = 10000;
	for(std::vector<UnitModel*>::const_iterator i = points.begin(); i != points.end(); i++) {
		UnitModel* cur = *i;
		int dist = cur->getPosition().getDistance(subject->getPosition());
		if(dist < bestDistSoFar){
			bestDistSoFar = dist;
			bestSoFar = cur;
		}
	}
	return bestSoFar;
}

void UnitModel::attack( UnitModel* tar )
{
subject->attack(tar->getUnit());
}

void UnitModel::attack( Unit* tar )
{
	subject->attack(tar);
}

bool UnitModel::isUnderThreatMin( int num )
{
	std::vector<UnitModel*> nearByUnits = unitmanager->getAllEnemyUnitsInRange(subject->getPosition(), 256);
	int c = 0;
	for(std::vector<UnitModel*>::const_iterator i = nearByUnits.begin(); i != nearByUnits.end(); i++) {
		UnitModel* cur = *i;
		if(!cur->getUnit()->isVisible()) {
			continue;
		}
		if(!cur->isOwnedByPlayer() && !cur->getUnit()->getType().isNeutral() && cur->getType() != UnitTypes::Terran_Medic) {
			c++;
		}
	}

	return c >= num;
}

BWAPI::Position UnitModel::getThreatLoc()
{
	return threatLoc;
}

BWAPI::Position UnitModel::findClosestPointTowardsBase( std::vector<BWAPI::Position> points, BWAPI::Position clamp, int dist )
{
	BWAPI::Position bestPosition = Positions::None;
	BaseModel* closestBase = unitmanager->getClosestFriendlyBaseModel(subject->getPosition());
	for (std::vector<BWAPI::Position>::iterator i = points.begin(); i != points.end(); ++i)
	{
		BWAPI::Position cur = *i;
		if(clamp == BWAPI::Positions::None) {

		} else {
			if(cur.getDistance(clamp) > dist) {
				continue;
			}
		}
		if(bestPosition == BWAPI::Positions::None) {
			bestPosition = cur;
		} else {
			if(unitmanager->hasBuildablePath(subject->getTilePosition(), (TilePosition)cur)) {
			if(getPosition().getDistance(cur) < getPosition().getDistance(bestPosition)) {
				if(getUnit()->hasPath(cur) && Broodwar->getUnitsOnTile(cur.x(), cur.y()).empty()) {
					if(getRegion() != closestBase->getBaseRegion()){
						if(cur.getDistance(closestBase->getPosition()) < bestPosition.getDistance(closestBase->getPosition())){
							bestPosition = cur;
						}
					} else {
					bestPosition = cur;
					}

				}
			}
			}
		}
	}
	return bestPosition;
}

void UnitModel::clearArmyOwner()
{
	armyOwner = NULL;
}

int UnitModel::getQualitativeShields()
{
	float prp = 100.0/lastKnownType.maxShields();
	return lastKnownShields*prp;
}

BWAPI::TilePosition UnitModel::getNextPathWaypoint()
{
	return nextWayPoint;
}

std::vector<TilePosition> UnitModel::getPath()
{
	return path;
}

NudgeModule* UnitModel::getNudgeModule()
{
	return nm;
}

bool UnitModel::isUnderThreatRange(int range)
{
	if(subject->isUnderAttack()) {
		threatLoc = subject->getPosition();
		return true;
	}
	std::vector<UnitModel*> nearByUnits = unitmanager->getAllEnemyUnitsInRange(subject->getPosition(), range);
	for(std::vector<UnitModel*>::const_iterator i = nearByUnits.begin(); i != nearByUnits.end(); i++) {
		UnitModel* cur = *i;
		if(!cur->getUnit()->isVisible()) {
			continue;
		}
		if(cur->getType().isBuilding() && cur->getType() != UnitTypes::Protoss_Photon_Cannon && cur->getType() != UnitTypes::Terran_Bunker && cur->getType() != UnitTypes::Zerg_Sunken_Colony) {
			continue;
		}
		if(cur->getType().isBuilding() && subject->getType().isFlyer() && cur->getType() != UnitTypes::Terran_Missile_Turret && cur->getType() != UnitTypes::Zerg_Spore_Colony && cur->getType() != UnitTypes::Protoss_Photon_Cannon) {
			continue;
		}
		if(!cur->isOwnedByPlayer() && !cur->getUnit()->getType().isNeutral() && cur->getType() != UnitTypes::Terran_Medic) {
			threatLoc = cur->getPosition();
			return true;
		}
	}

	return false;
}

bool UnitModel::checkUnderThreat()
{
	if(subject->isUnderAttack() || isBeingTargetted()) {
		threatLoc = subject->getPosition();
		return true;
	}
	std::vector<UnitModel*> nearByUnits = unitmanager->getAllEnemyUnitsInRange(subject->getPosition(), 300);
	for(std::vector<UnitModel*>::const_iterator i = nearByUnits.begin(); i != nearByUnits.end(); i++) {
		UnitModel* cur = *i;
		if(!cur->getUnit()->isVisible()) {
			continue;
		}
		if(cur->getType().isBuilding() && cur->getType() != UnitTypes::Protoss_Photon_Cannon && cur->getType() != UnitTypes::Terran_Bunker && cur->getType() != UnitTypes::Zerg_Sunken_Colony) {
			continue;
		}
		if(cur->getType().isBuilding() && subject->getType().isFlyer() && cur->getType() != UnitTypes::Terran_Missile_Turret && cur->getType() != UnitTypes::Zerg_Spore_Colony && cur->getType() != UnitTypes::Protoss_Photon_Cannon) {
			continue;
		}
		if(!cur->isOwnedByPlayer() && !cur->getUnit()->getType().isNeutral() && cur->getType() != UnitTypes::Terran_Medic) {
			threatLoc = cur->getPosition();
			return true;
		}
	}

	return false;
}

void UnitModel::setUnderThreat( bool s )
{
	underThreat = s;
}

