#include "NovaStdAfx.h"
#include "WorkerManager.h"
#include "SquadManager.h"

using namespace BWAPI;

WorkerManager::WorkerManager()
	:_mineralPS(0),
	 _mineralPM(0),
	 _averageMineralPS(0),
	 _averageMineralPM(0),
	_logger(log4cxx::Logger::getLogger("WorkerManager"))
{
	// Initialize the counters for minerals per second, or per minute:
	for(int i = 0;i<61;i++) _accumluatedMinerals[i]=50;
	_lastFrameCount = GetTickCount();

	// save initial minerals
	for(UnitSet::const_iterator i=Broodwar->getMinerals().begin();i!=Broodwar->getMinerals().end();++i) {
		if ((*i)->isVisible()) { // only save closest to our base
			_mineralsExploitation[*i] = 0;
		}
	}

	_workerState.clear();
	_workerBuildOrder.clear();
	_workerBuildingRefinery.clear();
};

void WorkerManager::addUnit(Unit* newWorker) 
{
	_workerUnits.insert(newWorker);
	// assign best mineral
	Unit* bestMineral = getBestMineral();
	_workersTarget[newWorker] = bestMineral;
	_workerState[newWorker] = Gathering_Mineral;
	// increase mineral exploitation
	_mineralsExploitation[bestMineral] = _mineralsExploitation[bestMineral]++;
}

void WorkerManager::onUnitDestroy(Unit* unit)
{
	WorkerToTargetMap::iterator found = _workersTarget.find(unit);
	if(found != _workersTarget.end()) {
		LOG4CXX_TRACE(_logger, "Deleting worker state: " << _workerState[unit]);
		Unit* workerTarget = found->second;
		if (workerTarget!=0 && workerTarget->getType().isMineralField())
			_mineralsExploitation[workerTarget] = _mineralsExploitation[workerTarget]--;
		if (_workerState[unit] == Gathering_Gas) {
			Unit* refinery = _workersTarget[unit];
			Unit* newWorker = 0;
			if (refinery!=0) {
				newWorker = getWorkerForTask(refinery->getPosition());
			} else {
				newWorker = getWorkerForTask(unit->getPosition());
			}
			if (newWorker!=NULL) {
				Unit* oldTarget = _workersTarget[newWorker];
				if (oldTarget!=0 && oldTarget->getType().isMineralField())
					_mineralsExploitation[oldTarget] = _mineralsExploitation[oldTarget]--;
				_workersTarget[newWorker] = refinery;
				_workerState[newWorker] = Gathering_Gas;
				newWorker->rightClick(refinery);
			}
			// if worker was building
			if (_workerBuildingRefinery.find(unit) != _workerBuildingRefinery.end()) {
				_workerBuildingRefinery.erase(unit);
				if (newWorker!=0) _workerBuildingRefinery.insert(newWorker);
			}
		} else if (_workerState[unit] == Building) {
			_workerBuildOrder.erase(unit); // TODO We lost the build request
		} else if (_workerState[unit] == Bunker_Support) {
			supportWorker();
		}
		_workersTarget.erase(unit);
		_workerState.erase(unit);
		_workerUnits.erase(unit);
	} else {
		// Look if the SquadManager has the SCV
		LOG4CXX_TRACE(_logger, "Deleting worker on squad");
		squadManager->onUnitDestroy(unit);
	}
}

void WorkerManager::onMineralDestroy(Unit* mineralField)
{
	LOG4CXX_TRACE(_logger, "Deleting mineral: " << mineralField->getID());
	ResourceToWorkerMap::iterator foundMineral = _mineralsExploitation.find(mineralField);
	if(foundMineral != _mineralsExploitation.end()) { // if is our mineral field
		// delete it
		_mineralsExploitation.erase(foundMineral);
		// reassign workers
		for (WorkerToTargetMap::iterator i = _workersTarget.begin(); i != _workersTarget.end(); ++i) {
			if (i->second == mineralField) {
				Unit* bestMineral = getBestMineral();
				_workersTarget[i->first] = bestMineral;
				if (bestMineral != 0) {
					i->first->rightClick(bestMineral);
					_mineralsExploitation[bestMineral] = _mineralsExploitation[bestMineral]++;
				} else {
					LOG4CXX_FATAL(_logger, "No more minerals");
				}
			}
		}

	}
}

