#include "NovaStdAfx.h"
#include "NovaAIModule.h"
#include "log4cxx/propertyconfigurator.h"
#include <log4cxx/helpers/pool.h>
#include <log4cxx/basicconfigurator.h>
#include <log4cxx/fileappender.h>
#include <log4cxx/simplelayout.h>
#include <log4cxx/logstring.h>
#include "Search/AbstractLayer.h"
#include <cstdlib>
#include <ctime>

using namespace BWAPI;

int myKillingScore = 0;
int enemyKillingScore = 0;

NovaAIModule::NovaAIModule()
	: leavingGame(0)
	, saidGG(false)
    , timeout(0)
{
    // use current time as seed for random generator
    std::srand((unsigned int)std::time(0)); 
}

void NovaAIModule::onStart()
{
    if (Broodwar->isReplay()) return;

    loadIniConfig();
	setUpLogging();

	enhancedUI = new EnhancedUI();
	informationManager = new InformationManager();
    informationManager->gameState.initFileVariables();

	Broodwar->enableFlag(Flag::UserInput);

#if defined(NOVA_GUI)
	Broodwar->enableFlag(Flag::CompleteMapInformation);
#endif

    if (HIGH_LEVEL_SEARCH) {
        Broodwar->enableFlag(Flag::CompleteMapInformation);
    }

    std::string enableGUI = LoadConfigString("general", "gui", "ON");
    if (enableGUI == "OFF") {
        Broodwar->setGUI(false); // to speed up (skips draw frames)
    }

    timeout = LoadConfigInt("general", "timeout");

	// check MICRO map
	char first = Broodwar->mapFileName()[0];
	ONLY_MICRO = false;
	PATHFINDING01 = false;
	usingCloackUnits = false;
	totalKitingFrames = 0;
	vulturesCreated = 0;
	vulturesKilled = 0;
	totalVulutreLife = 0;
	zealotsKilled = 0;

  	if ( first != '(' ) {
		ONLY_MICRO = true;
		//Broodwar->printf("Map name '%s'", Broodwar->mapFileName().c_str() );
		Broodwar->setLocalSpeed(50);
  	}
	if ( Broodwar->mapFileName().compare("path01.scx") == 0) {
		PATHFINDING01 = true;
	}
	if ( Broodwar->mapFileName().compare("kiting04.scm") == 0) {
		ONLY_MICRO = true;
		Broodwar->enableFlag(Flag::CompleteMapInformation);
		Broodwar->setLocalSpeed(0);
		// globals
		selfHitPoints = 0;
		selfMaxHitPoints = 0;
		enemyHitPoints = 0;
		enemyMaxHitPoints = 0;
		//microBullets = BWAPI::Bullet(null);
		numShots = 0;
	}

	// Initialize managers
	squadManager = new SquadManager();
	workerManager = new WorkerManager();
	plannerManager = new PlannerManager();
	productionManager = new ProductionManager();
	buildManager = new BuildManager();
	strategyManager = new StrategyManager(productionManager);
	

#ifdef TERRAIN_ANALYSIS
		Broodwar->setLocalSpeed(20);

		//Analyze Map
		BWTA::readMap();
		BWTA::analyze();
		informationManager->mapAnalyzed = true;

		BWTA::computeDistanceTransform();
		BWTA::buildChokeNodes();

// 		TilePosition start(12, 101);
// 		TilePosition target(109, 5);
// 		path = BWTA::getShortestPath2(start, target);

//		BWTA::balanceAnalysis();

		// Testing ground distance performance
// 		clock_t clockStart;
// 		clock_t clockEnd;
// 		double seconds;
// 		double distance1;
// 		int distance2;
// 
// 		// Old way
// 		clockStart = clock();
// 		for(int x = 0 ; x <= 10000 ; ++x) {
// 			distance1 = BWTA::getGroundDistance(start, target);
// 		}
// 		clockEnd = clock();
// 		seconds = double(clockEnd-clockStart)/CLOCKS_PER_SEC;
// 		LOG("[BWTA] Ground Distance seconds: " << seconds);
// 
// 		// New way
// 		clockStart = clock();
// 		for(int x = 0 ; x <= 10000 ; ++x) {
// 			distance2 = BWTA::getGroundDistance2(start, target);
// 		}
// 		clockEnd = clock();
// 		seconds = double(clockEnd-clockStart)/CLOCKS_PER_SEC;
// 		LOG("[BWTA] Ground Distance 2 seconds: " << seconds);
// 
// 		Position start2(start);
// 		Position target2(target);
// 
// 		clockStart = clock();
// 		for(int x = 0 ; x <= 10000 ; ++x) {
// 			distance1 = start2.getDistance(target2);
// 		}
// 		clockEnd = clock();
// 		seconds = double(clockEnd-clockStart)/CLOCKS_PER_SEC;
// 		LOG("[BWAPI] Distance seconds: " << seconds);
// 
// 		clockStart = clock();
// 		for(int x = 0 ; x <= 10000 ; ++x) {
// 			distance2 = start2.getApproxDistance(target2);
// 		}
// 		clockEnd = clock();
// 		seconds = double(clockEnd-clockStart)/CLOCKS_PER_SEC;
// 		LOG("[BWAPI] Approx Distance seconds: " << seconds);

#endif
	
	if (PATHFINDING01) {
		//Select SVC
		UnitSet::const_iterator i = Broodwar->self()->getUnits().begin();
		Unit* scv = *i;

		Broodwar->setLocalSpeed(20);
		Position target(19*TILE_SIZE, 87*TILE_SIZE);

		//Analyze Map
		BWTA::readMap();
		BWTA::analyze();
		informationManager->mapAnalyzed = true;

		//Init walkable map
		/*int mapW = Broodwar->mapWidth()*4;
		int mapH = Broodwar->mapHeight()*4;

		walkableMap = new int*[mapW];
		for(int x = 0 ; x < mapW ; ++x) {
			walkableMap[x] = new int[mapH];

			//Fill from static map
			for (int y = 0; y < mapH; ++y) {
				walkableMap[x][y] = (Broodwar->isWalkable(x, y))? WALKABLE : BLOCKED;
			}
		}

		//Block static neutral units
		for(UnitSet::iterator m = Broodwar->getStaticNeutralUnits().begin(); m != Broodwar->getStaticNeutralUnits().end(); ++m) {
			// get area
			MyRectangle area;
			area.x1 = (*m)->getTilePosition().x()*4;
			area.y1 = (*m)->getTilePosition().y()*4;
			area.x2 = (*m)->getTilePosition().x()*4 + (*m)->getType().tileWidth()*4;
			area.y2 = (*m)->getTilePosition().y()*4 + (*m)->getType().tileHeight()*4;
			// map area
			for (int x = area.x1; x <= area.x2; x++) {
				for (int y = area.y1; y <= area.y2; y++) {
					if (x >= 0 && x < mapW && y >= 0 && y < mapH) {
						walkableMap[x][y] = BLOCKED;
					}
				}
			}
		}

		//Build ChokeNodeGraph
		buildChokeNodes();

		//Get walkable path
		path = getShortestPath(WalkPosition(scv->getPosition()), WalkPosition(target));
*/
		
		scv->move(target);


	}else if (ONLY_MICRO) {
		// Set enemy start position
		std::set<TilePosition> enemyPosition = Broodwar->getStartLocations();
		for (std::set<TilePosition>::const_iterator it = enemyPosition.begin(); it != enemyPosition.end(); ++it) {
			if ( (*it).x() != Broodwar->self()->getStartLocation().x() || (*it).y() != Broodwar->self()->getStartLocation().y()) {
				informationManager->_enemyStartPosition = Position((*it).x()*TILE_SIZE, (*it).y()*TILE_SIZE);
				//DEBUG("Enemy Start Location: " << (*it).x() << "," << (*it).y() );
			}
		}
		// Add units to squad
		squadManager->newSquad(Broodwar->self()->getUnits());
		//DEBUG("New squad created");

		//testing merge squads
// 		SquadAgent* squad1;SquadAgent*  squad2;SquadAgent*  squad3;
// 		squad1 = squadManager->testSquad(Broodwar->getUnitsInRadius(Position(28*TILE_SIZE,25*TILE_SIZE),40));
// 		squad2 = squadManager->testSquad(Broodwar->getUnitsInRadius(Position(28*TILE_SIZE,29*TILE_SIZE),40));
// 		squad3 = squadManager->testSquad(Broodwar->getUnitsInRadius(Position(28*TILE_SIZE,39*TILE_SIZE),40));
// 
// 		squad2->inMerge(squad3);
// 		squad3->inMerge(squad2);
// 		squadManager->_squadsToMerge.insert(std::make_pair(squad2, squad3));
// 		squad3->inMerge(squad1);
// 		squad1->inMerge(squad3);
// 		squadManager->_squadsToMerge.insert(std::make_pair(squad3, squad1));

	} else {
        int gameSpeed = LoadConfigInt("general", "gameSpeed");
		Broodwar->setLocalSpeed(gameSpeed);


        std::string newThread = LoadConfigString("general", "analyze_map_thread", "ON");
#ifdef NOVA_GUI
        newThread = "OFF";
#endif
        if (newThread == "OFF" ) {
		    BWTA::readMap();
		    AnalyzeMapThread();
        } else {
		    informationManager->analyzeMap();
            //BWTA::readMap();
		    //AnalyzeMapThread();
        }
	}

#ifdef NOVA_GUI
	gameStateMutex = CreateMutex(NULL,FALSE,NULL); // nameless mutex object
	CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)GuiThread, NULL, 0, NULL);
