//////////////////////////////////////////////////////////////////////////
//
// 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 "raiding.h"
#include "sieging.h"
#include "kiting.h"
#include "walking.h"
#include "repairing.h"
#include "exploring.h"
#include "defaultBehavior.h"
#include "../units/factory.h"
#include "../strategy/strategy.h"
#include "../strategy/zerglingRush.h"
#include "../Iron.h"

namespace { auto & bw = Broodwar; }


namespace iron
{


//////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                          //
//                                  class Raiding
//                                                                                          //
//////////////////////////////////////////////////////////////////////////////////////////////

vector<Raiding *> Raiding::m_Instances;

Raiding::Raiding(MyUnit * pAgent, Position target)
	: Behavior(pAgent, behavior_t::Raiding), m_target(target)
{
	assert_throw(!pAgent->Is(Terran_SCV));
	assert_throw(pAgent->Flying() || pathExists(pAgent->Pos(), target));

	m_pTargetArea = ai()->GetMap().GetNearestArea(WalkPosition(Target()));


	PUSH_BACK_UNCONTAINED_ELEMENT(m_Instances, this);

	if (!(ai()->GetStrategy()->Detected<ZerglingRush>() && pAgent->Is(Terran_Marine)))
		if (Agent()->GetStronghold())
			Agent()->LeaveStronghold();
}


Raiding::~Raiding()
{
#if !DEV
	try //3
#endif
	{
		assert_throw(contains(m_Instances, this));
		really_remove(m_Instances, this);
	}
#if !DEV
	catch(...){} //3
#endif
}


void Raiding::ChangeState(state_t st)
{CI(this);
	assert_throw(m_state != st);
	
	m_state = st; OnStateChanged();
}


bool Raiding::CanRepair(const MyBWAPIUnit * , int ) const
{CI(this);
	return false;
}


bool Raiding::CanChase(const HisUnit * ) const
{CI(this);
	return true;
}


bool Raiding::WaitGroup() const
{CI(this);
	if (!Agent()->Flying())
		if (ai()->GetMap().GetMiniTile(WalkPosition(Agent()->Pos())).Altitude() > 6*32)
		{
			int vultures = 0;
			int others = 0;
			int distToTarget = groundDist(Agent()->Pos(), Target());
			int maxDistToTargetInRaidersAround = numeric_limits<int>::min();
			for (const Raiding * r : Instances())
				if (!r->Agent()->Flying())
					if (r != this)
						if (r->Target() == Target())
							if (groundDist(r->Agent()->Pos(), Agent()->Pos()) < 20*32)
							{
								maxDistToTargetInRaidersAround = max(maxDistToTargetInRaidersAround, groundDist(r->Agent()->Pos(), Target()));

								if (r->Agent()->Is(Terran_Vulture)) ++vultures;
								else ++others;
							}
	
			const int maxVultures = others ? 2 : 8;

			if (distToTarget + 7*32 < maxDistToTargetInRaidersAround)
				if (vultures <= maxVultures)
				if (maxVultures + others <= 8)
				{
#if DEV
					for (const Raiding * r : Instances())
						if (!r->Agent()->Flying())
							drawLineMap(Agent()->Pos(), r->Agent()->Pos(), GetColor());
#endif
				///	bw << Agent()->NameWithId() << " wait group" << endl;
				///	ai()->SetDelay(500);
					return true;
				}
		}

	return false;
}


void Raiding::OnFrame_v()
{CI(this);
#if DEV
	drawLineMap(Agent()->Pos(), m_target, GetColor());//1
#endif

	if (!Agent()->CanAcceptCommand()) return;

	if (Agent()->Life() < Agent()->PrevLife(10))
		return Agent()->ChangeBehavior<Kiting>(Agent());

	if (Sieging::EnterCondition(Agent()))
		return Agent()->ChangeBehavior<Sieging>(Agent()->As<Terran_Siege_Tank_Tank_Mode>());

	if (Kiting::KiteCondition(Agent()) || Kiting::AttackCondition(Agent()))
		return Agent()->ChangeBehavior<Kiting>(Agent());

	if (Agent()->Life() < Agent()->MaxLife())
		if (Agent()->Type().isMechanical())
			if (Agent()->RepairersCount() < Agent()->MaxRepairers())
				if (Repairing * pRepairer = Repairing::GetRepairer(Agent(),
					Agent()->StayInArea() ? 8*32 :
					(Agent()->Life()*4 > Agent()->MaxLife()*3) ? 10*32 :
					Agent()->Is(Terran_Wraith) ? 1000000 :
					(Agent()->Life()*4 > Agent()->MaxLife()*2) ? 30*32 : 1000000))
				{
					return Agent()->ChangeBehavior<Repairing>(Agent(), pRepairer);
				}


	if (dist(Agent()->Pos(), Target()) < 32*5)
		if (Agent()->GetArea(no_check) == TargetArea())
		{
			if (!Agent()->Flying()) ResetSubBehavior();
	//		return Agent()->ChangeBehavior<DefaultBehavior>(Agent());
			return Agent()->ChangeBehavior<Exploring>(Agent(), Agent()->GetArea());
		}

	if (JustArrivedInState())
	{
		m_inStateSince = ai()->Frame();
		if (Agent()->Flying())
			return Agent()->Move(Target());
		else
			return SetSubBehavior<Walking>(Agent(), Target(), __FILE__ + to_string(__LINE__));
	}

	if (WaitGroup())
		return ResetSubBehavior();

	if (ai()->Frame() - m_inStateSince > 5)
	{
		if (!Agent()->Flying())
			if (!GetSubBehavior())
				if (ai()->Frame() - Agent()->LastFrameMoving() > 10)
					return SetSubBehavior<Walking>(Agent(), Target(), __FILE__ + to_string(__LINE__));

		if (ai()->Frame() - Agent()->LastFrameMoving() > 20)
		{
			if (Agent()->GetArea(no_check) == TargetArea())
				return Agent()->ChangeBehavior<Exploring>(Agent(), TargetArea());

			return Agent()->ChangeBehavior<DefaultBehavior>(Agent());
		}

//		if (ai()->Frame() % 100 == 0)
//			return Agent()->ChangeBehavior<DefaultBehavior>(Agent());
	}
}



} // namespace iron