Unit* WorkerManager::getBestMineral()
{
	int bestScore = 99;
	Unit* bestMineral = 0;

	for (ResourceToWorkerMap::iterator i = _mineralsExploitation.begin(); i != _mineralsExploitation.end(); ++i) {		
		if (i->second < bestScore) {
			bestScore = i->second;
			bestMineral = i->first;
		}
	}
	return bestMineral;
}

void WorkerManager::rebalanceGathering()
{
	//TODO
}

void WorkerManager::reassignRevomedMinerals()
{
	for(WorkerToTargetMap::iterator workerTarget=_workersTarget.begin();workerTarget!=_workersTarget.end();++workerTarget) {
		if (_mineralsExploitation.find(workerTarget->second) == _mineralsExploitation.end()) {
			Unit* bestMineral = getBestMineral();
			workerTarget->second = bestMineral;
			workerTarget->first->rightClick(bestMineral);
		}
	}
}

void WorkerManager::onFrame() 
{
	LOG4CXX_TRACE(_logger, "Update workers state");
	for(UnitSet::const_iterator worker=_workerUnits.begin();worker!=_workerUnits.end();++worker) {

		// auto-defense from workers
		//LOG4CXX_TRACE(_logger, "[WORKER] Auto-defense");
		if ( (*worker)->isUnderAttack() && !(*worker)->isAttacking() ) {
			UnitSet UnitsInRange = (*worker)->getUnitsInRadius(35);
			for(UnitSet::const_iterator i=UnitsInRange.begin();i!=UnitsInRange.end();++i) {
				if (Broodwar->self()->isEnemy((*i)->getPlayer()) && (*i)->getType().isWorker() ) {
					// if is contructing we need to stop first
					if ( (*worker)->isConstructing() ) { 
						(*worker)->stop();
						//Broodwar->printf("Stoping workers onFrame");
						return; 
					}
					(*worker)->attack(*i);
					_workerStartAttacking[(*worker)] = Broodwar->getFrameCount();
				}
			}
		}
		// If worker is attacking but target not, stop attacking.
		if ( (*worker)->isAttacking() && Broodwar->getFrameCount() - _workerStartAttacking[(*worker)] > 1*24 ) {
			Unit* target = (*worker)->getTarget();
			if ( target != NULL && !target->isAttacking() && !target->getType().isBuilding() ) {
				(*worker)->stop();
			}
		}

		if (_workerState[(*worker)] == Gathering_Mineral || _workerState[(*worker)] == Gathering_Gas) {  // ---------- WORKERS GATHERING -----------------------------
			//LOG4CXX_TRACE(_logger, "[WORKER] Gathering");
//  			Position q=(*worker)->getPosition();
//  			if (_workerState[(*worker)] == Gathering_Gas) {
// 				Broodwar->drawCircleMap(q.x(),q.y(),30,Colors::Green,false);
// 			} else {
// 				Broodwar->drawCircleMap(q.x(),q.y(),30,Colors::Cyan,false);
// 			}
			// check for idle workers
			if ((*worker)->getOrder() == Orders::PlayerGuard) {
				//Broodwar->printf("Idle Worker send to mineral id %d", workerTarget->getID());
				if (_workersTarget[(*worker)] != 0) {
					(*worker)->rightClick(_workersTarget[(*worker)]);
				} else {
					//DEBUG("[ERROR] No more minerals"); 
				}
			}
		} else if (_workerState[(*worker)] == Building) { // ---------- WORKERS BUILDING -----------------------------
			//LOG4CXX_TRACE(_logger, "[WORKER] Building");
			TilePosition buildPosition = _workerBuildOrder[(*worker)].first;
			UnitType buildType = _workerBuildOrder[(*worker)].second;
// 			if ("The Fortress 1.1" == Broodwar->mapName() && Broodwar->getFrameCount() - _workerStartBuilding[(*worker)] > 25*24) {
// 				Broodwar->drawCircleMap((*worker)->getPosition().x(),(*worker)->getPosition().y(),5*TILE_SIZE,Colors::Yellow,false);
// 				Broodwar->drawCircleMap(Position(buildPosition).x(),Position(buildPosition).y(),7*TILE_SIZE,Colors::Yellow,false);
// 				//Broodwar->printf("Worker %s speed %0.2f", (*worker)->getOrder().getName().c_str(), (*worker)->getVelocityX()+(*worker)->getVelocityY());
// 				if ((*worker)->getOrder() == Orders::Move || (*worker)->getOrder() == Orders::PlaceBuilding ) {
// 					tryMiningTrick(*worker);
// 				}
// 				if ((*worker)->getOrder() == Orders::MiningMinerals ) {
// 					(*worker)->move(Position(buildPosition));
// 				}
// 			} else
			if ( buildType == UnitTypes::Terran_Command_Center && Broodwar->getFrameCount() - _workerStartBuilding[(*worker)] > 50*24) {
				//Broodwar->printf("Change Command center location");
				informationManager->_ignoreBases.insert(buildPosition);
				if (informationManager->_expandType == InformationManager::Natural) {
					_workerBuildOrder[(*worker)].first = informationManager->getNaturalExpandPosition();
				} else {
					_workerBuildOrder[(*worker)].first = informationManager->getGasExpandPosition();
				}
				buildPosition = _workerBuildOrder[(*worker)].first;
				_workerStartBuilding[(*worker)] = Broodwar->getFrameCount();
				(*worker)->move(Position(buildPosition));
			}
			// Debug info
#ifndef TOURNAMENT
			Position q=(*worker)->getPosition();
 			Broodwar->drawCircleMap(q.x(),q.y(),30,Colors::Orange,false);
			Broodwar->drawLineMap(q.x(), q.y(), buildPosition.x()*TILE_SIZE, buildPosition.y()*TILE_SIZE, Colors::Orange);
			Broodwar->drawBoxMap(buildPosition.x()*TILE_SIZE, buildPosition.y()*TILE_SIZE,
								(buildPosition.x()+ buildType.tileWidth())*TILE_SIZE, (buildPosition.y()+ buildType.tileHeight())*TILE_SIZE, Colors::Orange);
// 			DEBUG("[TEST] worker state " << (*worker)->getOrder().getName() );
#endif
			
			if ((*worker)->getOrder() == Orders::PlayerGuard ||
				(*worker)->getOrder() == Orders::MiningMinerals ) {
				// if building finished change state to gather
				bool buildExist = false;
				UnitSet unitsOnPosition = Broodwar->getUnitsOnTile(buildPosition.x(), buildPosition.y());
				for(UnitSet::iterator u = unitsOnPosition.begin(); u != unitsOnPosition.end(); ++u) {
					if ((*u)->getType() == buildType) {
						buildExist = true;
					}
				}
				if (buildExist) {
					_workerState[(*worker)] = Gathering_Mineral;
					_workerBuildOrder.erase(*worker);
				} else { // else if we are not in position, move
					if (!Broodwar->isVisible(buildPosition) || !Broodwar->isVisible(buildPosition.x()+buildType.tileWidth(), buildPosition.y()+buildType.tileHeight())) {
						(*worker)->move(Position(buildPosition),true);
					} else { // else build
						bool ok = (*worker)->build(buildPosition, buildType);
						if (!ok) {
							LOG4CXX_ERROR(_logger, "[WORKER BUILDING] " << Broodwar->getLastError().toString());
							_workerState[(*worker)] = Gathering_Mineral;
							_workerBuildOrder.erase(*worker);
						}
					}
				}
			} else if ((*worker)->getOrder() == Orders::ConstructingBuilding) {
				if (buildType == UnitTypes::Terran_Supply_Depot && Broodwar->self()->visibleUnitCount(UnitTypes::Terran_Supply_Depot) == 1 ) {
					if (informationManager->startLocationCouldContainEnemy.size() > 1) {
						squadManager->newScoutSquad(*worker);
						workerManager->onUnitDestroy(*worker);
					}
				} else {
					_workerState[(*worker)] = Gathering_Mineral;
				}
				_workerBuildOrder.erase(*worker);
			} else if ((*worker)->getOrder() == Orders::Move && Broodwar->isVisible(buildPosition) && 
						Broodwar->isVisible(buildPosition.x()+buildType.tileWidth()-1, buildPosition.y()+buildType.tileHeight()-1)) {
				bool ok = (*worker)->build(buildPosition, buildType);
				if (!ok) {
					LOG4CXX_ERROR(_logger, "[WORKER BUILDING] " << Broodwar->getLastError().toString());
					_workerState[(*worker)] = Gathering_Mineral;
					_workerBuildOrder.erase(*worker);
				}
			} 
		} else if (_workerState[(*worker)] == Defending) { // ---------- WORKERS DEFENDING -----------------------------
			//LOG4CXX_TRACE(_logger, "[WORKER] Defending");
 			Position q=(*worker)->getPosition();
#ifndef TOURNAMENT
 			Broodwar->drawCircleMap(q.x(),q.y(),30,Colors::Red,false);
#endif
			// if out of the region or no enemy
			BWTA::Region* workerRegion = BWTA::getRegion( (*worker)->getTilePosition() );
			if (workerRegion != informationManager->home || (*worker)->getOrder() == Orders::PlayerGuard) {
				_workerState[(*worker)] = Gathering_Mineral;
				(*worker)->rightClick(_workersTarget[(*worker)]);
			}
		} else if (_workerState[(*worker)] == Bunker_Support) { // ---------- WORKERS BUNKER SUPPORT --------------------------------
			//LOG4CXX_TRACE(_logger, "[WORKER] Bunker support");
			if ((*worker)->getOrder() == Orders::PlayerGuard) {
				// first priority bunker
				if (informationManager->_lastBunker->getHitPoints()!=informationManager->_lastBunker->getType().maxHitPoints()) {
					(*worker)->repair(informationManager->_lastBunker); // TODO avoid spam
				} else {
				// second priority tanks
					UnitSet UnitsInRange = (*worker)->getUnitsInRadius(250);
					for(UnitSet::const_iterator i=UnitsInRange.begin();i!=UnitsInRange.end();++i) {
						Unit* nearUnit = *i;
						if ( nearUnit != NULL && (nearUnit->getType()==UnitTypes::Terran_Siege_Tank_Siege_Mode || nearUnit->getType()==UnitTypes::Terran_Siege_Tank_Tank_Mode) 
								&& nearUnit->getHitPoints()!=nearUnit->getType().maxHitPoints()) {
							(*worker)->repair(nearUnit); // TODO avoid spam
						}
					}
				}
			}
		}

		// Debug repair
// 		if ((*worker)->getOrder() == Orders::Repair) {
// 			Position q=(*worker)->getPosition();
// 			Position repairTarget = (*worker)->getTarget()->getPosition();
// 			Broodwar->drawCircleMap(q.x(),q.y(),30,Colors::Green,false);
// 			Broodwar->drawLineMap(q.x(), q.y(), repairTarget.x(), repairTarget.y(), Colors::Green);
// 		}
	}

	// check for complete Refinery in order to assign more workers -------------------------------------------------------
	LOG4CXX_TRACE(_logger, "Checking for complete Refineries");
	if (!_workerBuildingRefinery.empty()) {
		for(UnitSet::const_iterator worker=_workerBuildingRefinery.begin();worker!=_workerBuildingRefinery.end();) {
			if ( (*worker)->getOrder() != Orders::ConstructingBuilding ) {
				// refinery ready, assign workers
				Unit* refinery = _workersTarget[*worker];
				Unit* oldTarget;
				// worker 2
				Unit* newWorker = getWorkerForTask(refinery->getPosition());
				if (newWorker!=0) {
					oldTarget = _workersTarget[newWorker];
					_workersTarget[newWorker] = refinery;
					_workerState[newWorker] = Gathering_Gas;
					newWorker->rightClick(refinery);
					_mineralsExploitation[oldTarget] = _mineralsExploitation[oldTarget]--;
				}
				//worker 3
				newWorker = getWorkerForTask(refinery->getPosition());
				if (newWorker!=0) {
					oldTarget = _workersTarget[newWorker];
					_workersTarget[newWorker] = refinery;
					_workerState[newWorker] = Gathering_Gas;
					newWorker->rightClick(refinery);
					_mineralsExploitation[oldTarget] = _mineralsExploitation[oldTarget]--;
				}
				_gasExploitation[refinery] = 3;
				_workerBuildingRefinery.erase(worker++);
			} else {
				++worker;
			}
		}
	}

	// check for complete Command Center in order to add new mineral spots --------------------------------------------------
	LOG4CXX_TRACE(_logger, "Check completed command center");
	if (!informationManager->_basesUnderConstruction.empty()) {
		for(std::map<BWTA::BaseLocation*, BWAPI::Unit*>::iterator ourBase=informationManager->_basesUnderConstruction.begin();ourBase!=informationManager->_basesUnderConstruction.end();) {
			if ( ourBase->second->isCompleted() ) {
				// add new minerals spots
				for(UnitSet::const_iterator i=ourBase->first->getMinerals().begin();i!=ourBase->first->getMinerals().end();++i) {
					_mineralsExploitation[*i] = 0;
				}
				informationManager->_basesUnderConstruction.erase(ourBase++);
			} else {
				++ourBase;
			}
		}
	}


	// check for incomplete buildings ----------------------------------------------------------------------------------------
	LOG4CXX_TRACE(_logger, "Check incomplete buidings");
	UnitSet incompleteBuildings = Broodwar->self()->getUnits();
	for(UnitSet::const_iterator it=incompleteBuildings.begin();it!=incompleteBuildings.end();) {
		if ( !(*it)->isCompleted() && (*it)->getType().isBuilding() && !(*it)->isBeingConstructed() ) {
			++it;
		} else {
			incompleteBuildings.erase( it++ );
		}
	}
	if (!incompleteBuildings.empty()) {
		UnitSet UnitsInRange;
		bool noEnemies;
		Unit* workerToFinishBuild;
		for(UnitSet::const_iterator it=incompleteBuildings.begin();it!=incompleteBuildings.end();++it) {
			// if no enemy near
			UnitsInRange = (*it)->getUnitsInRadius(390);
			noEnemies = true;
			for(UnitSet::const_iterator i=UnitsInRange.begin();i!=UnitsInRange.end();++i) {
				if ( Broodwar->self()->isEnemy((*i)->getPlayer()) ) {
					noEnemies = false;
				}
			}
			if (noEnemies) {
				workerToFinishBuild = getWorkerForTask((*it)->getPosition());
				if (workerToFinishBuild!=0) workerToFinishBuild->rightClick(*it);
			}
		}
	}

	// check builds needing repair ----------------------------------------------------------------------------------------
	LOG4CXX_TRACE(_logger, "Check builds needing repair");
	UnitSet damagedBuildings = Broodwar->self()->getUnits();
	for(UnitSet::const_iterator it=damagedBuildings.begin();it!=damagedBuildings.end();) {
		if ( (*it)->isCompleted() && (*it)->getType().isBuilding() && (*it)->getHitPoints()!=(*it)->getType().maxHitPoints() ) {
			++it;
		} else {
			damagedBuildings.erase( it++ );
		}
	}
	if (!damagedBuildings.empty()) {
		UnitSet UnitsInRange;
		bool noEnemies, isBeingRepared;
		Unit* workerToRepairBuild;
		for(UnitSet::const_iterator it=damagedBuildings.begin();it!=damagedBuildings.end();++it) {
			// if no enemy near
			UnitsInRange = (*it)->getUnitsInRadius(390);
			noEnemies = true;
			isBeingRepared = false;
			for(UnitSet::const_iterator i=UnitsInRange.begin();i!=UnitsInRange.end();++i) {
				if ( Broodwar->self()->isEnemy((*i)->getPlayer()) ) {
					noEnemies = false;
				}
			}
			if (noEnemies) {
				// if no worker repairing
				for(UnitSet::const_iterator i=_workerUnits.begin();i!=_workerUnits.end();++i) {
					if ( (*i)->getTarget() == *it && (*i)->getOrder() == Orders::Repair) {
						isBeingRepared = true;
					}
				}
				if (!isBeingRepared) {
					#ifndef TOURNAMENT
						Broodwar->printf("SCV send to repair!");
					#endif
					workerToRepairBuild = getWorkerForTask((*it)->getPosition());
					if (workerToRepairBuild!=0) workerToRepairBuild->repair(*it);
				}
			}
		}
	}

	// compute minerals per second
// 	LOG4CXX_TRACE(_logger, "Compute stats");
// 	int currentFrameCount = Broodwar->getFrameCount();
// 	if ( currentFrameCount >= _lastFrameCount + 24 )
// 	{
// 		for(int i = 0;i<60;i++) _accumluatedMinerals[i]=_accumluatedMinerals[i+1];
// 		_accumluatedMinerals[60] = Broodwar->self()->gatheredMinerals();
// 
// 		_mineralPS = _accumluatedMinerals[60] - _accumluatedMinerals[59];
// 		_mineralPM = _accumluatedMinerals[60] - _accumluatedMinerals[0];
// 		_lastFrameCount = currentFrameCount;
// 		_averageMineralPS = (double) (_accumluatedMinerals[60] - 50) / ( double(Broodwar->getFrameCount()+1) / 24 );
// 		_averageMineralPM = (double) (_accumluatedMinerals[60] - 50) / ( double(Broodwar->getFrameCount()+1) / (24 * 60) );
// 	}
// 
// 
// 	//Display stats
// 	Broodwar->drawTextScreen(5,0,"Minerals in last second: %d   (Avg MPS: %f)", _mineralPS, _averageMineralPS);
// 	Broodwar->drawTextScreen(5,16,"Minerals in last minute: %d   (Avg MPM: %f)", _mineralPM, _averageMineralPM);
// 
// 	int i = 0;
// 	for (WorkerToTargetMap::iterator it = _workersTarget.begin(); it != _workersTarget.end(); ++it) {
// 		Broodwar->drawTextScreen(5,16*i,"Worker %p mining on %d", it->first, it->second->getID());
// 		++i;
// 	}
// 	int line = 0;
// 	for(UnitSet::const_iterator worker=_workerUnits.begin();worker!=_workerUnits.end();++worker) {
// 		Broodwar->drawTextScreen(100,13*line,"worker[%p] state: %d", *worker, _workerState[(*worker)] );
// 		++line;
// 	}
}

