#include "SCAILCore.h"
using namespace BWAPI;
#include <iostream>
#include <fstream>
#include <ctime>
#include <sys/timeb.h>
#include "../tinyxml.h"
//#include "Windows.h"
bool analyzed;
bool analysis_just_finished;
BWTA::Region* home;
BWTA::Region* enemy_base;
std::vector<BWAPI::TilePosition> testPath;
Overseer* globalOverseer;

int tf = 0;
int ts = 0;
int tt = 0;

int longestFrame = 0;
int gid = 0;

void UOBBotCore::onStart()
{
	
	Broodwar->enableFlag(Flag::UserInput);
	////Broodwar->sendText("show me the money");
	Broodwar->setLocalSpeed(0);

  Broodwar->sendText("-- SCAIL active, initializing...");
  analyzed=false;
  analysis_just_finished=false;
  BWTA::readMap();
  BWTA::analyze();
  analyzed = true;
  globalOverseer = new Overseer();
  

  show_bullets=false;
  show_visibility_data=false;
  
  //////Broodwar->sendText("Hdurf");

   // Broodwar->printf("The match up is %s v %s",
   // Broodwar->self()->getRace().getName().c_str(),
   // Broodwar->enemy()->getRace().getName().c_str());

    //send each worker to the mineral field that is closest to it
 
    for(std::set<Unit*>::const_iterator i=Broodwar->self()->getUnits().begin();i!=Broodwar->self()->getUnits().end();i++)
    {
		if((*i)->getType() == Broodwar->self()->getRace().getCenter()) {
			(*i)->train(Broodwar->self()->getRace().getWorker());
		}

      if ((*i)->getType().isWorker() && !(*i)->isGatheringMinerals() && !(*i)->isMoving())
      {
		 // ////Broodwar->sendText("found worker");
		  // Find the closest mineral patch
		  // Declare some variables to help with obtaining the closest unit
		  Unit *closest = NULL;
		  int   closestDistance = 999999;
		  for ( std::set<Unit*>::iterator m = Broodwar->getMinerals().begin(); m != Broodwar->getMinerals().end(); ++m )
		  {
			  int newDistance = (*i)->getDistance(*m);

			  // Continue iterating if the new distance is greater than the old distance
			  if ( newDistance >= closestDistance )
				  continue;

			  // Save our distance information if it is closer
			  closestDistance = newDistance;
			  closest = *m;
		  }
		  // If a mineral patch was found
		  if ( closest )
		  {
			  // Order our worker to harvest the patch!
			  (*i)->gather(closest);
			  // Continue iterating other units so we don't interrupt this order and produce unnecessary commands
			  continue;
			}
    }
	}

}

void UOBBotCore::onEnd(bool isWinner)
{
  int winloss = 0;
  int myscore = Broodwar->self()->getUnitScore()+Broodwar->self()->getRazingScore()+Broodwar->self()->getKillScore()+Broodwar->self()->getBuildingScore();
  int enemyscore = Broodwar->enemy()->getUnitScore()+Broodwar->enemy()->getRazingScore()+Broodwar->enemy()->getKillScore()+Broodwar->enemy()->getBuildingScore();

  if (isWinner)
  {
    winloss = 1;
  } else {
	 
	  if(Broodwar->getFrameCount() >= 86400) {
		  if(myscore > enemyscore) {
			  winloss = 1;
		  }
	  }
  
  }
  globalOverseer->getScoreManager()->registerNewGameResult(Broodwar->enemy()->getName().c_str(), globalOverseer->getCurrentHighLevelStrat(), winloss, myscore, make_pair(Broodwar->mapWidth(), Broodwar->mapHeight()));

  ofstream myfile;
  myfile.open ("results.txt", ios::app);
  myfile << Broodwar->getFrameCount() << "\t \t" << Broodwar->self()->getBuildingScore() << "\t \t" << winloss << "\n";
  myfile.close();

  ofstream sumfile;
  sumfile.open ("framelogsummary.txt", ios::app);
  sumfile << "id: " << gid << "\t" << Broodwar->getFrameCount() << "\t \t" << " f: " << tf << " s: " << ts << " t: " << tt << "\n";
  sumfile.close();
  gid++;
   tf = 0;
   ts = 0;
   tt = 0;


}

