//////////////////////////////////////////////////////////////////////////
//
// 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 "fighting.h"
#include "walking.h"
#include "fleeing.h"
#include "laying.h"
#include "defaultBehavior.h"
#include "../units/factory.h"
#include "../Iron.h"

namespace { auto & bw = Broodwar; }


namespace iron
{


//////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                          //
//                                  class Fighting
//                                                                                          //
//////////////////////////////////////////////////////////////////////////////////////////////

vector<Fighting *> Fighting::m_Instances;

Fighting::Fighting(MyUnit * pAgent, Position where)
	: Behavior(pAgent, behavior_t::Fighting), m_where(where)
{

	PUSH_BACK_UNCONTAINED_ELEMENT(m_Instances, this);

///	ai()->SetDelay(100);
}


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


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


string Fighting::StateName() const
{CI(this);
	switch(State())
	{
	case reachingArea:		return "reachingArea";
	case attacking:			return "attacking";
	default:				return "?";
	}
}


HisUnit * Fighting::ChooseTarget() const
{
	multimap<int, HisUnit *> Candidates;

	for (const FaceOff & faceOff : Agent()->FaceOffs())
		if (HisUnit * pTarget = faceOff.His()->IsHisUnit())
			if (faceOff.MyAttack() > 0)
				if (!pTarget->InFog() && pTarget->Unit()->exists())
					if (!pTarget->Type().isWorker())
					if (!pTarget->Is(Zerg_Egg))
					if (!pTarget->Is(Zerg_Larva))
					{
						int score = pTarget->LifeWithShields();
						score += faceOff.DistanceToMyRange();

						if (Agent()->Is(Terran_SCV)) score += static_cast<int>(1000 * pTarget->Speed());

						Candidates.emplace(score, pTarget);
					}

	if (Candidates.empty()) return nullptr;

	return Candidates.begin()->second;
}


void Fighting::OnOtherBWAPIUnitDestroyed_v(BWAPIUnit * other)
{CI(this);
	if (m_pTarget == other)
		m_pTarget = nullptr;
}


void Fighting::OnFrame_v()
{CI(this);
#if DEV
	if ((m_state == attacking) && m_pTarget)
	{
		drawLineMap(Agent()->Pos(), m_pTarget->Pos(), GetColor());//1
		bw->drawCircleMap(m_pTarget->Pos(), 18, GetColor());
	}
#endif

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

	auto Threats2 = findThreats(Agent(), 2*32);
	if (any_of(Threats2.begin(), Threats2.end(), [](const FaceOff * fo){ return fo->His()->Is(Terran_Vulture_Spider_Mine); }))
		return Agent()->ChangeBehavior<Fleeing>(Agent());

	if (!Agent()->CanMove()) return Agent()->ChangeBehavior<DefaultBehavior>(Agent());

	switch (State())
	{
	case reachingArea:	OnFrame_reachingArea(); break;
	case attacking:		OnFrame_attacking(); break;
	default: assert_throw(false);
	}
}


void Fighting::OnFrame_reachingArea()
{CI(this);
//	const bool inHisArea = (Agent()->GetArea() == HisBase()->BWEMPart()->GetArea());

	int distToDest = Agent()->Flying()
					? roundedDist(Agent()->Pos(), Where())
					: groundDist(Agent()->Pos(), Where());

	if ((distToDest < 15*32) && ChooseTarget())
	{
		if (GetSubBehavior()) ResetSubBehavior();
		return ChangeState(attacking);
	}

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

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


	if (ai()->Frame() - m_inStateSince > 5)
		if (Agent()->Unit()->isIdle())
			return Agent()->ChangeBehavior<DefaultBehavior>(Agent());
}


void Fighting::OnFrame_attacking()
{CI(this);
	if (JustArrivedInState())
	{
		m_inStateSince = ai()->Frame();
		m_lastMinePlacement = 0;
	}

	{
		auto Threats5 = findThreats(Agent()->IsMyUnit(), 5*32);
		bool enemyTargetedByMines = false;
		if (!Agent()->Flying())
			for (const auto * pFaceOff : Threats5)
				if (auto * pHisUnit = pFaceOff->His()->IsHisUnit())
					if (pFaceOff->GroundDistanceToHitHim() < 6*32)
						for (Position minePos : pHisUnit->MinesTargetingThis())
							if (roundedDist(minePos, Agent()->Pos()) < roundedDist(minePos, pHisUnit->Pos()))
							{
								enemyTargetedByMines = true;
								break;
							}
		if (enemyTargetedByMines) return Agent()->ChangeBehavior<Fleeing>(Agent());
	}
//	if (ai()->Frame() - m_lastMinePlacement < 16) return;

	if (Agent()->CoolDown() > 0) m_shooted = true;

	if (Agent()->CoolDown() % 8 == 0)
	{
		m_pTarget = ChooseTarget();
	
		if (!m_pTarget)
			return Agent()->ChangeBehavior<DefaultBehavior>(Agent());

//		bw->drawCircleMap(Agent()->Pos(), 18, Colors::Red, true);
/*
		if (me().HasResearched(TechTypes::Spider_Mines))
			if (auto * pVulture = Agent()->IsMy<Terran_Vulture>())
				if (pVulture->RemainingMines() >= 1)
					if (m_pTarget->Is(Terran_Siege_Tank_Siege_Mode) ||
						m_pTarget->Is(Terran_Siege_Tank_Tank_Mode) ||
						m_pTarget->Is(Terran_Goliath) ||
						m_pTarget->Is(Protoss_Dragoon))
					{
						m_lastMinePlacement = ai()->Frame();
						return pVulture->PlaceMine(pVulture->Pos());
					}
*/
		Agent()->Attack(m_pTarget, no_check);
	}
}



} // namespace iron