bool WorkerManager::needWorkers()
{
	return ( ( _mineralsExploitation.size() * WORKERS_PER_MINERAL ) > getWorkersMining() );
}

unsigned int WorkerManager::getWorkersMining()
{
	// TODO: Optimize calculating on add/delete worker on mineral spot
	int workersMining = 0;
	for(ResourceToWorkerMap::const_iterator mineral=_mineralsExploitation.begin();mineral!=_mineralsExploitation.end();++mineral) {
		workersMining += mineral->second;
	}
	return workersMining;
}

Unit* WorkerManager::getWorkerForTask(Position toPosition)
{
	// search worker closest to position
	int newDistance;
	int distance = 9999999;
	Unit *bestWorker = 0;
	int distanceWithoutMineral = 9999999;
	Unit *bestWorkerWithoutMineral = 0;
	for(UnitSet::const_iterator worker=_workerUnits.begin();worker!=_workerUnits.end();++worker) {
		if (_workerState[(*worker)] == Gathering_Mineral && (*worker)->isCompleted() && (*worker)->getOrder() != Orders::ConstructingBuilding) {
			newDistance = (*worker)->getPosition().getApproxDistance(toPosition);
			if ((*worker)->isCarryingMinerals()) {
				if (newDistance < distance) {
					distance = newDistance;
					bestWorker = *worker;
				}
			} else {
				if (newDistance < distanceWithoutMineral) {
					distanceWithoutMineral = newDistance;
					bestWorkerWithoutMineral = *worker;
				}
			}
		}
	}

	if (bestWorkerWithoutMineral != 0) {
		return bestWorkerWithoutMineral;
	} else if (bestWorker != 0) {
		return bestWorker;
	} else {
		LOG4CXX_ERROR(_logger, "We didn't find any worker for task");
		if (!_workerUnits.empty())
			if ( (_workersTarget.begin()->first)->exists() )
				return _workersTarget.begin()->first;
			else return bestWorker;
		else {
			LOG4CXX_FATAL(_logger, "Any worker for task!!!");
			//Broodwar->printf("Critical Error: Any worker for task!!!");
			return bestWorker;
		}
	}
}

