//////////////////////////////////////////////////////////////////////////
//
// 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 "freeTurrets.h"
#include "../units/cc.h"
#include "../territory/vgridMap.h"
#include "../Iron.h"

namespace { auto & bw = Broodwar; }




namespace iron
{


//////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                          //
//                                  class FreeTurrets
//                                                                                          //
//////////////////////////////////////////////////////////////////////////////////////////////


FreeTurrets::FreeTurrets()
{
}


string FreeTurrets::StateDescription() const
{
	if (!m_active) return "-";
	if (m_active) return "active";

	return "-";
}


void FreeTurrets::CheckNewConstruction()
{
	m_nextLocation = TilePositions::None;
	m_pBuilder = nullptr;

	multimap<int, pair<int, int>> Candidates;

	for (int j = 0 ; j < ai()->GetGridMap().Height() ; ++j)
	for (int i = 0 ; i < ai()->GetGridMap().Width() ; ++i)
		if (!contains(me().EnlargedAreas(), ai()->GetMap().GetArea(ai()->GetGridMap().GetCenter(i, j))))
		{
			const GridMapCell & Cell = ai()->GetGridMap().GetCell(i, j);
			if ((Cell.AvgHisUnitsAndBuildingsLast256Frames() == 0) && (Cell.HisUnits.size() == 0))
			if ((Cell.AvgMyUnitsAndBuildingsLast256Frames() >= 3) && (Cell.MyUnits.size() >= 3) && (Cell.MyUnits.front()->GetArea(no_check)))
			{
				int turrets = count_if(Cell.MyBuildings.begin(), Cell.MyBuildings.end(), [](MyBuilding * b){ return b->Is(Terran_Missile_Turret); });
				if (turrets < (NeedManyManyTurrets() ? 6 : NeedManyTurrets() ? 5 : 4))
				{
				//	int mines = count_if(Cell.MyBuildings.begin(), Cell.MyBuildings.end(), [](MyBuilding * b){ return b->Is(Terran_Vulture_Spider_Mine); });
					int score = Cell.AvgMyUnitsAndBuildingsLast256Frames() - turrets;// - mines;
					if (score >= 3)
						Candidates.emplace(score, make_pair(i, j));
				}
			}
		}

	if (Candidates.empty()) return;


	int iCand, jCand;
	tie(iCand, jCand) = Candidates.rbegin()->second;
	TilePosition focusTile = ai()->GetGridMap().GetCenter(iCand, jCand);
	const GridMapCell & Cand = ai()->GetGridMap().GetCell(iCand, jCand);

	vector<const Area *> Areas = {Cand.MyUnits.front()->GetArea()};
	for (const Area * a : Areas.front()->AccessibleNeighbours())
		Areas.push_back(a);

	const int radius = 10;

	vector<MyUnit *> MyUnitsAround = ai()->GetGridMap().GetMyUnits(ai()->GetMap().Crop(focusTile-(radius+2)), ai()->GetMap().Crop(focusTile+(radius+2)));
	vector<MyBuilding *> MyBuildingsAround = ai()->GetGridMap().GetMyBuildings(ai()->GetMap().Crop(focusTile-(radius+2)), ai()->GetMap().Crop(focusTile+(radius+2)));

	vector<HisBuilding *> HisBuildingsAround;
	for (const auto & u : him().Buildings())
		if (dist(u->Pos(), center(focusTile)) < (radius + 10)*32)
			HisBuildingsAround.push_back(u.get());

	vector<HisUnit *> HisUnitsAround;
	for (const auto & u : him().Units())
		if (dist(u->Pos(), center(focusTile)) < (radius + 16)*32)
			if (u->GroundAttack())
				HisUnitsAround.push_back(u.get());

	auto it = find_if(MyUnitsAround.begin(), MyUnitsAround.end(), [](MyUnit * u)
			{ return u->Is(Terran_SCV) && !u->GetStronghold() &&
					(u->GetBehavior()->IsHarassing() || u->GetBehavior()->IsRepairing()) ; });
	if (it == MyUnitsAround.end()) return;
	m_pBuilder = (*it)->IsMy<Terran_SCV>();



	int countTurrets = count_if(MyBuildingsAround.begin(), MyBuildingsAround.end(), [](MyBuilding * b)
			{ return b->Is(Terran_Missile_Turret); });

	const int basePriority = NeedManyManyTurrets() ? 530 :
							((him().Race() == Races::Zerg) || m_needTurrets || NeedManyTurrets()) ? 490 :
							400;
	m_priority = max(basePriority - countTurrets*(NeedManyManyTurrets() ? 15 : NeedManyTurrets() ? 20 : 40), 100);

	map<int, TilePosition> Locations;
	TilePosition boundingBoxTopLeft = ai()->GetMap().Crop(focusTile - radius);
	TilePosition boundingBoxBottomRight = ai()->GetMap().Crop(focusTile + radius) - 1;
	for (int y = boundingBoxTopLeft.y ; y <= boundingBoxBottomRight.y ; ++y)
	for (int x = boundingBoxTopLeft.x ; x <= boundingBoxBottomRight.x ; ++x)
	{
		TilePosition t(x, y);
		CHECK_POS(t);
		Position pos(t+1);
		if (contains(Areas, ai()->GetMap().GetArea(t)))
		{
			bool canBuild = true;
			for (int dy = 0 ; dy < 2 ; ++dy) if (canBuild)
			for (int dx = 0 ; dx < 2 ; ++dx) if (canBuild)
			{
				CHECK_POS(t + TilePosition(dx, dy));
				const Tile & tile = ai()->GetMap().GetTile(t + TilePosition(dx, dy), no_check);
				if (!tile.Buildable() || tile.GetNeutral() ||
					ai()->GetVMap().GetBuildingOn(tile) || ai()->GetVMap().AddonRoom(tile) || ai()->GetVMap().CommandCenterWithAddonRoom(tile) ||
					(tile.MinAltitude() < 32) || (bw->isVisible(t) && bw->hasCreep(t)))
					canBuild = false;
			}
			if (!canBuild) continue;

			if (any_of(MyUnitsAround.begin(), MyUnitsAround.end(), [pos](MyUnit * u)
						{ return (u->Is(Terran_Vulture_Spider_Mine) && (squaredDist(u->Pos(), pos) < 32*32))
							  || (u->Is(Terran_Siege_Tank_Siege_Mode) && (squaredDist(u->Pos(), pos) < 56*56)); }))
				continue;
			if (any_of(MyBuildingsAround.begin(), MyBuildingsAround.end(), [t, this](MyBuilding * b)
						{ return b->Is(Terran_Missile_Turret) && (squaredDist(b->TopLeft(), t) < (NeedManyManyTurrets() ? 3*3 : NeedManyTurrets() ? 4*4 : 6*6)); }))
				continue;

			if (any_of(HisBuildingsAround.begin(), HisBuildingsAround.end(), [pos](HisBuilding * b)
						{ return (squaredDist(b->Pos(), pos) < (32*9)*(32*9)); }))
				continue;

			if (any_of(HisUnitsAround.begin(), HisUnitsAround.end(), [t](HisUnit * u)
						{ return (distToRectangle(u->Pos(), t, UnitType(Terran_Missile_Turret).tileSize()) < u->GroundRange() + 16); }))
				continue;

#if DEV
///			bw->drawBoxMap(Position(t)+3, Position(t + 1)-4 , Colors::Orange);
#endif
			Locations[roundedDist(Position(t), him().StartingBase()->BWEMPart()->Center()) + ai()->GetMap().GetTile(t).MinAltitude()] = t;
		}
	}

	if (Locations.empty())
	{
		m_nextLocation = TilePositions::None;
		m_priority = 0;
		m_pBuilder = nullptr;
	}
	else
	{
#if DEV
///		bw->drawBoxMap(Position(Locations.rbegin()->second)+3, Position(Locations.rbegin()->second + 1)-4 , Colors::Orange, true);
#endif
		m_nextLocation = Locations.rbegin()->second;
	}

#if DEV
///	bw->drawBoxMap(	Position(ai()->GetGridMap().GetTopLeft(iCand, jCand)), Position(ai()->GetGridMap().GetBottomRight(iCand, jCand) + 1), Colors::Yellow);
#endif
}


void FreeTurrets::OnFrame_v()
{
	if (m_active)
	{
		if (!me().CompletedBuildings(Terran_Engineering_Bay))
		{
			m_active = false;
			return;
		}

		const int mutas = count_if(him().UnitTrace().begin(), him().UnitTrace().end(), [](const pair<Unit, HisUnitTrace> & p)
								{ return p.second.Type() == Zerg_Mutalisk; });

		const int lurkers = count_if(him().UnitTrace().begin(), him().UnitTrace().end(), [](const pair<Unit, HisUnitTrace> & p)
								{ return p.second.Type() == Zerg_Lurker; });

		const int carriers = count_if(him().UnitTrace().begin(), him().UnitTrace().end(), [](const pair<Unit, HisUnitTrace> & p)
								{ return p.second.Type() == Protoss_Carrier; });

		if (mutas >= 7) SetNeedManyManyTurrets(true);
		else
		{
			SetNeedManyManyTurrets(false);

			if ((mutas >= 4) || (lurkers >= 1) || (carriers >= 1)) SetNeedManyTurrets(true);
			else SetNeedManyTurrets(false);
		}

		CheckNewConstruction();
	}
	else
	{
		if (me().CompletedBuildings(Terran_Engineering_Bay))
		{
///			ai()->SetDelay(100);
			m_active = true;
			m_activeSince = ai()->Frame();
			return;
		}
	}
}


} // namespace iron



