#include "OffenseMonitor.h"
#include "Overseer.h"
#include "Army.h"
//#include "Windows.h"
#include <iostream>
#include <fstream>

OffenseMonitor::OffenseMonitor( Overseer* ov )
{
	targetEval = new TargetEvaluator(ov->getUnitManager());
	overseer = ov;
	memChunk = 0;
	attacking = false;
	baseSquadSize = 8;
	std::set<BWTA::Region*> r = BWTA::getRegions();
	for(std::set<BWTA::Region*>::iterator i = r.begin(); i != r.end(); ++i) {
		BWTA::Region* cur = *i;
		battleMap.insert(pair<BWTA::Region*, Army*>(cur, NULL));
	}
	MIN_FORCE_SIZE = 8;
	knownMainForce = NULL;
	enabled = true;
}

void OffenseMonitor::evaluate()
{
	if(!enabled)  {
		return;
	}
	//	if(Broodwar->getFrameCount()%8 == 0) {
	// so look at all of the units I have, and theorize - if they were an army, what would their strength be?
	std::vector<UnitModel*> unitModels = overseer->getUnitManager()->getFriendlyMilitaryUnits();
	BWTA::Region* ebr = overseer->getUnitManager()->getBelievedEnemyMainBaseRegion();
	if(ebr == NULL) {
		return;
	}	
	if(unitModels.empty()) {
		return;
	}	
	// so now i have my army, what is it's strength
	Army* theoreticalArmy = getTheoreticalArmy();
	if(theoreticalArmy != NULL) {
		float armyval = targetEval->calculateEnemyArmyValueOf(ebr);
		float regionval = targetEval->calculateValueOf(ebr);

		////Broodwar->drawTextScreen(32,64, "strength of region: %f", regionval);
		////Broodwar->drawTextScreen(32,80,"strength of army against region: %f", armyval);

		////Broodwar->drawTextScreen(32,96,"army val: %f", targetEval->calculateArmyValueOf(theoreticalArmy));
		////Broodwar->drawTextScreen(32,112,"defenders val: %f", targetEval->calculateEnemyArmyValueOf(ebr));
		////Broodwar->drawTextScreen(32,128,"val disparity: %f", abs(targetEval->calculateArmyValueOf(theoreticalArmy)-targetEval->calculateEnemyArmyValueOf(ebr)));
		////Broodwar->drawTextScreen(32,144,"attack when %f is > %f", (targetEval->calculateArmyValueOf(theoreticalArmy)*0.9), targetEval->calculateEnemyArmyValueOf(ebr));
		////Broodwar->drawTextScreen(32,160,"base squad size: %d ", baseSquadSize);

		//}
		reccomend();
	}
}

bool OffenseMonitor::isRegionUnderAttack(BWTA::Region* r)
{
	Army* supportingArmy = battleMap[r];

	if(supportingArmy != NULL && supportingArmy->isAlive()){
		if(supportingArmy->getCurrentTarget() == r) {
			return true;
		}
	}
	std::set<BWTA::Region*> regs = overseer->getOffenseMonitor()->getConnectedRegions(r);
	for (std::set<BWTA::Region*>::iterator i = regs.begin(); i != regs.end(); ++i)
	{
		Army* rarmy = battleMap[(*i)];
		if(rarmy != NULL) {
			if(rarmy->isAlive() && rarmy->getCurrentTarget() == (*i)) {
				return true;
			} else {
				battleMap[(*i)] = NULL;
			}
		}
	}
	return false;
}