int WorkerManager::getBuildingWorkers()
{
	int workers = 0;
	//DEBUG("[TEST] Checking workers orders");
	for (WorkerToTargetMap::iterator i = _workersTarget.begin(); i != _workersTarget.end(); ++i) {
		//DEBUG("Worker order: " << i->first->getOrder().getName() );
		if ( i->first->getOrder() == Orders::PlaceBuilding || i->first->getOrder() == Orders::ResetCollision) {
			++workers;
		}
	}
	return workers;
}

void WorkerManager::workerBuildingRefinery()
{
	for (WorkerToTargetMap::iterator i = _workersTarget.begin(); i != _workersTarget.end(); ++i) {
		Unit* worker = i->first;
		if ( worker->getOrder() == Orders::ConstructingBuilding && worker->getBuildUnit()->getType() == UnitTypes::Terran_Refinery ) {
			if ( std::find(_workerBuildingRefinery.begin(), _workerBuildingRefinery.end(), worker) == _workerBuildingRefinery.end() ) { // if we didn't get before
				// reassign future worker target
				Unit* oldTarget = i->second;
				Unit* newTarget = worker->getBuildUnit();
				_workersTarget[worker] = newTarget;
				_workerState[worker] = Gathering_Gas;
				_workerBuildOrder.erase(worker);
				_gasExploitation[newTarget] = 1;
				if (oldTarget->getType().isMineralField()) {
					_mineralsExploitation[oldTarget] = _mineralsExploitation[oldTarget]--;
				}
				_workerBuildingRefinery.insert(worker);
				return;
			}
		}
	}
}

