#include "NovaStdAfx.h"
#include "StrategyManager.h"
#include "BuildManager.h"
#include "SquadManager.h"

using namespace BWAPI;

StrategyManager::StrategyManager(ProductionManager *productionManager)
	: _logger(log4cxx::Logger::getLogger("StrategyManager"))
{
	_lastSweepFrame.clear();
	_productionManager = productionManager;
	_StateMachine = 0;
	_liftBuildings.clear();
	_hiddingCorners = true;

	// Marine rush
//	informationManager->_percentList[UnitTypes::Terran_Marine] = 100;

	// Marine+Medic rush
// 	informationManager->_percentList[UnitTypes::Terran_Marine] = 80;
// 	informationManager->_percentList[UnitTypes::Terran_Medic] = 20;
// 	informationManager->researchRequest(TechTypes::Stim_Packs);
//	informationManager->researchRequest(TechTypes::Optical_Flare);
// 	informationManager->upgradeRequest(UpgradeTypes::U_238_Shells);
// 	informationManager->upgradeRequest(UpgradeTypes::Terran_Infantry_Weapons);
// 	informationManager->buildRequest(UnitTypes::Terran_Comsat_Station, true);

	//BBS
// 	informationManager->buildRequest(UnitTypes::Terran_Barracks);
// 	informationManager->buildRequest(UnitTypes::Terran_Barracks);
// 	informationManager->buildRequest(UnitTypes::Terran_Supply_Depot);
// 	informationManager->_percentList[UnitTypes::Terran_Marine] = 100;
// 	informationManager->_useProxyPosition = true;
// 	informationManager->_bbs = true;
//     informationManager->_minSquadSize = 7;

	// Vulture rush
// 	informationManager->_percentList[UnitTypes::Terran_Vulture] = 100;
// 	informationManager->upgradeRequest(UpgradeTypes::Ion_Thrusters);
// 	informationManager->upgradeRequest(UpgradeTypes::Terran_Vehicle_Weapons, 3);
// 	informationManager->upgradeRequest(UpgradeTypes::Terran_Vehicle_Plating, 3);

	// Tank rush
// 	informationManager->_percentList[UnitTypes::Terran_Siege_Tank_Tank_Mode] = 100;
// 	informationManager->researchRequest(TechTypes::Tank_Siege_Mode);

	// Tank + dropship rush
// 	informationManager->_percentList[UnitTypes::Terran_Siege_Tank_Tank_Mode] = 90;
// 	informationManager->_percentList[UnitTypes::Terran_Dropship] = 10;
// 	informationManager->researchRequest(TechTypes::Tank_Siege_Mode);

	// Wraith rush
// 	informationManager->_percentList[UnitTypes::Terran_Wraith] = 100;
// 	informationManager->upgradeRequest(UpgradeTypes::Terran_Ship_Weapons, 3);
// 	informationManager->buildRequest(UnitTypes::Terran_Control_Tower, true);

	
  	/*_StateMachine = new StateMachine<StrategyManager>(this);
	if (Broodwar->enemy()->getRace() == Races::Zerg) {
		//_StateMachine->ChangeState(TwoPortWraith::Instance());
		_StateMachine->ChangeState(OneRaxFE::Instance());
	} else if (Broodwar->enemy()->getRace() == Races::Protoss) {
		//_StateMachine->ChangeState(OneRaxFE::Instance());
		//_StateMachine->ChangeState(TwoFactMines::Instance());
		_StateMachine->ChangeState(TwoFactTanks1::Instance());
	} else {
  		_StateMachine->ChangeState(OneFactory::Instance());
	}*/

    // Check I/O files for learning--------------------------------------
    std::string readFilename = LoadConfigString("learning", "read", "");

    // Get the opponent name and transform it in lower case
    std::string nameEnemy = Broodwar->enemy()->getName();
	std::transform( nameEnemy.begin(), nameEnemy.end(), nameEnemy.begin(), ::tolower );

    readFilename += nameEnemy + ".txt";

    // for now we consider 3 different strategies
    int sizeStrategies = 3;
    // max number of start locations * number of strategies
    informationManager->learningDataSize = 8 * sizeStrategies;
    informationManager->learningData = new int[informationManager->learningDataSize];

    std::ifstream readFile( (char*)readFilename.c_str(), std::ifstream::in );

	if( readFile.peek() != std::ifstream::traits_type::eof() ) {
        int i = 0;
        readFile >> informationManager->gamesSaved;
        while( readFile >> informationManager->learningData[i]) {
			++i;
        }
	} else { // If the read-file is empty use default data
        for( int i = 0; i < informationManager->learningDataSize; ++i ) {
			informationManager->learningData[i] = 0;
        }
	}

    // Select the best stragety using e-greedy ----------------------------
    //int index = BWTA::getStartLocations().size() - 2;
    int index = Broodwar->getStartLocations().size() - 2;
    float randomNumber = (float)(std::rand() % 100) / (float)100;
    //LOG("RandomnNumber: " << randomNumber);
    float epsilon = 1.0f / (1 + informationManager->learningData[index] +
                            informationManager->learningData[index+1] +
                            informationManager->learningData[index+2]);

    if (randomNumber < epsilon ) { // select random
        informationManager->strategySelected = std::rand() % 3;
        //LOG("Random Strategy: " << informationManager->strategySelected);
    } else { // select best strategy
        //LOG("Learning Data (" << index << "): " << informationManager->learningData[index] << "," << informationManager->learningData[index+1] << "," << informationManager->learningData[index+2]);
        if (informationManager->learningData[index+2] > informationManager->learningData[index] &&
            informationManager->learningData[index+2] > informationManager->learningData[index+1] ) {
            informationManager->strategySelected = 2;
        } else if (informationManager->learningData[index] > informationManager->learningData[index+1] &&
            informationManager->learningData[index] > informationManager->learningData[index+2] ) {
            informationManager->strategySelected = 0;
        } else {
            informationManager->strategySelected = 1;
        }
        //LOG("Best Strategy: " << informationManager->strategySelected);
    }

    // Inisit strategy-----------------------------------------------------
    if (informationManager->strategySelected == 0) { //BBS
	    informationManager->buildRequest(UnitTypes::Terran_Barracks);
	    informationManager->buildRequest(UnitTypes::Terran_Barracks);
	    informationManager->buildRequest(UnitTypes::Terran_Supply_Depot);
	    informationManager->_percentList[UnitTypes::Terran_Marine] = 100;
	    informationManager->_useProxyPosition = true;
	    informationManager->_bbs = true;
        informationManager->_minSquadSize = 7;
    } else {
        _StateMachine = new StateMachine<StrategyManager>(this);
        if (Broodwar->enemy()->getRace() == Races::Zerg) {
            if (informationManager->strategySelected == 1)  _StateMachine->ChangeState(OneRaxFE::Instance());
            else                        _StateMachine->ChangeState(TwoPortWraith::Instance());
	    } else if (Broodwar->enemy()->getRace() == Races::Protoss) {
            if (informationManager->strategySelected == 1)  _StateMachine->ChangeState(TwoFactTanks1::Instance());
            else                        _StateMachine->ChangeState(TwoFactMines::Instance());
	    } else {
            if (informationManager->strategySelected == 1)  _StateMachine->ChangeState(OneFactory::Instance());
            else                        _StateMachine->ChangeState(TwoPortWraith::Instance());
	    }
    }


};