int getMilliCount(){
	timeb tb;
	ftime(&tb);
	int nCount = tb.millitm + (tb.time & 0xfffff) * 1000;
	return nCount;
}

int getMilliSpan(int nTimeStart){
	int nSpan = getMilliCount() - nTimeStart;
	if(nSpan < 0)
		nSpan += 0x100000 * 1000;
	return nSpan;
}

void UOBBotCore::onFrame()
{
	if(Broodwar->getFrameCount() > 86400) {
		Broodwar->restartGame();
	}
	
	int start = getMilliCount();

		globalOverseer->update();

 if (show_visibility_data)
    drawVisibilityData();

  drawBullets();

  if (Broodwar->isReplay()) {
    return;
  }

  if(analyzed){
	  drawTerrainData();
	  
	  if(Broodwar->getFrameCount() > 48 && !globalOverseer->isInitialized()) {
		  int s = getMilliCount();
	  globalOverseer->initialize();
	  int sp = getMilliSpan(s);

	    ofstream myfile;
		myfile.open ("inittime.txt", ios::app);
		myfile << sp << "\n";
		myfile.close();

	  }
  }

  if (analysis_just_finished)
  {
    Broodwar->printf("Finished analyzing map.");
    analysis_just_finished=false;

  }
  /*
  int milliSecondsElapsed = getMilliSpan(start);
  if(milliSecondsElapsed > 5) {
	  int alertLevel = 0;

	  // 10 seconds
	  if(milliSecondsElapsed >= 10000) {
		  alertLevel = 3;
		  tf++;
	  }

	  // 1 second
	  if(milliSecondsElapsed >= 1000 && milliSecondsElapsed < 10000) {
		  alertLevel = 2;
		  ts++;
	  }

	  // 55 ms
	  if(milliSecondsElapsed >= 55 && milliSecondsElapsed < 1000) {
		  alertLevel = 1;
		  tt++;
	  } 
	
	  ofstream myfile;
	    myfile.open ("longframes.txt", ios::app);
	  if(tf >= 2) {
		  myfile << "********* DISQUALIFIED ON TIME ********* FIRST RULE \n\n";
	  
	  }
	  if(ts >= 10) {
		  myfile << "********* DISQUALIFIED ON TIME ********* SECOND RULE \n\n";
	  }
	  if(ts >= 200) {
		  myfile << "********* DISQUALIFIED ON TIME ********* THIRD RULE \n\n";
	  }

	  if(alertLevel == 0) {
		 myfile << "frame \t" << Broodwar->getFrameCount() << "\t took \t" << milliSecondsElapsed << " ms" << "\n";
	  }
	  if(alertLevel == 1) {
		  myfile << "*** DANGER *** frame \t" << Broodwar->getFrameCount() << "\t took \t" << milliSecondsElapsed << " ms" << "\n";
	  }
	  if(alertLevel == 2) {
		  myfile << "****** WARNING ****** frame \t" << Broodwar->getFrameCount() << "\t took \t" << milliSecondsElapsed << " ms" << "\n";
	  }
	  if(alertLevel == 3) {
		 myfile << "************ SEVERE DANGER ************ frame \t" << Broodwar->getFrameCount() << "\t took \t" << milliSecondsElapsed << " ms" << "\n";
	  }
	  myfile.close();
	  
  }
*/
}