bool WorkerManager::isUnderConstruction(UnitType type)
{
	//TODO improve with function Broodwar->self()->incompleteUnitCount(type)
	for (WorkerToTargetMap::iterator i = _workersTarget.begin(); i != _workersTarget.end(); ++i) {
		Unit* worker = i->first;
		if ( worker->getOrder() == Orders::ConstructingBuilding && worker->getBuildUnit()->getType() == type ) {
			return true;
		}
	}
	return false;
}

void WorkerManager::buildRequest(TilePosition locationToBuild, UnitType buildType)
{
	Position workerNear = Position(locationToBuild);
	// Map "The Fortress" has trap base locations
	if ("The Fortress 1.1" == Broodwar->mapName() && (buildType == UnitTypes::Terran_Command_Center || buildType == UnitTypes::Terran_Refinery) ) {
		workerNear = Position(Broodwar->self()->getStartLocation());
	}
	
	Unit* worker = getWorkerForTask(workerNear);
	if (worker==0) return;
	_workerState[worker] = Building;
	_workerStartBuilding[worker] = Broodwar->getFrameCount();
	_workerBuildOrder[worker] = std::make_pair(locationToBuild, buildType);
	worker->move(Position(locationToBuild));
	if (Broodwar->isVisible(locationToBuild) || Broodwar->isVisible(locationToBuild.x()+buildType.tileWidth()-1, locationToBuild.y()+buildType.tileHeight()-1)) {
		bool ok = worker->build(locationToBuild, buildType);
		if (!ok) { 
			LOG4CXX_ERROR(_logger, "[WORKER BUILD ORDER] " << Broodwar->getLastError().toString());
			_workerState[worker] = Gathering_Mineral;
			_workerBuildOrder.erase(worker);
		}
	}
}

