#include "MW4Headers.hpp"
#include "Adept\AdeptHeaders.hpp"

#include "mw4.hpp"
#include "moverai.hpp"
#include "Adept\Interface.hpp"
#include "Adept\Application.hpp"
#include "Adept\player.hpp"
#include "torso.hpp"

#include "aimovedata.hpp"

using namespace MechWarrior4;
using namespace MW4AI;
#pragma warning (push)
#include <algorithm>
#pragma warning (pop)

void CFleeData::Save (MemoryStream *stream)
{
	*stream << m_FleeLoc;
	if (m_From)
	{
		*stream << true;
		*stream << m_From->objectID;
	}
	else
		*stream << false;
	CMoveData::Save (stream);
}

void CFleeData::Load (MemoryStream *stream)
{
	ObjectID id;
	bool flag;

	*stream >> m_FleeLoc;
	*stream >> flag;
	if (flag)
	{
		*stream >> id;
		m_From = Cast_Object (MWObject *,NameTable::GetInstance()->FindData(id));
	}
	CMoveData::Load (stream);
}
CFleeData::CFleeData (MW4AI::CRailGraph *graph,MoverAI *owner) : CMoveData (graph,owner)
{
	m_From = NULL;
	m_MoveType = MOVE_FLEE;
}

CFleeData::~CFleeData (void)
{
	m_From = NULL;
}

void CFleeData::InsertObject (Entity *who)
{
	m_From = who;
	inherited::InsertObject (who);
}

void CFleeData::StartExecuting (MoverAI *ai,Stuff::Time till)
{
	CPathRequest *req;
	CRailPath *path;

	inherited::StartExecuting (ai,till);
	req = Request ();
	if (!req->Done ())
	{
		Executing (false);
		return;
	}
	path = req->NodePath ();
	ai->NewPath (req->DetachNodePath ());
}
void CFleeData::PathDone (MoverAI *ai,Stuff::Time till,bool failed)
{

	Verify (ai->m_Vehicle);
	inherited::PathDone (ai,till,failed);
	Done (true);
}

void CFleeData::CreateRequests (const Stuff::Point3D& start,int usageflags)
{
	Point3D loc,third,fred;

	Check_Object (m_From);
	loc = (Point3D) m_From->GetLocalToWorld ();

	inherited::CreateRequests (start,usageflags);
	third.Subtract (start,loc);
	third.y = 0;
	fred.Normalize (third);
	fred *= 500;
	m_FleeLoc.Add (fred,start);
	ConvertPointToGrid (m_FleeLoc);

	if (m_AI->m_CombatLeashRadius != 0)
	{
		m_AI->m_CombatLeash.y = m_FleeLoc.y;
		if (GetApproximateLength(m_FleeLoc,m_AI->m_CombatLeash) > m_AI->m_CombatLeashRadius)
		{
			Point3D point(Stuff::Random::GetFraction() * m_AI->m_CombatLeashRadius,
						  m_AI->m_CombatLeash.y,
						  Stuff::Random::GetFraction() * m_AI->m_CombatLeashRadius);

			Point3D point2(-point.x,point.y,point.z);
			Point3D point3(point.x,point.y,-point.z);
			Point3D point4(-point.x,point.y,-point.z);

			std::vector<Point3D> points;
			points.push_back(point);
			points.push_back(point2);
			points.push_back(point3);
			points.push_back(point4);

			Scalar best_dist = 0;
			int best_index = 4;
			{for (int i = 0;
				  i < 4;
				  ++i)
			{
				points[i].x += m_AI->m_CombatLeash.x;
				points[i].z += m_AI->m_CombatLeash.z;

				Scalar dist(GetLengthSquared(points[i],m_FleeLoc));

				if ((dist < best_dist) ||
					(best_dist == 0))
				{
					best_index = i;
					best_dist = dist;
				}
			}}

			if (best_index == 4)
			{
				best_index = 0;
			}

			m_FleeLoc = points[best_index];
		}
	}

	CRailNode *node = m_Graph->FindClosestNode (m_FleeLoc);
	if (!node)
	{
		Done (true);
		return;
	}
	m_FleeLoc = node->Location();
	Stuff::Scalar rad = node->Radius ();
	rad *= 1.5f;
	rad -= (rad*0.5f);
	m_FleeLoc.x += Stuff::Random::GetFraction () * rad;
	m_FleeLoc.z += Stuff::Random::GetFraction () * rad;

	if (m_FleeLoc.x < 50.0f)
		m_FleeLoc.x = 50.0f;
	if (m_FleeLoc.z < 50.0f)
		m_FleeLoc.z = 50.0f;
	if (m_FleeLoc.x > (MW4AI::MaxX-50.0f))
		m_FleeLoc.x = (MW4AI::MaxX-50.0f);
	if (m_FleeLoc.z > (MW4AI::MaxZ-50.0f))
		m_FleeLoc.z = (MW4AI::MaxZ-50.0f);

	m_PathRequest = g_PathManager->AddRequest (gos_GetElapsedTime (),m_Graph,start,m_FleeLoc,usageflags,m_AI,m_AI->AllowTenMeter ());
}
void CFleeData::CreatePlaneRequests (const Stuff::Point3D& start)
{
	Point3D loc,third,fred;

	loc = (Point3D) m_From->GetLocalToWorld ();

	third.Subtract (start,loc);
	fred.Normalize (third);
	fred *= 500;
	m_FleeLoc = fred;
	ConvertPointToGrid (m_FleeLoc);
	inherited::CreatePlaneRequests (start);
}
bool CFleeData::operator== (const CFleeData& p1)
{
	if (!inherited::operator== ((inherited)p1))
		return false;
	if (m_From->objectID == p1.m_From->objectID)
		return false;
	return true;
}

Stuff::Point3D CFleeData::Location (int id)
{
	return m_FleeLoc;
}

int CFleeData::LocationSize (void) const
{
	return 1;
}

int CFleeData::FleeDistance (Entity *src)
{
	Check_Object (src);
	Check_Object (m_From);
	return(int) src->GetDistanceFrom (m_From);
}
