
#include "MW4Headers.hpp"

#include "AI_FocusFireSquad.hpp"

#include "AI_Groups.hpp"
#include "AI_UserConstants.hpp"
#include "ai_squadorders.hpp"
#include "CombatAI.hpp"
#include <Adept\NameTable.hpp>
#include "Sensor.hpp"
#pragma warning(disable : 4284)
#pragma warning (push)
#include <stlport\set>
#pragma warning (pop)

using namespace MW4AI;
using namespace MW4AI::Squad;
using namespace MechWarrior4;

const Stuff::Scalar min_target_update_time = 2.0f;


//________________________________________________________________________________________________________________

namespace MW4AI
{
	namespace Squad
	{
		class FocusFireSquad;
	};
};

class FocusFireSquadOrders
	: public SquadOrders
{
  public:
	FocusFireSquadOrders(FocusFireSquad& squad, MechWarrior4::Group& group, int alignment);

	virtual void Update();
	virtual bool PointIsValid(const Stuff::Point3D& point) const;
	virtual void IssueCommand(Stuff::Auto_Ptr<LancemateCommands::LancemateCommand>& command);
	virtual void NotifyShotFired();
	virtual MWObject* GetAutoTarget() const;
	virtual bool GetExtendedSensorData(std::vector<MWObject*>& sensor_data_list) const;
	virtual Stuff::Scalar GetLeastSquaredSensorDistance(const Stuff::Point3D& point) const;

  private:
	FocusFireSquad& m_Squad;
	MechWarrior4::Group& m_Group;
	int m_Alignment;
};

FocusFireSquadOrders::FocusFireSquadOrders(FocusFireSquad& squad, MechWarrior4::Group& group, int alignment)
	: m_Squad(squad)
	, m_Group(group)
	, m_Alignment(alignment)
{
}

void FocusFireSquadOrders::Update()
{
}

bool FocusFireSquadOrders::PointIsValid(const Stuff::Point3D& point) const
{
	return (true);
}

void FocusFireSquadOrders::IssueCommand(Stuff::Auto_Ptr<LancemateCommands::LancemateCommand>& command)
{
}

void FocusFireSquadOrders::NotifyShotFired()
{
}

MWObject* FocusFireSquadOrders::GetAutoTarget() const
{
	return (m_Squad.GetAutoTarget());
}

bool FocusFireSquadOrders::GetExtendedSensorData(std::vector<MWObject*>& sensor_data_list) const
{
	std::vector<MWObject*> members;
	m_Group.GetMembers(members);

	{for (std::vector<MWObject*>::const_iterator i_members = members.begin();
		  i_members != members.end();
		  ++i_members)
	{
		if ((*i_members)->GetSensor() != 0)
		{
			{for (int i_sensor = 0;
				  i_sensor < (*i_members)->GetSensor()->RefreshAndGetSensorData().GetLength();
				  ++i_sensor)
			{
				MechWarrior4::SensorData* d = (*i_members)->GetSensor()->RefreshAndGetSensorData()[i_sensor];

				if (d != 0)
				{
					Adept::Entity* current = d->object.GetCurrent();

					if ((current != 0) &&
						(current->IsDestroyed() == false) &&
						(current->IsDerivedFrom(MWObject::DefaultData) == true))
					{
						MWObject* target = Cast_Object(MWObject*,current);

						if ((target->GetAlignment() != m_Alignment) &&
							(std::find(members.begin(),members.end(),target) == members.end()) &&
							(std::find(sensor_data_list.begin(),sensor_data_list.end(),target) == sensor_data_list.end()))
						{
							sensor_data_list.push_back(target);
						}
					}
				}
			}}
		}
	}}

	return (true);
}

Stuff::Scalar FocusFireSquadOrders::GetLeastSquaredSensorDistance(const Stuff::Point3D& point) const
{
	std::vector<MWObject*> members;
	m_Group.GetMembers(members);

	Stuff::Scalar least_distance = 0;

	{for (std::vector<MWObject*>::const_iterator i_members = members.begin();
		  i_members != members.end();
		  ++i_members)
	{
		Stuff::Scalar distance(GetLengthSquared((Stuff::Point3D)((*i_members)->GetLocalToWorld()),point));

		if ((least_distance == 0) ||
			(distance < least_distance))
		{
			least_distance = distance;
		}
	}}

	return (least_distance);
}


//________________________________________________________________________________________________________________