bool WorkerManager::workersDefending()
{
	for(UnitSet::const_iterator worker=_workerUnits.begin();worker!=_workerUnits.end();++worker) {
		if (_workerState[(*worker)] == Defending) {
			return true;
		}
	}

	return false;
}

bool WorkerManager::workersDefending(Unit* target)
{
	//DEBUG("[WORKER DEFENDING] searching unit " << target);
	for(UnitSet::const_iterator worker=_workerUnits.begin();worker!=_workerUnits.end();++worker) {
		//DEBUG("[WORKER DEFENDING] worker state: " << _workerState[(*worker)] << " target: " << (*worker)->getTarget());
		Unit* targetSelected = (*worker)->getTarget();
		if (targetSelected == target) return true;
		if (_workerState[(*worker)] == Defending) {
			if (targetSelected == 0) return true;
			if (targetSelected->getType().isResourceContainer()) return true;
		}
	}

	return false;
}


void WorkerManager::defenseBase(Unit* unit)
{
	Unit* worker = getWorkerForTask(unit->getPosition());
	if (worker==0) return;
	_workerState[worker] = Defending;
	worker->attack(unit);
	_workerStartAttacking[worker] = Broodwar->getFrameCount();
}

void WorkerManager::tryMiningTrick(Unit* worker)
{
	TilePosition buildPosition = _workerBuildOrder[worker].first;
	UnitType buildType = _workerBuildOrder[worker].second;
	UnitSet UnitsInRange;
	if (buildType == UnitTypes::Terran_Command_Center) {
		UnitsInRange = worker->getUnitsInRadius(5*TILE_SIZE);
		int minerals = 0;
		for(UnitSet::const_iterator i=UnitsInRange.begin();i!=UnitsInRange.end();++i) {
			if ( (*i)->getType().isMineralField() ) minerals++;
		}
		//Broodwar->printf("Minerals in range %d", minerals);
		if (minerals > 2) return;
	} else {
		UnitsInRange = Broodwar->getUnitsInRadius(Position(buildPosition), 7*TILE_SIZE);
	}
	for(UnitSet::const_iterator i=UnitsInRange.begin();i!=UnitsInRange.end();++i) {
		if ( (*i)->getType().isMineralField() && buildPosition.getDistance((*i)->getTilePosition()) < buildPosition.getDistance(worker->getTilePosition()) &&
			(*i)->getTilePosition().getDistance(worker->getTilePosition()) > 2 ) {
				//Broodwar->printf("Mineral distance: %0.2f", (*i)->getTilePosition().getDistance(worker->getTilePosition()));
			worker->rightClick(*i);
		}
	}
}