void UOBBotCore::onSendText(std::string text)
{
  if (text=="/show bullets")
  {
    show_bullets = !show_bullets;
  } else if (text=="/show players")
  {
    showPlayers();
  } else if (text=="/show forces")
  {
    showForces();
  } else if (text=="/show visibility")
  {
    show_visibility_data=!show_visibility_data;
  } else if (text=="/analyze")
  {
    if (analyzed == false)
    {
      Broodwar->printf("Analyzing map... this may take a minute");
      CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)AnalyzeThread, NULL, 0, NULL);
    }
  } else if (text == "/tl") {
	  Broodwar->leaveGame();
  } else if (text == "/ts") {
	  globalOverseer->testGrabScout();
  } else if (text == "/tp") {
	  globalOverseer->testUnitPrediction();  
  } else if (text == "/tw") {
	  globalOverseer->testArmyMovement(); 
  } else if (text == "/ttar") {
	  globalOverseer->testTargetting();
  } else if (text == "/tap") {

	  int s = getMilliCount();
	  globalOverseer->testPathing();
	  int sp = getMilliSpan(s);

	  ofstream myfile;
	  myfile.open ("pathtime.txt", ios::app);
	  myfile << sp << "\n";
	  myfile.close();


	  


  } else if (text == "/ta") {
	globalOverseer->getUnitManager()->grabArmy();
  } else if (text == "/tf") {
	  globalOverseer->getUnitManager()->testFormArmy();
  }  else if (text == "/ttap") {
	   testPath = globalOverseer->getUnitManager()->threatAwareAstarSearchPathAir(Broodwar->self()->getStartLocation(), Broodwar->enemy()->getStartLocation());
  } else {
    Broodwar->printf("You typed '%s'!",text.c_str());
    ////////Broodwar->sendText("%s",text.c_str());
  }
}

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

void UOBBotCore::onPlayerLeft(BWAPI::Player* player)
{
  ////////Broodwar->sendText("%s left the game.",player->getName().c_str());
}

void UOBBotCore::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 UOBBotCore::onUnitDiscover(BWAPI::Unit* unit)
{
//  if (!Broodwar->isReplay() && Broodwar->getFrameCount()>1)
 //   ////////Broodwar->sendText("A %s [%x] has been discovered at (%d,%d)",unit->getType().getName().c_str(),unit,unit->getPosition().x(),unit->getPosition().y());
	globalOverseer->getUnitManager()->notifyCreate(unit);
}

void UOBBotCore::onUnitEvade(BWAPI::Unit* unit)
{
//  if (!Broodwar->isReplay() && Broodwar->getFrameCount()>1)
  //  ////////Broodwar->sendText("A %s [%x] was last accessible at (%d,%d)",unit->getType().getName().c_str(),unit,unit->getPosition().x(),unit->getPosition().y());
}

void UOBBotCore::onUnitShow(BWAPI::Unit* unit)
{
	 // if(unit->getType() == UnitTypes::Spell_Scanner_Sweep) {
	//	 //////Broodwar->sendText("A %s [%x] has been spotted at (%d,%d)",unit->getType().getName().c_str(),unit,unit->getPosition().x(),unit->getPosition().y());
	 // }
	  
}

void UOBBotCore::onUnitHide(BWAPI::Unit* unit)
{
//  if (!Broodwar->isReplay() && Broodwar->getFrameCount()>1)
//    ////////Broodwar->sendText("A %s [%x] was last seen at (%d,%d)",unit->getType().getName().c_str(),unit,unit->getPosition().x(),unit->getPosition().y());
}

void UOBBotCore::onUnitCreate(BWAPI::Unit* unit)
{
  if (Broodwar->getFrameCount()>1)
  {
    if (!Broodwar->isReplay())
    //  ////////Broodwar->sendText("A %s [%x] has been created at (%d,%d)",unit->getType().getName().c_str(),unit,unit->getPosition().x(),unit->getPosition().y());
		globalOverseer->getUnitManager()->notifyCreate(unit);

    else
    {
      /*if we are in a replay, then we will print out the build order
      (just of the buildings, not the units).*/
      if (unit->getType().isBuilding() && unit->getPlayer()->isNeutral()==false)
      {
        int seconds=Broodwar->getFrameCount()/24;
        int minutes=seconds/60;
        seconds%=60;
        ////////Broodwar->sendText("%.2d:%.2d: %s creates a %s",minutes,seconds,unit->getPlayer()->getName().c_str(),unit->getType().getName().c_str());
      }
    }
  }
}


void UOBBotCore::onUnitDestroy(BWAPI::Unit* unit)
{
 // if (!Broodwar->isReplay() && Broodwar->getFrameCount()>1)
 //   ////////Broodwar->sendText("A %s [%x] has been destroyed at (%d,%d)",unit->getType().getName().c_str(),unit,unit->getPosition().x(),unit->getPosition().y());
	if (Broodwar->getFrameCount()>1){
		if (!Broodwar->isReplay()) {
			globalOverseer->getUnitManager()->notifyDestroy(unit);
		}	
	}
}

