#include "SurveyerManager.h"
#include "MapGrid.h"
#include <random>

using namespace UAlbertaBot;

SurveyerManager::SurveyerManager()
	: _firstSurveyor(nullptr)
{ 
}

void SurveyerManager::executeMicro(const BWAPI::Unitset & targets)
{
	BWTA::BaseLocation * enemyBaseLocation = InformationManager::Instance().getEnemyMainBaseLocation();
	BWTA::BaseLocation * myMainBaseLocation = InformationManager::Instance().getMyMainBaseLocation();
	BWTA::BaseLocation * myNaturalLocation = InformationManager::Instance().getMyNaturalLocation();
	BWAPI::Unit ourNatural = nullptr;
	ourNatural = InformationManager::Instance().getBaseDepot(InformationManager::Instance().getMyNaturalLocation());

	const BWAPI::Unitset & surveyerUnits = getUnits();

	if (surveyerUnits.empty())
	{
		return;
	}

	if (BWAPI::Broodwar->getFrameCount() < 10 && surveyerUnits.size() == 1)
	{
		for (auto & surveyor : surveyerUnits)
		{
			_firstSurveyor = surveyor;
		}
	}

	// Find enemy cloaked units.
	// NOTE This code is unused, but it is potentially useful.
	cloakedUnitMap.clear();
	BWAPI::Unitset cloakedUnits;
	for (auto & unit : BWAPI::Broodwar->enemy()->getUnits())
	{
		// conditions for targeting
		// TODO is this good enough for arbiters?
		if (unit->getType().hasPermanentCloak() ||     // dark templar, observer
			unit->getType().isCloakable() ||           // wraith, ghost
			unit->getType() == BWAPI::UnitTypes::Terran_Vulture_Spider_Mine ||
			unit->getType() == BWAPI::UnitTypes::Zerg_Lurker ||
			unit->isBurrowed() ||
			(unit->isVisible() && !unit->isDetected()))
		{
			cloakedUnits.insert(unit);
			cloakedUnitMap[unit] = false;
		}
	}

	// for each surveyorUnit
	int x = 0;
	for (auto & surveyor : surveyerUnits)
	{
		if (surveyor->getType() == BWAPI::UnitTypes::Zerg_Overlord) {
			// Set order position for a surveyer
			x++;

			std::uniform_int_distribution<int> uniform_dist(50, 300);
			std::random_device rng_seed;
			std::mt19937 rng(rng_seed());
			int w = uniform_dist(rng);        // a lot of trouble to get one random number...

			BWAPI::Position surveyorTarget;
			// first surveyor
			if (surveyor == _firstSurveyor)
			{
				surveyorTarget = order.getPosition();
				//if (enemyBaseLocation)
				//{
					//surveyorTarget = enemyBaseLocation->getPosition();
				//}
				if (BWAPI::Broodwar->getFrameCount() < 10 || 
					(BWAPI::Broodwar->getFrameCount() % 10 == 0 && surveyor->getDistance(order.getPosition()) > 96))
				{
					Micro::SmartMove(surveyor, surveyorTarget);
				}
			}
			// later surveyors
			else 
			{
				/*
				//Assign detector to closest cloaked enemy unit
				BWAPI::Unit closestCloaked;
				closestCloaked = closestCloakedUnit(cloakedUnits, surveyor);

				// Move the detector toward the closest cloaked enemy unit.
				if (closestCloaked && closestCloaked->getPosition().isValid() &&
					surveyor->getDistance(closestCloaked) > 200 &&
					surveyor->getDistance(closestCloaked) < 600)
				{
					cloakedUnitMap[closestCloaked] = true;
					Micro::SmartMove(surveyor, closestCloaked->getPosition());
				}
				*/

				// Move the surveyor towards our main and natural bases.
				//surveyorTarget = MapGrid::Instance().getLeastExploredNearUs(surveyor->getPosition());
				if (BWAPI::Broodwar->getFrameCount() < 10 || 
					(BWAPI::Broodwar->getFrameCount() % w == 0 && surveyor->getDistance(order.getPosition()) > 96))
				{
					surveyorTarget = myMainBaseLocation->getPosition();
					if (UnitUtil::IsValidUnit(ourNatural) && x % 2 == 0) {
						surveyorTarget = myNaturalLocation->getPosition();
					}
					Micro::SmartMove(surveyor, surveyorTarget);
				}
			}

			if (Config::Debug::DrawSquadInfo)
			{
				BWAPI::Broodwar->drawCircleMap(surveyorTarget, 10, BWAPI::Colors::Green, true);
				BWAPI::Broodwar->drawCircleMap(surveyorTarget, 100, BWAPI::Colors::Red, false);
				BWAPI::Broodwar->drawTextMap(surveyorTarget + BWAPI::Position(0, 10), "%s %d", "Surveyer", x);
			}
		}
	}

	return;
}

BWAPI::Position SurveyerManager::getNearestStartLocation()
{
	BWTA::BaseLocation * ourBaseLocation = InformationManager::Instance().getMyMainBaseLocation();

	BWAPI::Position home(BWAPI::Broodwar->self()->getStartLocation());
	BWTA::BaseLocation * location = ourBaseLocation;
	double dist = 10000;
	double distNew = 0;

	// Pick closest startlocation that's not ours.
	for (BWTA::BaseLocation * startLocation : BWTA::getStartLocations())
	{
		if (startLocation && startLocation != ourBaseLocation && startLocation->getPosition().isValid())
		{
			distNew = home.getDistance(startLocation->getPosition());
			if (distNew < dist) {
				location = startLocation;
				dist = distNew;
			}
		}
	}

	if (location)
	{
		return location->getPosition();
	}

	if (ourBaseLocation && ourBaseLocation->getPosition().isValid()) {
		UAB_ASSERT(false, "map seems to have only 1 base");
		return ourBaseLocation->getPosition();
	}
	else {
		UAB_ASSERT(false, "map seems to have no bases");
		return BWAPI::Position(0, 0);
	}
}

void SurveyerManager::update(SquadOrder order)
{
	const BWAPI::Unitset & surveyerUnits = getUnits();

	if (surveyerUnits.empty())
	{
		return;
	}

	return;
}

BWAPI::Unit SurveyerManager::closestCloakedUnit(const BWAPI::Unitset & cloakedUnits, BWAPI::Unit detectorUnit)
{
	BWAPI::Unit closestCloaked = nullptr;
	int closestDist = 10000;

	for (auto & unit : cloakedUnits)
	{
		// if we haven't already assigned a detectorUnit to this cloaked unit
		if (!cloakedUnitMap[unit])
		{
			int dist = unit->getDistance(detectorUnit);

			if (!closestCloaked || (dist < closestDist))
			{
				closestCloaked = unit;
				closestDist = dist;
			}
		}
	}

	return closestCloaked;
}

