//////////////////////////////////////////////////////////////////////////
//
// 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 "zealotRush.h"
#include "../units/army.h"
#include "../behavior/mining.h"
#include "../behavior/chasing.h"
#include "../Iron.h"

namespace { auto & bw = Broodwar; }




namespace iron
{


//////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                          //
//                                  class ZealotRush
//                                                                                          //
//////////////////////////////////////////////////////////////////////////////////////////////


ZealotRush::ZealotRush()
{
}


string ZealotRush::StateDescription() const
{
	if (!m_detected) return "-";
	if (m_detected) return "detected";

	return "-";
}


void ZealotRush::WorkerDefense()
{
	for (const auto & u : me().Units(Terran_SCV))
		if (u->Completed())
			if (groundDist(u->Pos(), me().GetBase(0)->Center()) < 10*32)
				if (u->GetBehavior()->IsMining() ||
					u->GetBehavior()->IsRefining())
				{
					int minDistToMyRange = 2*32;
					HisUnit * pNearestTarget = nullptr;
					for (const auto & faceOff : u->FaceOffs())
						if (faceOff.MyAttack() && faceOff.HisAttack())
						if (faceOff.DistanceToMyRange() < minDistToMyRange)
						{
							minDistToMyRange = faceOff.DistanceToMyRange();
							pNearestTarget = faceOff.His()->IsHisUnit();
						}

					if (pNearestTarget)
						u->ChangeBehavior<Chasing>(u.get(), pNearestTarget, bool("insist"), 15 + minDistToMyRange/4);
				}
}


bool ZealotRush::Detection() const
{
//	if (me().SupplyUsed() >= 21)return true;
//	if (!me().Buildings(Terran_Machine_Shop).empty()) return true;

	for (const auto & u : him().Units())
		if (u->Is(Protoss_Zealot))
		{
			if (groundDist(u->Pos(), me().GetBase(0)->Center()) < 30*32)
				return true;

			if (him().StartingBase())
				if (groundDist(u->Pos(), me().GetBase(0)->Center()) < groundDist(u->Pos(), him().StartingBase()->BWEMPart()->Center()))
					if (me().CompletedUnits(Terran_Vulture) == 0)
						return true;
		}

	int zealotCount = 0;
	for (const auto & info : him().UnitTrace())
		if (info.second.Type() == Protoss_Zealot)
			++zealotCount;

	if ((zealotCount >= 3) && (me().CompletedBuildings(Terran_Factory) == 0))
		return true;

	return false;
}


int ZealotRush::MaxMarines() const
{
	return max(0, 6 - (int)me().Units(Terran_Vulture).size());
}


bool ZealotRush::TechRestartingCondition() const
{
	return (me().MineralsAvailable() >= 200) || (me().CompletedUnits(Terran_Marine) + me().CompletedUnits(Terran_Vulture) >= 2);
}


void ZealotRush::OnFrame_v()
{
	if ((him().Race() == Races::Terran) || (him().Race() == Races::Zerg)) return Abort();

	if (m_detected)
	{
		if (none_of(him().Units().begin(), him().Units().end(), [](const unique_ptr<HisUnit> & u)
						{ return u->Is(Protoss_Zealot) && (groundDist(u->Pos(), me().GetBase(0)->Center()) < 20*32); }))
			if (me().CompletedUnits(Terran_Vulture) >= 3)
				if (me().Army().GroundLead() || (me().CompletedUnits(Terran_Vulture) >= 5))
					return Abort();

		WorkerDefense();

		if (Mining::Instances().size() < 6)
			if (me().MineralsAvailable() < 30)
				if (!(me().UnitsBeingTrained(Terran_Vulture) && me().UnitsBeingTrained(Terran_Marine)))
				{
					if (me().SupplyAvailable() >= 3)
						for (const auto & b : me().Buildings(Terran_Supply_Depot))
							if (!b->Completed())
								if (b->CanAcceptCommand())
									return b->CancelConstruction();

					if (me().Buildings(Terran_Factory).size() >= 2)
						if (me().Units(Terran_Vulture).size() == 0)
						{
							MyBuilding * pLatestUncompletedFactory = nullptr;
							for (const auto & b : me().Buildings(Terran_Factory))
								if (!b->Completed())
									if (!pLatestUncompletedFactory || (b->RemainingBuildTime() > pLatestUncompletedFactory->RemainingBuildTime()))
										pLatestUncompletedFactory = b.get();

							if (pLatestUncompletedFactory)
								if (pLatestUncompletedFactory->RemainingBuildTime() > 750)
									if (pLatestUncompletedFactory->CanAcceptCommand())
										return pLatestUncompletedFactory->CancelConstruction();
						}
				}

		static frame_t lastCancel = 0;
///		bw << ai()->Frame() - lastCancel << endl;
		if (ai()->Frame() - lastCancel > 3*bw->getRemainingLatencyFrames())
			for (UnitType type : {Terran_Vulture, Terran_Marine})
				for (const auto & bb : me().Buildings(type == Terran_Marine ? Terran_Barracks : Terran_Factory))
					if (bb->Completed() && bb->CanAcceptCommand() && !bb->Unit()->isTraining())
						if ((type != Terran_Marine) || ((int)me().Units(Terran_Marine).size() < MaxMarines()))
						{
							const bool needMinerals = me().MineralsAvailable() - Cost(type).Minerals() < -3*(int)Mining::Instances().size();
							const int supplyResult = me().SupplyAvailable() - Cost(type).Supply();

							if (( needMinerals && (supplyResult + 1 >= 0)) ||
								(!needMinerals && (supplyResult + 1 == 0)))
								for (const auto & b : me().Buildings(Terran_Command_Center))
									if (b->Unit()->isTraining())
										if (b->TimeToTrain() > 75)
											if (b->CanAcceptCommand())
											{
											///	bw << "CancelTrain SCV" << endl;
											///	ai()->SetDelay(5000);
												lastCancel = ai()->Frame();
												return b->CancelTrain();
											}

							if (needMinerals && (supplyResult >= 0))
								for (const auto & b : me().Buildings(Terran_Supply_Depot))
									if (!b->Completed())
										if (b->CanAcceptCommand())
										{
										///	bw << "CancelConstruction Depot" << endl;
										///	ai()->SetDelay(5000);
											lastCancel = ai()->Frame();
											return b->CancelConstruction();
										}
						}
	}
	else
	{
		if (me().CompletedUnits(Terran_Vulture) >= 2) return Abort();

		if (Detection())
		{
		///	ai()->SetDelay(100);
			m_detected = true;
			return;
		}
	}
}


} // namespace iron