void UOBBotCore::onUnitMorph(BWAPI::Unit* unit)
{

	if(unit->getType().getRace() == BWAPI::Races::Zerg || unit->getType().isRefinery()) {
		 UnitModel* u = globalOverseer->getUnitManager()->getUnitModelFromMap(unit);
		if(u == NULL || (u != NULL && (unit->getType().getRace() == BWAPI::Races::Zerg))) {
			 globalOverseer->getUnitManager()->notifyCreate(unit);
		 } 
	  } else {
		  if(unit->getType() == UnitTypes::Resource_Vespene_Geyser || unit->getType() == UnitTypes::Zerg_Spore_Colony || unit->getType() == UnitTypes::Zerg_Creep_Colony || unit->getType() == UnitTypes::Zerg_Sunken_Colony) {
			  globalOverseer->getUnitManager()->notifyDestroy(unit);
		  }
		}
  
}

void UOBBotCore::onUnitRenegade(BWAPI::Unit* unit)
{
  //if (!Broodwar->isReplay())
    ////////Broodwar->sendText("A %s [%x] is now owned by %s",unit->getType().getName().c_str(),unit,unit->getPlayer()->getName().c_str());
}

void UOBBotCore::onSaveGame(std::string gameName)
{
  Broodwar->printf("The game was saved to \"%s\".", gameName.c_str());
}

DWORD WINAPI AnalyzeThread()
{
  BWTA::analyze();
/*
  //self start location only available if the map has base locations
  if (BWTA::getStartLocation(BWAPI::Broodwar->self())!=NULL)
  {
    home       = BWTA::getStartLocation(BWAPI::Broodwar->self())->getRegion();
  }
  //enemy start location only available if Complete Map Information is enabled.
  if (BWTA::getStartLocation(BWAPI::Broodwar->enemy())!=NULL)
  {
    enemy_base = BWTA::getStartLocation(BWAPI::Broodwar->enemy())->getRegion();
  }
  */
  analyzed   = true;
  analysis_just_finished = true;
  return 0;
}

void UOBBotCore::drawStats()
{
	/*
  std::vector<UnitModel*> myUnits = globalOverseer->getUnitManager()->getAllUnits();
  ////Broodwar->drawTextScreen5,0,"I believe I am aware of %d units:",myUnits.size());
  std::map<UnitType, int> unitTypeCounts;
  for(std::vector<UnitModel*>::iterator i=myUnits.begin();i!=myUnits.end();i++)
  {
	  
    if (unitTypeCounts.find(((*i)->getUnit())->getType())==unitTypeCounts.end())
    {
      unitTypeCounts.insert(std::make_pair(((*i)->getUnit())->getType(),0));
    }
    unitTypeCounts.find(((*i)->getUnit())->getType())->second++;
  }
  int line=1;
  for(std::map<UnitType,int>::iterator i=unitTypeCounts.begin();i!=unitTypeCounts.end();i++)
  {
    ////Broodwar->drawTextScreen5,16*line,"- %d %ss",(*i).second, (*i).first.getName().c_str());
    line++;
  }
  */
}

void UOBBotCore::drawBullets()
{
  std::set<Bullet*> bullets = Broodwar->getBullets();
  for(std::set<Bullet*>::iterator i=bullets.begin();i!=bullets.end();i++)
  {
    Position p=(*i)->getPosition();
    double velocityX = (*i)->getVelocityX();
    double velocityY = (*i)->getVelocityY();
    if ((*i)->getPlayer()==Broodwar->self())
    {
      //Broodwar->drawLineMap(p.x(),p.y(),p.x()+(int)velocityX,p.y()+(int)velocityY,Colors::Green);
      //Broodwar->drawTextMap(p.x(),p.y(),"\x07%s",(*i)->getType().getName().c_str());
    }
    else
    {
      //Broodwar->drawLineMap(p.x(),p.y(),p.x()+(int)velocityX,p.y()+(int)velocityY,Colors::Red);
      //Broodwar->drawTextMap(p.x(),p.y(),"\x06%s",(*i)->getType().getName().c_str());
    }
  }
}