#endif
}

NovaAIModule::~NovaAIModule()
{
	//TODO delete mangers
}

void NovaAIModule::onFrame()
{
    if (Broodwar->isReplay()) return;

#ifndef TOURNAMENT
	enhancedUI->onFrame();
#endif

//#ifdef HIGH_LEVEL_SEARCH
    // game timeout when high level search (20 minutes)
    if (timeout > 0 && Broodwar->getFrameCount() >= timeout) {
        Broodwar->leaveGame();
        return;
    }
//#endif

#ifdef TERRAIN_ANALYSIS
		// Draw path
// 		WalkPosition previousPosition(-1, -1);
// 		for each(const BWTA::Chokepoint* choke in path) {
// 			WalkPosition pos(choke->getCenter());
// 			if(previousPosition.x != -1 && previousPosition.y != -1)
// 				BWAPI::Broodwar->drawLineMap(pos.x * 8 + 4, pos.y * 8 + 4, previousPosition.x * 8 + 4, previousPosition.y * 8 + 4, BWAPI::Colors::Cyan);
// 
// 			previousPosition = pos;
// 		}
#endif

	if (PATHFINDING01){
		// Draw walkable tiles
		//int mapW = Broodwar->mapWidth()*4;
		//int mapH = Broodwar->mapHeight()*4;
		//for(int x=0; x < mapW; ++x) {
		//	for(int y=0; y < mapH; ++y) {
		//		if ( walkableMap[x][y] == WALKABLE) {
		//		//if ( Broodwar->isWalkable(x,y) ) {
		//			Broodwar->drawCircleMap(x*8+4,y*8+4,1,Colors::Green,true);
		//		} else {
		//			Broodwar->drawCircleMap(x*8+4,y*8+4,1,Colors::Red,true);
		//		}
		//	}
		//}

		// Draw path
		UnitSet::const_iterator i = Broodwar->self()->getUnits().begin();
		Unit* scv = *i;
		Position target(19*TILE_SIZE, 87*TILE_SIZE);
		WalkPosition previousPosition(-1, -1);
		for each(const BWTA::Chokepoint* choke in path) {
			WalkPosition pos(choke->getCenter());
			if(previousPosition.x != -1 && previousPosition.y != -1)
				BWAPI::Broodwar->drawLineMap(pos.x * 8 + 4, pos.y * 8 + 4, previousPosition.x * 8 + 4, previousPosition.y * 8 + 4, BWAPI::Colors::Cyan);
			else
				BWAPI::Broodwar->drawLineMap(pos.x * 8 + 4, pos.y * 8 + 4, scv->getPosition().x(), scv->getPosition().y(), BWAPI::Colors::Cyan);

			previousPosition = pos;
		}
		BWAPI::Broodwar->drawLineMap(target.x(), target.y(), previousPosition.x * 8 + 4, previousPosition.y * 8 + 4, BWAPI::Colors::Cyan);
	}

	if (ONLY_MICRO) {
		//DEBUG("squadManager");
		squadManager->onFrame();

// 		if (Broodwar->getFrameCount() == 1) {
// 			for(UnitSet::const_iterator it = Broodwar->self()->getUnits().begin();it!=Broodwar->self()->getUnits().end();it++) {
// 				selfMaxHitPoints += (*it)->getType().maxHitPoints() + (*it)->getType().maxShields();
// 			}
// 
// 			for(UnitSet::const_iterator it = Broodwar->enemy()->getUnits().begin();it!=Broodwar->enemy()->getUnits().end();it++) {
// 				enemyMaxHitPoints += (*it)->getType().maxHitPoints() + (*it)->getType().maxShields();
// 			}
// 		}
	} else {
		kitingFrame = false;
		informationManager->_frameMineralSpend = 0;
		informationManager->_frameGasSpend = 0;
		LOG4CXX_TRACE(_logger, "Starting Squad Manager");
		squadManager->onFrame();
		LOG4CXX_TRACE(_logger, "Starting Worker Manager");
		workerManager->onFrame();
		LOG4CXX_TRACE(_logger, "Starting Build Manager");
		buildManager->onFrame();
		LOG4CXX_TRACE(_logger, "Starting Planner Manager");
		plannerManager->onFrame();
		LOG4CXX_TRACE(_logger, "Starting Production Manager");
		productionManager->onFrame();
		LOG4CXX_TRACE(_logger, "Starting Strategy Manager");
		strategyManager->onFrame();
		if (kitingFrame) totalKitingFrames++;

        if (informationManager->_bbs && Broodwar->getFrameCount() == 400) {
            BWAPI::Unit * scv = workerManager->getWorkerForTask(BWAPI::Position(informationManager->_proxyPosition));
            scv->move(BWAPI::Position(informationManager->_proxyPosition));
        }

		//Be social
		if (Broodwar->getFrameCount() == 260)
			Broodwar->sendText("gl hf");

		if (leavingGame == 0 && Broodwar->self()->visibleUnitCount(UnitTypes::Terran_SCV) < 3) {
			leavingGame = BWAPI::Broodwar->getFrameCount();
		}
		if(leavingGame != 0) {
			if(BWAPI::Broodwar->getFrameCount() - leavingGame > 24 && !saidGG) {
				saidGG = true;
				BWAPI::Broodwar->sendText("gg");
			} else if(BWAPI::Broodwar->getFrameCount() - leavingGame > 80)
				BWAPI::Broodwar->leaveGame();
		}
	}

#ifdef NOVA_GUI
// 	if (informationManager->mapAnalyzed && (Broodwar->getFrameCount() % GAME_STATE_REFRESH == 0) && !Broodwar->isPaused()) {
// 		WaitForSingleObject(gameStateMutex,INFINITE); // wait for ownership
// 		informationManager->gameState.updateGameState();
// 		ReleaseMutex(gameStateMutex);
// 		informationManager->_GUIsignal->emitGameStateChanged();
// 	}
#endif
}