FocusFireSquad::FocusFireSquad()
	: m_AutoTarget(-2)
	, m_LastTargetUpdateTime(0)
{
}

ID FocusFireSquad::GetID() const
{
	return (GROUPAI_FOCUSFIRESQUAD);
}

	template <class X>
	X GetMostFrequentElement(const std::multiset<X>& m)
	{
		std::set<X> s(m.begin(),m.end());

		std::multiset<X>::size_type best_count = 0;
		X best_element = 0;

		{for (std::set<X>::const_iterator i = s.begin();
			  i != s.end();
			  ++i)
		{
			std::multiset<X>::size_type count = m.count(*i);

			if (count > best_count)
			{
				best_element = *i;
				best_count = count;
			}
		}}

		return (best_element);
	}

void FocusFireSquad::Update(MechWarrior4::Group& group, MechWarrior4::CombatAI& combat_ai)
{
	inherited::Update(group,combat_ai);

	if (m_LastTargetUpdateTime + min_target_update_time > (Stuff::Scalar)gos_GetElapsedTime())
	{
		return;
	}

	m_LastTargetUpdateTime = (Stuff::Scalar)gos_GetElapsedTime();

	m_AutoTarget = -2;

	std::multiset<Adept::ObjectID> potential_targets;

	std::vector<MWObject*> members;
	group.GetMembers(members);

	{for (std::vector<MWObject*>::iterator i = members.begin();
		  i != members.end();
		  ++i)
	{
		if (((*i)->GetAI() != 0) &&
			((*i)->GetAI()->IsDerivedFrom(CombatAI::DefaultData) == true))
		{
			CombatAI* combat_ai = Cast_Object(CombatAI*,(*i)->GetAI());

			MWObject* target = combat_ai->FindBestAutoTarget();

			if (target != 0)
			{
				potential_targets.insert(target->objectID);
			}
		}
	}}

	if (potential_targets.size() == 0)
	{
		return;
	}

	m_AutoTarget = GetMostFrequentElement<Adept::ObjectID>(potential_targets);
}

void FocusFireSquad::NotifyShot(MechWarrior4::Group& group, Adept::ObjectID id)
{
	inherited::NotifyShot(group,id);
}

void FocusFireSquad::NotifyShotFired(MechWarrior4::Group& group, const Stuff::Line3D& line, MechWarrior4::MWObject& at_who, MechWarrior4::MWObject& shooter)
{
	inherited::NotifyShotFired(group,line,at_who,shooter);
}

void FocusFireSquad::NotifyMemberAdded(MechWarrior4::Group& group, MechWarrior4::MWObject& who)
{
	inherited::NotifyMemberAdded(group,who);

	if ((who.GetAI() != 0) &&
		(who.GetAI()->IsDerivedFrom(CombatAI::DefaultData) == true))
	{
		CombatAI* combat_ai = Cast_Object(CombatAI*,who.GetAI());
		#pragma warning(disable:4239)
		FocusFireSquadOrders* new_squad_orders = new FocusFireSquadOrders(*this,group,who.GetAlignment());
		combat_ai->SetSquadOrders(Stuff::Auto_Ptr<SquadOrders>(new_squad_orders));
		#pragma warning(default:4239)
	}
}

void FocusFireSquad::NotifyMemberRemoved(MechWarrior4::Group& group, MechWarrior4::MWObject& who)
{
	inherited::NotifyMemberRemoved(group,who);

	if ((who.GetAI() != 0) &&
		(who.GetAI()->IsDerivedFrom(CombatAI::DefaultData) == true))
	{
		CombatAI* combat_ai = Cast_Object(CombatAI*,who.GetAI());
		#pragma warning(disable:4239)
		combat_ai->SetSquadOrders(Stuff::Auto_Ptr<SquadOrders>(0));
		#pragma warning(default:4239)
	}
}

void FocusFireSquad::SetEntityToIgnore(Adept::ObjectID objectid)
{
}

MWObject* FocusFireSquad::GetAutoTarget()
{
	if (m_AutoTarget < 0)
	{
		return (0);
	}

	Entity* entity = Adept::NameTable::GetInstance()->FindData(m_AutoTarget);

	if ((entity == 0) ||
		(entity->IsDestroyed() == true) ||
		(entity->IsDerivedFrom(MWObject::DefaultData) == false))
	{
		m_AutoTarget = -2;
		return (0);
	}

	return (Cast_Object(MWObject*,entity));
}
