//////////////////////////////////////////////////////////////////////////
//
// This file is part of Iron's source files.
// Iron is free software, licensed under the MIT/X11 License. 
// A copy of the license is provided with the library in the LICENSE file.
// Copyright (c) 2016, Igor Dimitrijevic
//
//////////////////////////////////////////////////////////////////////////


#include "expand.h"
#include "strategy.h"
#include "zerglingRush.h"
#include "zealotRush.h"
#include "dragoonRush.h"
#include "marineRush.h"
#include "cannonRush.h"
#include "wraithRush.h"
#include "../units/cc.h"
#include "../units/army.h"
#include "../behavior/mining.h"
#include "../behavior/exploring.h"
#include "../behavior/defaultBehavior.h"
#include "../territory/stronghold.h"
#include "../territory/vgridMap.h"
#include "../Iron.h"

namespace { auto & bw = Broodwar; }




namespace iron
{


VBase * findNatural(const VBase * pMain)
{
	VBase * natural = nullptr;

	int minLength = numeric_limits<int>::max();
	for (VBase & base : ai()->GetVMap().Bases())
		if (&base != pMain)
			if (!base.BWEMPart()->Minerals().empty() && !base.BWEMPart()->Geysers().empty())
			{
				int length;
				ai()->GetMap().GetPath(base.BWEMPart()->Center(), pMain->BWEMPart()->Center(), &length);
				if ((length > 0) && (length < minLength))
				{
					minLength = length;
					natural = &base;
				}
			}

	return natural;
}


VBase * findNewBase()
{
	vector<pair<VBase *, int>> Candidates;

	for (VBase & base : ai()->GetVMap().Bases())
		if (!contains(me().Bases(), &base))
		if (him().StartingBase() != &base)
		{
			TilePosition baseCenter = base.BWEMPart()->Location() + UnitType(Terran_Command_Center).tileSize()/2;
			auto & Cell = ai()->GetGridMap().GetCell(baseCenter);
			if (Cell.HisUnits.empty())
			if (Cell.HisBuildings.empty())
				if (all_of(him().Buildings().begin(), him().Buildings().end(), [baseCenter](const unique_ptr<HisBuilding> & b)
								{ return dist(b->Pos(), center(baseCenter)) > 10*32; }))
					if (!base.BWEMPart()->Minerals().empty())// && !base.BWEMPart()->Geysers().empty())
					{
						int distToMyMain;
						ai()->GetMap().GetPath(base.BWEMPart()->Center(), me().GetBase(0)->Center(), &distToMyMain);

						int distToHisMain = 1000000;
						if (him().StartingBase())
							ai()->GetMap().GetPath(base.BWEMPart()->Center(), him().StartingBase()->BWEMPart()->Center(), &distToHisMain);

						if (distToMyMain > 0)
						{
							int ressourcesScore = base.MineralsAmount() + base.GasAmount();
							int score = ressourcesScore/50 + distToHisMain - distToMyMain;
							Candidates.emplace_back(&base, score);
						}
					}
		}

	if (Candidates.empty()) return nullptr;

	sort(Candidates.begin(), Candidates.end(),
		[](const pair<VBase *, int> & a, const pair<VBase *, int> & b){ return a.second > b.second; });

	return Candidates.front().first;
}


//////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                          //
//                                  class Expand
//                                                                                          //
//////////////////////////////////////////////////////////////////////////////////////////////


Expand::Expand()
{
}


Expand::~Expand()
{
}


string Expand::StateDescription() const
{
	if (!m_active) return "-";
	if (m_active) return "base #" + to_string(me().Bases().size());

	return "-";
}


bool Expand::ConditionOnUnits() const
{
	if (me().Units(Terran_Vulture).size() < 5)
		return false;

	return true;
}


bool Expand::ConditionOnActivity() const
{
	const int activeBases = count_if(me().Bases().begin(), me().Bases().end(), [](VBase * b){ return b->Active(); });

	if (activeBases >= 2)
		if (me().FactoryActivity() >= 9)
			if (me().MineralsAvailable() <= 600)
				return false;

	return true;
}


bool Expand::ConditionOnTech() const
{
	const int activeBases = count_if(me().Bases().begin(), me().Bases().end(), [](VBase * b){ return b->Active(); });

	const int mechProductionSites = me().Buildings(Terran_Factory).size() + me().Buildings(Terran_Starport).size();

	if (activeBases >= 2)
		if (mechProductionSites < min(12, 1*activeBases + 2))
			if (me().MineralsAvailable() <= 600)
				return false;

	if (him().Race() == Races::Terran)
	{
		if (!ai()->GetStrategy()->Detected<WraithRush>())
			if (!(
					(me().Buildings(Terran_Starport).size() >= 1)// &&
					//(me().Units(Terran_Wraith).size() >= 1)
				))
				return false;
	}
	else
	{
		if (!(
				(me().Buildings(Terran_Engineering_Bay).size() >= 1) &&
				(me().Units(Terran_Siege_Tank_Tank_Mode).size() >= 1)
			))
			return false;
	}
	return true;
}


bool Expand::Condition() const
{
	if (ai()->GetStrategy()->Detected<ZerglingRush>() ||
		ai()->GetStrategy()->Detected<ZealotRush>() ||
		ai()->GetStrategy()->Detected<DragoonRush>() ||
		ai()->GetStrategy()->Detected<CannonRush>() ||
		ai()->GetStrategy()->Detected<MarineRush>())
		return false;

	if (ConditionOnUnits())
	if (ConditionOnActivity())
	if (ConditionOnTech())
	{
		if (me().Bases().size() == 1)
		{
			if (me().Army().GroundLead())
			{
				if ((him().Race() == Races::Zerg) ||
					(me().Army().MyGroundUnitsAhead() >= me().Army().HisGroundUnitsAhead() + 3) ||
					(me().SupplyAvailable() >= 60) && (me().MineralsAvailable() >= 350) ||
					(me().SupplyAvailable() >= 70))
					return true;
			}
		}
		else
		{
			if (ai()->Frame() - me().Bases().back()->CreationTime() > 1000)
				if (me().Army().GroundLead())
				{
				//	bw << "a" << endl;
					return true;
				}

			if (ai()->Frame() - me().Bases().back()->CreationTime() > 10000)
				if (Mining::Instances().size() < 20)
				{
				//	bw << "b" << endl;
					return true;
				}

			if (ai()->Frame() - me().Bases().back()->CreationTime() > 2000)
				if (me().MineralsAvailable() > 600)
				{
				//	bw << "c" << endl;
					return true;
				}
		}
	}
	return false;
}


VBase * Expand::FindNewBase() const
{
	if (me().Bases().size() == 1)
	{
		return findNatural(me().GetVBase(0));
	}
	else if (me().Bases().size() >= 2)
	{
		if (ai()->Frame() - me().Bases().back()->CreationTime() > 1000)
			return findNewBase();
	}

	return nullptr;
}


void Expand::OnFrame_v()
{
	if (m_active)
	{
		VBase * newBase = me().Bases().back();

		if (newBase->Active())
		{
			vector<My<Terran_SCV> *> RemainingExploringSCVs;
			for (Exploring * pExplorer : Exploring::Instances())
				if (My<Terran_SCV> * pSCV = pExplorer->Agent()->IsMy<Terran_SCV>())
					if (pSCV->GetStronghold() == newBase->GetStronghold())
						RemainingExploringSCVs.push_back(pSCV);

			for (My<Terran_SCV> * pSCV : RemainingExploringSCVs)
				pSCV->ChangeBehavior<DefaultBehavior>(pSCV);

			m_active = false;
			return;
		}

		if (newBase->GetStronghold()->SCVs().size() < 3)
			if (none_of(Exploring::Instances().begin(), Exploring::Instances().end(),
						[newBase](const Exploring * e){ return e->Agent()->Is(Terran_SCV) && (e->Agent()->GetStronghold() == newBase->GetStronghold()); }))
				if (My<Terran_SCV> * pSCV = findFreeWorker(me().GetVBase(0)))
				{
					pSCV->ChangeBehavior<Exploring>(pSCV, newBase->GetArea()->BWEMPart());
					pSCV->LeaveStronghold();
					pSCV->EnterStronghold(newBase->GetStronghold());
				}
	}
	else
	{
		if (Condition())
		{
			if (VBase * newBase = FindNewBase())
			{
				newBase->SetCreationTime();
				me().AddBase(newBase);
				assert_throw(me().Bases().size() >= 2);
				me().AddBaseStronghold(me().Bases().back());

			///	bw << Name() << " started!" << endl; ai()->SetDelay(100);

				m_active = true;
				m_activeSince = ai()->Frame();
				return;
			}
		}
	}
}


} // namespace iron