void NovaAIModule::onSendText(std::string text)
{
	if (Broodwar->isReplay()) return;

	if (text=="p") Broodwar->setLocalSpeed(200);
	if (text=="/bo") PRINT_BUILD_ORDER = !PRINT_BUILD_ORDER;
	if (text=="/bm") PRINT_BUILD_MAP = !PRINT_BUILD_MAP;
	if (text=="/dps1") {
		PRINT_GROUND_DPS = !PRINT_GROUND_DPS;
		Broodwar->printf("Ground DPS map");
	}
	if (text=="/dps2") {
		PRINT_AIR_DPS = !PRINT_AIR_DPS;
		Broodwar->printf("Air DPS map");
	}
	if (text=="/production") PRINT_PRODUCTION = !PRINT_PRODUCTION;
    if (text=="/region") PRINT_REGION_ID_MAP = !PRINT_REGION_ID_MAP;
	if (text=="/search") {
		informationManager->_searchAndDestroy = true;
		Broodwar->printf("Search and Destroy activated!");
	}
	if (text=="/panic") {
		informationManager->_panicMode = true;
		Broodwar->printf("Panic mode activated!");
	}

    std::vector<std::string> parsed = splitString(text, ' ');

	if (parsed[0] == "/size") {
		if (parsed.size() > 1)
			informationManager->_minSquadSize = atoi(parsed[1].c_str());
		Broodwar->printf("Minim squad size: %i", informationManager->_minSquadSize);
    }
}