bool OffenseMonitor::reccomend()
{	
	if(Broodwar->getFrameCount()%64 == 0) {


		BWTA::Region* ebr = overseer->getUnitManager()->getBelievedEnemyMainBaseRegion();
		BWTA::Region* bestBase = NULL;
		std::set<BWTA::Region*> regions = BWTA::getRegions();
		int bval = 500000000;
		int knownEnemyPositions = 0;
		for (std::set<BWTA::Region*>::iterator i = regions.begin(); i != regions.end(); ++i)
		{
			BWTA::Region* cur = *i;
			if(!cur->isReachable(overseer->getUnitManager()->getFriendlyMainBase()->getBaseRegion())) {
				continue;
			}
			if(overseer->getUnitManager()->isBlackListed(cur)) {
				continue;
			}

			std::set<BWTA::Region*> regs = overseer->getOffenseMonitor()->getConnectedRegions(cur);
			int cval = overseer->getUnitManager()->valueRegion(cur);
			if(cval == 0) {
				continue;
			}
			
			
				bool underAssault = false;
				if(isRegionUnderAttack((*i))) {
					underAssault = true;
					break;
				}
				if(underAssault) {
					continue;
				}
				knownEnemyPositions++;
				if(cval < bval) {
					bval = cval;
					bestBase = cur;
				}
			
		}
		if(bestBase != NULL) {
			ebr = bestBase;
		}

		if(ebr == NULL) {
			// we don't know where the enemy base is yet, so lets just not make any predictions

			return false;
		}
		float DAMAGE_DISPARITY_THRESH = 0.9;
		Army* theoreticalArmy = getTheoreticalArmy();	
		float enemyDamage =  targetEval->calculateEnemyArmyValueOf(ebr);
		float myDamage = targetEval->calculateArmyValueOf(theoreticalArmy);

		bool rightSize = false;
		if(theoreticalArmy->getMembers().size() >= MIN_FORCE_SIZE) {
			rightSize = true;
			//////Broodwar->sendText("theoretical army size: %d", theoreticalArmy->getMembers().size());
		}
		if(!rightSize) {
			return false;
		}
		//ofstream myfile;
		//myfile.open ("vars.txt", ios::app);
		//myfile << Broodwar->getFrameCount() << "\t" << myDamage << "\t" << enemyDamage << "\t" << fabs(myDamage-enemyDamage) << "\n";
		//myfile.close();

		int timeShaping = Broodwar->getFrameCount();



		InformationCell* enemyBaseCell = overseer->getUnitManager()->getMapInformationSystem()->getBaseCellAt(ebr);

		if(enemyBaseCell == NULL) {
			////Broodwar->sendText("base cell null");
			return false;
		}

		float informationValue = enemyBaseCell->getInformationValue();
		int lastVisited = enemyBaseCell->getLastVisited();

		int visible = 0;
		if(informationValue == 0) {
			visible = 10000;
		}

		// modulate these up/down over time
		float RECENCY_THRESH = 720;
		float ATTACK_CONF_VAL = 0.65;
		if(Broodwar->getFrameCount()-lastVisited <= RECENCY_THRESH) {
		} else {
			//	return false;
		}

		bool damageAdvantage = false;
		//myDamage = myDamage/theoreticalArmy->getMembers().size();
		if(myDamage > enemyDamage && myDamage*DAMAGE_DISPARITY_THRESH >= enemyDamage) {
			damageAdvantage = true;
		}

		bool recentlyScouted = false;
		if(abs(Broodwar->getFrameCount()-lastVisited) <= Broodwar->getFrameCount()-RECENCY_THRESH) {
			recentlyScouted = true;
		}

		if(damageAdvantage && recentlyScouted) {
			//////Broodwar->sendText("reccomending attack!");
			reccomendationMemory[memChunk] = 1;
		} else {
			reccomendationMemory[memChunk] = 0;
		}

		memChunk++;
		if(memChunk >= 15) {
			memChunk = 0;
		}

		float val = 0;
		for(int i = 0; i < 15; i++) {
			val+=reccomendationMemory[i];
		}
		if(val >= ATTACK_CONF_VAL) {

			if(battleMap[ebr] == NULL) {

				// so here we know if there is an army out already

				Army* armyInForce = NULL;
				bool shouldSpawnSupportSquad = false;
				int numMainArmies = 0;
				int numSupportForces = 0;
						std::vector<Army*> support = overseer->getUnitManager()->getActiveArmies();
						for (std::vector<Army*>::iterator i = support.begin(); i != support.end(); ++i)
						{
							Army* supportingArmy = *i;
							if(supportingArmy == NULL) {

							} else {
								if(supportingArmy->isAlive()){
									if(!supportingArmy->isSupportSquad()) {
										numMainArmies++;
										armyInForce = supportingArmy;
										knownMainForce = armyInForce;
									} else {
										numSupportForces++;
									}

								}
							}
						}
					 
			
				// lets not just spam lots of armies around!
			if(numMainArmies != 0) {
				if(numMainArmies == 1 && numSupportForces >= 2) {
					return false;
				}
				if(numMainArmies == 1 && numSupportForces < 2) {
					// ok, so if we know there's an army out in force, we can only field SUPPORTING armies of a certain size
					float mainForceSize = armyInForce->getInitialForceSize();
					int supportForceSize = (int)(mainForceSize*0.5);
					if(knownEnemyPositions >= 1 && !armyInForce->isContainingForce() && numSupportForces <= 1) {
						shouldSpawnSupportSquad = true;
					}
				}
			}

				////Broodwar->sendText("launching attack!!");
				Army* grabbedArmy;
				if(baseSquadSize == 0) {
					grabbedArmy = overseer->getUnitManager()->grabArmy();
					////Broodwar->sendText("grabbing anything i can, wound up with: %d units", grabbedArmy->getMembers().size());
				} else {
					if(!shouldSpawnSupportSquad) {
						////Broodwar->sendText("spawning a MAIN ARMY of at least size... %d", baseSquadSize);
						grabbedArmy = overseer->getUnitManager()->grabArmy(baseSquadSize);
						knownMainForce = grabbedArmy;
					} else {
						if(numMainArmies == 1) {
							numMainArmies = 2;
						}
						if(numMainArmies == 2) {
							numMainArmies = 3;
						}
						////Broodwar->sendText("spawning a SUPPORT FORCE of at least size... %d", baseSquadSize/numMainArmies);
						grabbedArmy = overseer->getUnitManager()->grabArmy(baseSquadSize/numMainArmies);
						grabbedArmy->setSupportSquad();

					}
					if(grabbedArmy == NULL) {
						////Broodwar->sendText("got null back");
						return false;
					}
					////Broodwar->sendText("instantiating army of size %d", grabbedArmy->getMembers().size());
				}

				if(grabbedArmy != NULL) {
					

					if(baseSquadSize == 0) {
						baseSquadSize = grabbedArmy->getMembers().size();
					} else {
						baseSquadSize += 6;
						if(baseSquadSize >= 20) {
							baseSquadSize = 20;
						}
					}
				} else {
					return false;
				}
			} else {
				Army* curAttackingForce = battleMap[ebr];
				if(curAttackingForce != NULL) {
					if(curAttackingForce->isAlive()) {

					} else {
						////Broodwar->sendText("erasing army");
						battleMap[ebr] = NULL;
					}
				}
			}
		}

	}
	return false;
}