StrategyManager::~StrategyManager()
{
	if (_StateMachine!=0) delete _StateMachine;
}

void StrategyManager::onFrame()
{
	LOG4CXX_TRACE(_logger, "Update state machine");
	if (_StateMachine!=0) _StateMachine->Update();
    else if (informationManager->_bbs) {
        // enable auto suplies
        if (!informationManager->_autoBuildSuplies && Broodwar->self()->visibleUnitCount(UnitTypes::Terran_Supply_Depot) > 0)
            informationManager->_autoBuildSuplies = true;
    }

	// Check reactive events ------------------------------------------------------
	// ************** Detected cloaked Units? ********************
	LOG4CXX_TRACE(_logger, "Handle cloaked units");
	if (!informationManager->_cloakedEnemyPositions.empty())
		handleCloakedEnemy();

	// ************** Gas steal in our base? **************
	LOG4CXX_TRACE(_logger, "Gas steal in our base?");
	if (_productionManager->_commandCenters.size() == 1) {
		for(UnitSet::const_iterator enemy=informationManager->_seenEnemies.begin();enemy!=informationManager->_seenEnemies.end();++enemy) {
			if ( (*enemy)->getType().isWorker() ) {
				// check if is in the same region of a base
				BWTA::Region* workerRegion = BWTA::getRegion( (*enemy)->getTilePosition() );
				UnitSet::const_iterator base=_productionManager->_commandCenters.begin();
				BWTA::Region* baseRegion = BWTA::getRegion( (*base)->getTilePosition() );
				if (workerRegion == baseRegion) {
					// if we don't have Refinery try to build it
					if (Broodwar->self()->completedUnitCount(UnitTypes::Terran_Refinery) == 0)
						informationManager->criticalBuildRequest(UnitTypes::Terran_Refinery);

					if (!workerManager->workersDefending(*enemy)) {
						workerManager->defenseBase(*enemy);
					}
				}
			}
		}
	}

	// ************** Defense base from zergling rush and scout **************
	LOG4CXX_TRACE(_logger, "Defense base from zergling rush and scout");
	UnitSet allEnemiesSeen;
	for(UnitSet::const_iterator eunit = informationManager->_seenEnemies.begin();eunit!=informationManager->_seenEnemies.end();eunit++) allEnemiesSeen.insert(*eunit);
	for(SquadSet::const_iterator squadIter=squadManager->_squads.begin();squadIter!=squadManager->_squads.end();++squadIter) {
		for(UnitSet::const_iterator eunit = (*squadIter)->_enemies.begin();eunit!=(*squadIter)->_enemies.end();eunit++) allEnemiesSeen.insert(*eunit);
	}
	for(UnitSet::const_iterator enemy=allEnemiesSeen.begin();enemy!=allEnemiesSeen.end();++enemy) {
		if ( (*enemy)!=0 && (*enemy)->getType() != UnitTypes::Unknown ) {
			BWTA::Region* enemyRegion = BWTA::getRegion( (*enemy)->getTilePosition() );
			if (enemyRegion == informationManager->home && !workerManager->workersDefending(*enemy)) { 
				if ((*enemy)->getType() == UnitTypes::Zerg_Zergling ) {
					workerManager->defenseBase(*enemy);
					workerManager->defenseBase(*enemy);
				} else if ((*enemy)->getType().isWorker()) {
					workerManager->defenseBase(*enemy);
				}
			}
		}
	}

	// ************** scan empty bases for enemy **************
	LOG4CXX_TRACE(_logger, "Scan empty bases for enemy");
	informationManager->scanBases();

	// ************** need anti air units? **************
	LOG4CXX_TRACE(_logger, "Need anti air units?");
	double timeToKillEnemyAir = (informationManager->_enemyAirHP>0)? (informationManager->_ourAirDPS == 0)? 99999 : informationManager->_enemyAirHP/informationManager->_ourAirDPS : 0;
	//double timeToKillAntiAir = (totalSelfAirHP>0)? (totalEnemyAirDPS == 0 )? 99999 : totalSelfAirHP/totalEnemyAirDPS : 0;
	// if enemy is Zerg check for mutalsik
	if (Broodwar->enemy()->getRace() == Races::Zerg) {
		// TODO count mutalisk vs wraith
	} else {
		if (timeToKillEnemyAir > 5) {
			informationManager->_needAntiAirUnits = true;
			informationManager->checkRequirements(UnitTypes::Terran_Goliath);
			informationManager->upgradeRequest(UpgradeTypes::Charon_Boosters);
		} else {
			informationManager->_needAntiAirUnits = false;
		}
	}

	// ********** Search and destroy ********
	LOG4CXX_TRACE(_logger, "Search and destroy conditions");
	if ( !informationManager->_searchAndDestroy &&
		Broodwar->isVisible(TilePosition(informationManager->_enemyStartPosition)) && squadManager->_leadSquad != 0 && squadManager->_leadSquad->_state == SquadAgent::GetPosition &&
		squadManager->_leadSquad->_center.getApproxDistance(squadManager->_leadSquad->_positionTarget) < 200 ) {
		informationManager->_searchAndDestroy = true;
		Broodwar->printf("Search and Destroy activated!");
	}
	if (informationManager->_searchAndDestroy) {
		if (Broodwar->self()->visibleUnitCount(UnitTypes::Terran_Starport)==0 && !buildManager->alreadyBuilding(UnitTypes::Terran_Starport)) {
			informationManager->buildRequest(UnitTypes::Terran_Science_Facility, true);
			informationManager->buildRequest(UnitTypes::Terran_Starport, true);
		}
	}

	// ********** Panic mode ********
// 	if ( !informationManager->_panicMode && Broodwar->self()->visibleUnitCount(UnitTypes::Terran_SCV) < 3) {
// 		informationManager->_panicMode = true;
// 		Broodwar->printf("Panic mode activated!");
// 	}
	if (informationManager->_panicMode) {
		if (_liftBuildings.size() == 0) { // search buildings that can lift
			UnitSet allUnits = Broodwar->self()->getUnits();
			for(UnitSet::const_iterator i = allUnits.begin(); i!=allUnits.end(); ++i) {
				if ((*i)->getType().isFlyingBuilding() && !(*i)->isLifted()) {
					_liftBuildings.insert(*i);
					if ((*i)->isTraining()) (*i)->cancelTrain();
					if ((*i)->isResearching()) (*i)->cancelResearch();
					if ((*i)->isUpgrading()) (*i)->cancelUpgrade();
				}
			}
		} else { // hide buildings
			for(UnitSet::const_iterator i = _liftBuildings.begin(); i!=_liftBuildings.end();) {
				if (!(*i)->isLifted()) { // check if we are lifted
					(*i)->lift(); 
					++i;
				} else if (!(*i)->isIdle()) {
					// search best position to hide (map corners or middle corners)
					Position myBase = BWTA::getStartLocation(Broodwar->self())->getPosition();
					Position enemyBase = informationManager->_enemyStartPosition;
					std::set<BWAPI::Position> hiddingPositions;
					double newDistance;
					Position bestPlace;
					if (_hiddingCorners) {
						bestPlace = Position(0,0);
						hiddingPositions.insert(Position(Broodwar->mapWidth()*TILE_SIZE,0));
						hiddingPositions.insert(Position(0,Broodwar->mapHeight()*TILE_SIZE));
						hiddingPositions.insert(Position(Broodwar->mapWidth()*TILE_SIZE,Broodwar->mapHeight()*TILE_SIZE));
						_hiddingCorners = false;
					} else {
						bestPlace = Position(Broodwar->mapWidth()*TILE_SIZE,Broodwar->mapHeight()*TILE_SIZE/2);
						hiddingPositions.insert(Position(Broodwar->mapWidth()*TILE_SIZE/2,0));
						hiddingPositions.insert(Position(0,Broodwar->mapHeight()*TILE_SIZE/2));
						hiddingPositions.insert(Position(Broodwar->mapWidth()*TILE_SIZE/2,Broodwar->mapHeight()*TILE_SIZE));
						_hiddingCorners = true;
					}
					double bestDistance = bestPlace.getDistance(myBase);
					bestDistance += bestPlace.getDistance(enemyBase);

					for(std::set<BWAPI::Position>::iterator pos=hiddingPositions.begin();pos!=hiddingPositions.end();++pos) {
						newDistance = (*pos).getDistance(myBase);
						newDistance += (*pos).getDistance(enemyBase);
						if (newDistance > bestDistance) {
							bestDistance = newDistance;
							bestPlace = (*pos);
						}
					}

					(*i)->move(bestPlace);
					++i;
				} else {
					_liftBuildings.erase(++i);
				}
			}
		}
	}

// 	Broodwar->drawTextScreen(290,52,"Enemy Air DPS: %0.2f", informationManager->_enemyAirDPS);
// 	Broodwar->drawTextScreen(290,62,"Time Kill Enemy Air: %0.2f", timeToKillEnemyAir);
// 	Broodwar->drawTextScreen(290,72,"Barrack Production: %s", informationManager->_trainOrder[UnitTypes::Terran_Barracks].getName().c_str());
// 	Broodwar->drawTextScreen(290,82,"Factory Production: %s", informationManager->_trainOrder[UnitTypes::Terran_Factory].getName().c_str());
// 	Broodwar->drawTextScreen(290,92,"Starport Production: %s", informationManager->_trainOrder[UnitTypes::Terran_Starport].getName().c_str());

	// State info
	if (_StateMachine!=0) {
		Broodwar->drawTextScreen(437,17,"%cState: %c%s", 0x07, 0x03, _StateMachine->GetCurrentState()->GetName());
	}
	//Broodwar->drawTextScreen(437,17,"M: %d G: %d", informationManager->minerals(), informationManager->gas());
}