void NovaAIModule::onReceiveText(BWAPI::Player* player, std::string text)
{
  //Broodwar->printf("%s said '%s'", player->getName().c_str(), text.c_str());
}

void NovaAIModule::onNukeDetect(BWAPI::Position target)
{
//   if (target!=Positions::Unknown)
//     Broodwar->printf("Nuclear Launch Detected at (%d,%d)",target.x(),target.y());
//   else
//     Broodwar->printf("Nuclear Launch Detected");
}

void NovaAIModule::onUnitDiscover(BWAPI::Unit* unit)
{
	if (Broodwar->isReplay()) return;
	//DEBUG("UNIT DISCOVER (" << unit << ") " << unit->getType().getName() << " [" << unit->getPlayer()->getName() << "]"); 
}

void NovaAIModule::onUnitEvade(BWAPI::Unit* unit)
{
	if (Broodwar->isReplay()) return;
	//DEBUG("UNIT EVADE (" << unit << ") " << unit->getType().getName() << " [" << unit->getPlayer()->getName() << "]"); 
}

void NovaAIModule::onUnitShow(BWAPI::Unit* unit)
{
	if (Broodwar->isReplay()) return;
    if (unit->getType().isSpell()) return; // ignore spells
	//DEBUG("UNIT SHOW (" << unit << ") " << unit->getType().getName() << " [" << unit->getPlayer()->getName() << "]"); 
	if (!ONLY_MICRO) {
		if ( Broodwar->self() == unit->getPlayer() ) {
			if ( unit->getType() == UnitTypes::Terran_Vulture_Spider_Mine ) return; // skip spider mines
			if (unit->getType().isBuilding()) {
				productionManager->onBuildingShow(unit);
				buildManager->constructionPlaced(unit);
				if (!unit->getType().isAddon()) {
					informationManager->removeReservedMinerals(unit->getType().mineralPrice());
					informationManager->removeReservedGas(unit->getType().gasPrice());
				}
				if ( unit->getType() == UnitTypes::Terran_Command_Center ) informationManager->onCommandCenterShow(unit);
				if ( unit->getType() == UnitTypes::Terran_Missile_Turret ) informationManager->onMissileTurretShow(unit);
			} else if ( unit->getType() == UnitTypes::Terran_SCV )
				workerManager->addUnit(unit);
			else {
				if ( unit->getType() == UnitTypes::Terran_Vulture ) vulturesCreated++;
				plannerManager->rebalanceProduction();
				squadManager->unitTraining(unit);
			}
		}
	}

		if (Broodwar->self()->isEnemy(unit->getPlayer())) {
			// debug info
			if (firstUnit.length() == 0) {
				firstUnit = unit->getType().getName();
				frameFirstUnit = Broodwar->getFrameCount();
			}

			squadManager->newEnemy(unit);
			if ( unit->getType().isResourceDepot() ) {
				informationManager->onEnemyResourceDepotShow(unit);
			}
			if (!unit->getType().isWorker() && !informationManager->_firstPush) {
				#ifndef TOURNAMENT
					Broodwar->printf("First Push Detected");
				#endif
				informationManager->_firstPush = true;
			}
			if (Broodwar->enemy()->getRace() != Races::Zerg) {
				if (unit->getType().isFlyer()) informationManager->_turretDefenses = true;
			}
			if ( unit->getType() == UnitTypes::Terran_Vulture_Spider_Mine && !informationManager->_scienceVesselDetector) {
				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);
				}
			}
		}
	//}
	//DEBUG("UNIT SHOW (END)"); 
}