void WorkerManager::supportWorker()
{
	Unit* newWorker = getWorkerForTask(informationManager->_initialRallyPosition);
	if (newWorker != NULL) {
		#ifndef TOURNAMENT
			Broodwar->printf("Worker send to bunker!!");
		#endif
		Unit* oldTarget = _workersTarget[newWorker];
		_workersTarget[newWorker] = NULL;
		_workerState[newWorker] = Bunker_Support;
		newWorker->move(informationManager->_initialRallyPosition);
		_mineralsExploitation[oldTarget] = _mineralsExploitation[oldTarget]--;
	}
}

int WorkerManager::workersSupporting()
{
	int number = 0;
	for(UnitSet::const_iterator worker=_workerUnits.begin();worker!=_workerUnits.end();++worker) {
		if (_workerState[(*worker)] == Bunker_Support) {
			number++;
		}
	}

	return number;
}

void WorkerManager::supportWorkerDone() 
{
	for(UnitSet::const_iterator worker=_workerUnits.begin();worker!=_workerUnits.end();++worker) {
		if (_workerState[(*worker)] == Bunker_Support) {
			// assign best mineral
			Unit* bestMineral = getBestMineral();
			_workersTarget[(*worker)] = bestMineral;
			_workerState[(*worker)] = Gathering_Mineral;
			// increase mineral exploitation
			_mineralsExploitation[bestMineral] = _mineralsExploitation[bestMineral]++;
			return;
		}
	}

}