void StrategyManager::handleCloakedEnemy() {
	if (!informationManager->_comsatStation.empty() && !informationManager->_cloakedEnemyPositions.empty()) {
		for (UnitToTimeMap::iterator unit = informationManager->_comsatStation.begin(); unit != informationManager->_comsatStation.end(); ++unit) {
			// TODO select best position (at the moment we only get the first)
			TilePositionSet::iterator posIter = informationManager->_cloakedEnemyPositions.begin();
			TilePosition pos = *posIter;

			if (_lastSweepFrame.find(pos) != _lastSweepFrame.end()) {
				if (Broodwar->getFrameCount() - _lastSweepFrame[pos] < SCANNER_SWEEP_FREQUENCY) {
					break;
				}
			}

			// Use scanner sweep if possible
			if (unit->first->getEnergy() >= 50) {
				bool scanned = informationManager->useScanner(unit->first, Position(pos));
				if (scanned) {
					_lastSweepFrame[pos] = Broodwar->getFrameCount();
					break;
				}
			}
		}
	} else if ( !buildManager->alreadyBuilding(UnitTypes::Terran_Comsat_Station) ) { // Build Comsat Station
		informationManager->criticalBuildRequest(UnitTypes::Terran_Comsat_Station);
	}

	// Execute Science Vessel plan
	informationManager->_scienceVesselDetector = true;
	if (Broodwar->self()->visibleUnitCount(UnitTypes::Terran_Starport)==0 && !buildManager->alreadyBuilding(UnitTypes::Terran_Starport)) {
		informationManager->criticalBuildRequest(UnitTypes::Terran_Science_Facility, true);
		informationManager->criticalBuildRequest(UnitTypes::Terran_Starport, true);
		informationManager->criticalBuildRequest(UnitTypes::Terran_Factory, true);
	}

	// Execute missile turret defense plan
	if (Broodwar->enemy()->getRace() != Races::Zerg) {
		informationManager->_turretDefenses = true;
	} else if (informationManager->_enemyMutalisk > 3) {
		informationManager->_turretDefenses = true;
	}

	// Clear notifications
	informationManager->_cloakedEnemyPositions.clear();
}