void NovaAIModule::onUnitComplete(Unit *unit) 
{
	if ( Broodwar->self() == unit->getPlayer() && unit->getType() == UnitTypes::Terran_Bunker ) {
		workerManager->supportWorker();
		squadManager->newBunkerSquad(unit);
	}
}

void NovaAIModule::onUnitHide(BWAPI::Unit* unit)
{
	if (Broodwar->isReplay()) return;
	//DEBUG("UNIT HIDE (" << unit << ") " << unit->getType().getName() << " [" << unit->getPlayer()->getName() << "]"); 

	if ( Broodwar->self()->isEnemy(unit->getPlayer())||
		(unit->getPlayer()->isNeutral() && unit->getType().isAddon()) ) {
		// remove from squad target
		squadManager->onEnemyEvade(unit);
	}
}

void NovaAIModule::onUnitCreate(BWAPI::Unit* unit)
{
	//DEBUG("UNIT CREATE (" << unit << ") " << unit->getType().getName() << " [" << unit->getPlayer()->getName() << "]"); 
	if (Broodwar->isReplay()) return;
//	Broodwar->printf("Unit create");
}

void NovaAIModule::onUnitDestroy(BWAPI::Unit* unit)
{
	if (Broodwar->isReplay()) return;
    if (unit->getType().isSpell()) return; // ignore spells

	//DEBUG("UNIT DESTROY (" << unit << ") " << unit->getType().getName() << " [" << unit->getPlayer()->getName() << "]"); 
	if (Broodwar->self() == unit->getPlayer()) { // self unit
        if (!unit->getType().isBuilding()) {
            enemyKillingScore += unit->getType().destroyScore();
        }
		if (unit->getType().isBuilding()) {
			productionManager->onBuildingDestroy(unit);
			buildManager->onBuildingDestroy(unit);
			if ( unit->getType() == UnitTypes::Terran_Command_Center ) informationManager->onCommandCenterDestroy(unit);
			if ( unit->getType() == UnitTypes::Terran_Missile_Turret ) informationManager->onMissileTurretDestroy(unit);
			if ( unit->getType() == UnitTypes::Terran_Bunker ) squadManager->onUnitDestroy(unit);
		} else if ( unit->getType() == UnitTypes::Terran_SCV )
			workerManager->onUnitDestroy(unit);
		else {
			if ( unit->getType() == UnitTypes::Terran_Vulture ) {
				vulturesKilled++;
				totalVulutreLife += Broodwar->getFrameCount() - squadManager->getUnitFrameCreated(unit);
			}
			plannerManager->rebalanceProduction();
			squadManager->onUnitDestroy(unit);
		}
	} else if (Broodwar->self()->isEnemy(unit->getPlayer())) { // enemy unit
        if (!unit->getType().isBuilding()) {
            myKillingScore += unit->getType().destroyScore();
        }
		squadManager->onEnemyDestroy(unit);
		if ( unit->getType().isResourceDepot() ) {
			informationManager->onEnemyResourceDepotDestroy(unit);
		}
		if ( unit->getType() == UnitTypes::Protoss_Zealot ) zealotsKilled++;
	} else if (unit->getType().isMineralField()) {
		workerManager->onMineralDestroy(unit);
		// TODO free space on buildMap (buildManager)
	} else if (unit->getType().isAddon()) { // Neutral addon, could be self or enemy addon
		productionManager->onBuildingDestroy(unit);
		squadManager->onEnemyDestroy(unit);
	}
	//DEBUG("UNI DESTROY (END)"); 
}