void UOBBotCore::drawVisibilityData()
{

}

void UOBBotCore::drawTerrainData()
{
	//we will iterate through all the base locations, and draw their outlines.
	for(std::set<BWTA::BaseLocation*>::const_iterator i=BWTA::getBaseLocations().begin();i!=BWTA::getBaseLocations().end();i++)
	{

		TilePosition p=(*i)->getTilePosition();
		Position c=(*i)->getPosition();

		//draw outline of center location
		Broodwar->drawBox(CoordinateType::Map,p.x()*32,p.y()*32,p.x()*32+4*32,p.y()*32+3*32,Colors::Blue,false);

		//draw a circle at each mineral patch
		for(std::set<BWAPI::Unit*>::const_iterator j=(*i)->getStaticMinerals().begin();j!=(*i)->getStaticMinerals().end();j++)
		{
			Position q=(*j)->getInitialPosition();
			Broodwar->drawCircle(CoordinateType::Map,q.x(),q.y(),30,Colors::Cyan,false);
		}

		//draw the outlines of vespene geysers
		for(std::set<BWAPI::Unit*>::const_iterator j=(*i)->getGeysers().begin();j!=(*i)->getGeysers().end();j++)
		{
			TilePosition q=(*j)->getInitialTilePosition();
			Broodwar->drawBox(CoordinateType::Map,q.x()*32,q.y()*32,q.x()*32+4*32,q.y()*32+2*32,Colors::Orange,false);
		}

		//if this is an island expansion, draw a yellow circle around the base location
		if ((*i)->isIsland())
			Broodwar->drawCircle(CoordinateType::Map,c.x(),c.y(),80,Colors::Yellow,false);
	}

	//we will iterate through all the regions and draw the polygon outline of it in green.
	for(std::set<BWTA::Region*>::const_iterator r=BWTA::getRegions().begin();r!=BWTA::getRegions().end();r++)
	{
		BWTA::Polygon p=(*r)->getPolygon();
		for(int j=0;j<(int)p.size();j++)
		{
			Position point1=p[j];
			Position point2=p[(j+1) % p.size()];
			Broodwar->drawLine(CoordinateType::Map,point1.x(),point1.y(),point2.x(),point2.y(),Colors::Green);
		}
	}

	//we will visualize the chokepoints with red lines
	for(std::set<BWTA::Region*>::const_iterator r=BWTA::getRegions().begin();r!=BWTA::getRegions().end();r++)
	{
		for(std::set<BWTA::Chokepoint*>::const_iterator c=(*r)->getChokepoints().begin();c!=(*r)->getChokepoints().end();c++)
		{
			Position point1=(*c)->getSides().first;
			Position point2=(*c)->getSides().second;
			Broodwar->drawLine(CoordinateType::Map,point1.x(),point1.y(),point2.x(),point2.y(),Colors::Red);
		}
	}
}

void UOBBotCore::showPlayers()
{
  std::set<Player*> players=Broodwar->getPlayers();
  for(std::set<Player*>::iterator i=players.begin();i!=players.end();i++)
  {
    Broodwar->printf("Player [%d]: %s is in force: %s",(*i)->getID(),(*i)->getName().c_str(), (*i)->getForce()->getName().c_str());
  }
}

void UOBBotCore::showForces()
{
  std::set<Force*> forces=Broodwar->getForces();
  for(std::set<Force*>::iterator i=forces.begin();i!=forces.end();i++)
  {
    std::set<Player*> players=(*i)->getPlayers();
    Broodwar->printf("Force %s has the following players:",(*i)->getName().c_str());
    for(std::set<Player*>::iterator j=players.begin();j!=players.end();j++)
    {
      Broodwar->printf("  - Player [%d]: %s",(*j)->getID(),(*j)->getName().c_str());
    }
  }
}

void UOBBotCore::onUnitComplete(BWAPI::Unit *unit)
{
 // if (!Broodwar->isReplay() && Broodwar->getFrameCount()>1)
  //  ////////Broodwar->sendText("A %s [%x] has been completed at (%d,%d)",unit->getType().getName().c_str(),unit,unit->getPosition().x(),unit->getPosition().y());
}