void StrategyManager::checkGasSteal(Unit* unit)
{
	if (_productionManager->_commandCenters.size() == 1) {
		// check if is in the same region of a base
		BWTA::Region* refineryRegion = BWTA::getRegion( unit->getTilePosition() );
		UnitSet::const_iterator base=_productionManager->_commandCenters.begin();
		BWTA::Region* baseRegion = BWTA::getRegion( (*base)->getTilePosition() );
		if (refineryRegion == baseRegion) {
			Broodwar->sendText("Bastard you stole my gas!");
			// send two worker to destroy it
			Unit* worker = workerManager->getWorkerForTask(unit->getPosition());
			if (worker!=NULL) worker->attack(unit);
			worker = workerManager->getWorkerForTask(unit->getPosition());
			if (worker!=NULL) worker->attack(unit);
			
			//Broodwar->printf("Unit event: %s", unit->getType().getName().c_str());
			TilePosition geyserLocation = unit->getTilePosition();
			// check if refinery is already build it
			bool refineryExist = false;
			UnitSet unitsOnGeyser = BWAPI::Broodwar->getUnitsOnTile(geyserLocation.x(), geyserLocation.y());
			
			if (Broodwar->self()->completedUnitCount(UnitTypes::Terran_Barracks) == 0 && Broodwar->self()->incompleteUnitCount(UnitTypes::Terran_Barracks) == 0 )
				informationManager->criticalBuildRequest(UnitTypes::Terran_Barracks);
		}
	}
}