Army* OffenseMonitor::getTheoreticalArmy()
{
	std::vector<UnitModel*> unitModels = overseer->getUnitManager()->getFriendlyMilitaryUnits();
	if(unitModels.empty()) {
		return NULL;
	}	

	Army* theoreticalArmy = new Army(overseer);
	////Broodwar->drawTextScreen(32,48, "---army vals---");

	for (vector<UnitModel*>::iterator i = unitModels.begin(); i != unitModels.end(); ++i)
	{
		UnitModel* cur = *i;
		if(!cur->isAlive() || cur->getArmyOwner() != NULL || !cur->getUnit()->isCompleted()) {
			continue;
		}
		int curTask = cur->getCurrentTaskType();
		if(cur->getType() == UnitTypes::Protoss_Archon || cur->getType() == UnitTypes::Protoss_High_Templar || cur->getType() == UnitTypes::Protoss_Shuttle || cur->getType() == UnitTypes::Protoss_Arbiter || cur->getType() == UnitTypes::Protoss_Reaver || cur->getType() == UnitTypes::Protoss_Zealot || cur->getType() == UnitTypes::Protoss_Dragoon) {
			if(curTask == NO_TASK || curTask == GUARD) {
				theoreticalArmy->addToArmyDirect(cur);
			}
		}
	}
	if(theoreticalArmy->getMembers().empty()) {
		return NULL;
	}
	return theoreticalArmy;
}

BWTA::Region* OffenseMonitor::reccomendWeakestTarget()
{
	std::set<BWTA::Region*> regions = BWTA::getRegions();
	int bestValSoFar = -100000;
	BWTA::Region* bestRegion = NULL;
	for (std::set<BWTA::Region*>::iterator i = regions.begin(); i != regions.end(); ++i)
	{
		BWTA::Region* r = *i;
		if(!r->isReachable(overseer->getUnitManager()->getFriendlyMainBase()->getBaseRegion())) {
			continue;
		}
		if(overseer->getUnitManager()->isBlackListed(r)) {
			continue;
		}
		int regionScore = overseer->getUnitManager()->valueRegion(r);
		if(regionScore < bestValSoFar) {
			bestRegion = r;
		}
	}
	return bestRegion;
}

bool OffenseMonitor::regionsAreAdjacent( BWTA::Region* a, BWTA::Region* b )
{
	if(a == NULL || b == NULL) {
		return false;
	} 
	std::set<BWTA::Chokepoint*> cpa = a->getChokepoints();
	std::set<BWTA::Chokepoint*> cpb = b->getChokepoints();
	//if(a == b) {
	//	return true;
	//}
	if(a->getBaseLocations().empty() || b->getBaseLocations().empty()){
		return false;
	}
	for(std::set<BWTA::Chokepoint*>::const_iterator i = cpa.begin(); i != cpa.end(); i++) {
		BWTA::Chokepoint* cp = *i;
		std::pair<BWTA::Region*, BWTA::Region*> rs = cp->getRegions();
		if(rs.second == b) {
			return true;
		}
	} 
	return false;
}

std::set<BWTA::Region*> OffenseMonitor::getConnectedRegions( BWTA::Region* a )
{
	std::set<BWTA::Region*> connectedRegions;
	if( a == NULL) {
		return connectedRegions;
	}
	std::set<BWTA::Region*> allRegions = BWTA::getRegions();
	for(std::set<BWTA::Region*>::const_iterator i = allRegions.begin(); i != allRegions.end(); i++) {
		//if(a == (*i)) {
		//	continue;
		//}
		if((*i)->getBaseLocations().empty()) {
			continue;
		}
		if(regionsAreAdjacent(a, (*i))) {
			connectedRegions.insert((*i));
		}
	}
	return connectedRegions;
}

void OffenseMonitor::registerTarget( Army* a, BWTA::Region* r )
{
	std::set<BWTA::Region*> allRegions = BWTA::getRegions();
	for(std::set<BWTA::Region*>::const_iterator i = allRegions.begin(); i != allRegions.end(); i++) {

		if(battleMap[(*i)] == a) {
			battleMap[*i] == NULL;
		}
	}
	battleMap[r] = a;
}

void OffenseMonitor::enable()
{
	enabled = true;
}

void OffenseMonitor::disable()
{
	enabled = false;
}