void NovaAIModule::onUnitMorph(BWAPI::Unit* unit)
{
	if (Broodwar->isReplay()) return;
	//Broodwar->printf("Unit morph");

	if ( Broodwar->self() == unit->getPlayer() ) {
		if (unit->getType() == UnitTypes::Terran_Refinery) {
			buildManager->refineryPlaced(unit);
			informationManager->removeReservedMinerals(unit->getType().mineralPrice());
			informationManager->removeReservedGas(unit->getType().gasPrice());
		}
	} else if (unit->getType().isRefinery()) {
		strategyManager->checkGasSteal(unit);
		squadManager->newEnemy(unit);
	} else if (unit->getType() == UnitTypes::Resource_Vespene_Geyser) {
		//Broodwar->printf("Enemy refinery destroyed");
		squadManager->onEnemyDestroy(unit);
	}

}

void NovaAIModule::onUnitRenegade(BWAPI::Unit* unit)
{
	if (Broodwar->isReplay()) return;
	//Broodwar->printf("Unit renegade");
}

void NovaAIModule::onEnd(bool isWinner)
{
	// TODO close GUI

// 	if (ONLY_MICRO) {
// 		for(UnitSet::const_iterator it = Broodwar->self()->getUnits().begin();it!=Broodwar->self()->getUnits().end();it++) {
// 			selfHitPoints += (*it)->getHitPoints() + (*it)->getShields();
// 		}
// 		
// 		for(UnitSet::const_iterator it = Broodwar->enemy()->getUnits().begin();it!=Broodwar->enemy()->getUnits().end();it++) {
// 			enemyHitPoints += (*it)->getHitPoints() + (*it)->getShields();
// 		}
// 
// 		LOG(Broodwar->elapsedTime() << "," << selfHitPoints << "," << selfMaxHitPoints << "," << enemyHitPoints << "," << enemyMaxHitPoints <<
// 			"," << Broodwar->self()->getUnits().size() << "," << Broodwar->enemy()->getUnits().size() << "," << Broodwar->mapFileName());
// 	}
	//LOG(Broodwar->getFrameCount() << "," << totalKitingFrames << "," << frameFirstUnit << "," << firstUnit << "," << usingCloackUnits << "," << isWinner << "," <<
	//	vulturesCreated << "," << vulturesKilled << "," << totalVulutreLife << "," << Broodwar->self()->getUnitScore() << "," << zealotsKilled << "," << Broodwar->self()->getKillScore());
	//if (!TOURNAMENT) {
	//	LOG(Broodwar->getFrameCount() << "," << isWinner);
		//fileLog.close();
	//}

    LOG("Frames: "<<Broodwar->getFrameCount() << " winner: " << isWinner);


    // print evaluation function result from current game state
    // --------------------------------------------------------
    AbstractLayer search; // clean and add enemies in the game state

    // translate squads to high level squads into game state
    for(SquadSet::const_iterator squad=squadManager->_squads.begin();squad!=squadManager->_squads.end();++squad) {
        // ignore scout
        if ( (*squad)->_state == SquadAgent::Scout ) continue;
        search.addSquadToGameState(*squad);
    }
    LOG("EvaluationCurrentState: " << search.getEvaluation() << " myKillScore: " << Broodwar->self()->getKillScore() << " " << myKillingScore << " enemyKillScore: " << Broodwar->enemy()->getKillScore() << " " << enemyKillingScore );
    if (HIGH_LEVEL_SEARCH) {
        // print stats
        // --------------------------------------------------------
        LOG("Avg stats per group (groupSize, frequency, AvgTime, AvgMinBranch, AvgMaxBranch, AvgAvgBranch, AvgTiemouts, AvgDownSamplings");
        std::map<unsigned int, double>::const_iterator groupTimeIt = stats.groupTime.begin();
        std::map<unsigned int, unsigned long>::const_iterator groupFrequencyIt = stats.groupFrequency.begin();
//     std::map<unsigned int, double>::const_iterator groupBranchingMinIt = stats.groupBranchingMin.begin();
//     std::map<unsigned int, double>::const_iterator groupBranchingMaxIt = stats.groupBranchingMax.begin();
//     std::map<unsigned int, double>::const_iterator groupBranchingAvgIt = stats.groupBranchingAvg.begin();
//     std::map<unsigned int, unsigned long>::const_iterator groupTiemoutsIt = stats.groupTimeouts.begin();
//     std::map<unsigned int, unsigned long>::const_iterator groupDownSamplingsIt = stats.groupDownSamplings.begin();
    for(unsigned int i=0;i<stats.groupTime.size();++i) {
        LOG((*groupFrequencyIt).first << "," <<
        (*groupFrequencyIt).second << "," <<
        (*groupTimeIt).second/(*groupFrequencyIt).second);
//        (*groupTimeIt).second/(*groupFrequencyIt).second << "," <<
//         (*groupBranchingMinIt).second/(*groupFrequencyIt).second << "," <<
//         (*groupBranchingMaxIt).second/(*groupFrequencyIt).second << "," <<
//         (*groupBranchingAvgIt).second/(*groupFrequencyIt).second << "," <<
//         (*groupTiemoutsIt).second/(*groupFrequencyIt).second << "," <<
//         (*groupDownSamplingsIt).second/(*groupFrequencyIt).second);

        groupTimeIt++;
        groupFrequencyIt++;
//         groupBranchingMinIt++;
//         groupBranchingMaxIt++;
//         groupBranchingAvgIt++;
//         groupTiemoutsIt++;
//         groupDownSamplingsIt++;
    }
    
        LOG("Orders: "<<stats.orders << " overwritten: " << stats.ordersOverwritten);
    }

    // Check I/O files for learning
    // save winning strategy
    if (isWinner) {
        //int index = BWTA::getStartLocations().size() - 2;
        int index = Broodwar->getStartLocations().size() - 2;
        informationManager->learningData[index+informationManager->strategySelected]++;
    }

    std::string writeFilename = LoadConfigString("learning", "write", "");

    // 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 );
    writeFilename += nameEnemy + ".txt";

    std::ofstream writeFile( (char*)writeFilename.c_str(), std::ofstream::trunc );

    writeFile << ++informationManager->gamesSaved << std::endl;
    for( int i = 0; i < informationManager->learningDataSize; ++i ) {
        writeFile << informationManager->learningData[i] << std::endl;
		writeFile.flush();
	}
    writeFile.close();
    delete informationManager->learningData;
}

void NovaAIModule::setUpLogging()
{
    std::ostringstream config;
    std::string algorithm = LoadConfigString("high_level_search", "algorithm", "ABCD");
    config << algorithm;
    if (algorithm == "ABCD") {
        config << " max_depth: " << LoadConfigInt("ABCD", "depth", 1);
        config << " downsampling: " << LoadConfigInt("ABCD", "downsampling");
        config << " time_limit: " << LoadConfigInt("ABCD", "time_limit", 1);
    } else if (algorithm == "MCTSCD") {
        config << " max_depth: " << LoadConfigInt("MCTSCD", "depth", 1);
        config << " max_iterations: " << LoadConfigInt("MCTSCD", "iterations");
        config << " max_simulation_time: " << LoadConfigInt("MCTSCD", "max_simulation_time");
    }
    std::ostringstream abstraction;
    abstraction << "space_partition: " << LoadConfigString("high_level_search", "space_partition", "REGIONS_AND_CHOKEPOINTS");
    abstraction << " buildings: " << LoadConfigString("high_level_search", "buildings", "RESOURCE_DEPOT");

	//if (!TOURNAMENT) {
		// Open log file. Comment this when migration to log4cxx is done
		//fileLog.open("bwapi-data\\logs\\NovaAIModule.log");
		fileLog.open("bwapi-data\\logs\\NovaAIModule.log", std::ios_base::app); //append the output
        LOG("==========================================================================");
        LOG("                               NEW GAME                                   ");
        if (HIGH_LEVEL_SEARCH) LOG(abstraction.str());
        if (HIGH_LEVEL_SEARCH) LOG(config.str());
        LOG("==========================================================================");

		_logger = log4cxx::Logger::getLogger("Nova");
		log4cxx::PropertyConfigurator::configure("bwapi-data/AI/logger.config");

		LOG4CXX_INFO(_logger, "Logging set up!");
	//}
}

void NovaAIModule::loadIniConfig()
{
    TCHAR currentPath[MAX_PATH];
    GetCurrentDirectory(MAX_PATH, currentPath);
    configPath = std::string(currentPath) + "\\bwapi-data\\AI\\Nova.ini";

    std::string highLevel = LoadConfigString("high_level_search", "high_level_search", "OFF");
    if (highLevel == "ON") {
        HIGH_LEVEL_SEARCH = true;
    }
    
}