#include "MW4Headers.hpp"

#include "mw4.hpp"
#include <Adept\Resource.hpp>
#include <Stuff\NotationFile.hpp>
#include "move_formation.hpp"
#include "abl.h"
#include "AI_Groups.hpp"
#include "combatai.hpp"
namespace ABL
{
	extern TypePtr      IntegerTypePtr;
}

namespace MW4AI
{

	stlport::vector<stlport::vector<Point3D> > *g_Formations = NULL;

	void InitFormation (void)
	{
		AutoHeap local_heap (g_MoverAIHeap);
		Adept::Resource *m_Resource;
		Stuff::NotationFile *m_NotationFile;
		stlport::vector<Point3D> curform;

		g_Formations = new stlport::vector<stlport::vector<Point3D> >;
		Check_Pointer (g_Formations);
		m_Resource = new Adept::Resource("ai\\ai.formations");
		Check_Object (m_Resource);

		Verify(m_Resource->DoesResourceExist());
		m_Resource->LoadData();
		m_NotationFile = new Stuff::NotationFile(m_Resource);

		NotationFile::PageIterator *instance_names = m_NotationFile->MakePageIterator();
		Check_Object(instance_names);
		Page *instance;
		while ((instance = instance_names->ReadAndNext()) != NULL)
		{
			Check_Object(instance);

			curform.clear ();
			Page::NoteIterator *iter;
			iter = instance->MakeNoteIterator ();
			Note *note;
			while ((note = iter->ReadAndNext ()) != NULL)
			{
				Point3D data;

				note->GetEntry (&data);
				curform.push_back (data);
			}
			delete iter;

			char name[256];
			char *temp;

			ABL::SymTableNodePtr constantIdPtr;
			sprintf (name,"formation_%s",(char *) instance->GetName ());
			temp = name;
			while (*temp != 0)
			{
				*temp = (char) tolower (*temp);
				temp++;
			}
			if ((constantIdPtr = ABL::searchSymTable(name, ABL::SymTableDisplay[0])) == NULL)
				constantIdPtr = ABL::enterSymTable(name, &ABL::SymTableDisplay[0]);

			constantIdPtr->defn.key = ABL::DFN_CONST;
			constantIdPtr->library = NULL;

			constantIdPtr->typePtr = ABL::setType(ABL::IntegerTypePtr);
			constantIdPtr->defn.info.constant.value.integer = g_Formations->size ();

			g_Formations->push_back (curform);
		}
		delete instance_names;
		delete m_NotationFile;
		delete m_Resource;
	}

	void ClearFormation (void)
	{
		delete g_Formations;
		g_Formations = NULL;
	}

	bool ValidFormation (int id)
	{
		Verify (g_Formations);
		if (!g_Formations)
			return false;
		if (id < 0)
			return false;
		if (id >= g_Formations->size ())
			return false;
		return true;
	}

	bool Form_Move (stlport::vector<MoverAI *>& GroupList,int PathID,int speed,int type,int form,int sparse,bool clear)
	{
		Scalar mult;
		MoverAI *leader;
		Scalar offx,offz;
		MoverAI *lastunit;
		stlport::vector<MoverAI *>::iterator groupiter;
		stlport::vector<Point3D>::iterator lociter;
		int minspeed = 10000;

		for (groupiter = GroupList.begin ();groupiter != GroupList.end ();groupiter++)
		{
			Vehicle *obj;

			obj = Cast_Object (Vehicle *,(*groupiter)->getEntity ());
			const Vehicle__GameModel *model = static_cast<const Vehicle__GameModel *> (obj->GetGameModel ());
			if (model->maxSpeed  < minspeed)
			{
				minspeed = (int) model->maxSpeed;
			}
		}
		minspeed /= 2;
		Max_Clamp (speed,minspeed);

		Verify (ValidFormation (form));
		if (!ValidFormation (form))
			return false;
		if (GroupList.size () == 0)
			return true;
		mult = 1;
		switch (sparse)
		{
		case FORMTYPE_SPARSE:
			mult = 60;
			break;
		case FORMTYPE_NORMAL:
			mult = 45;
			break;
		case FORMTYPE_DENSE:
			mult = 30;
			break;
		default:
				Verify (false);
			break;
		}
		Verify (PathID != -1);
		Path *path = Cast_Object (Path *,NameTable::GetInstance()->FindData (PathID));
		Check_Object (path);
		Verify (path->IsDerivedFrom (Path::DefaultData));

		leader = GroupList[0];
		Check_Object(leader);
		Verify (!leader->Dead ());
		groupiter = GroupList.begin ();
		groupiter++;
		lociter = (*g_Formations)[form].begin ();
		offx = 0;
		offz = 0;
		lastunit = leader;
		while ((groupiter != GroupList.end ()) && (lociter != (*g_Formations)[form].end ()))
		{
			Verify (*groupiter);
			Scalar curx,curz;
			curx = lociter->x * mult;
			curz = lociter->z * mult;
			curx -= offx;
			curz -= offz;
			(*groupiter)->orderMoveFollow (lastunit,(int) curx,(int) curz,1000);
			offx = lociter->x * mult;
			offz = lociter->z * mult;
			lastunit = (*groupiter);
 			groupiter++;
			lociter++;
		}
		while (groupiter != GroupList.end ())
		{
			if ((*groupiter)->IsDerivedFrom (CombatAI::DefaultData))
			{
				CombatAI *combat = Cast_Object (CombatAI *,(*groupiter));
				if (combat->OverridingMovement () && combat->Attacking ())
				{
					combat->OrderStopAttacking ();
				}
			}
			(*groupiter)->orderMoveResumePatrol (path,speed,type,true,clear);
			groupiter++;
		}
		if (leader->IsDerivedFrom (CombatAI::DefaultData))
		{
			CombatAI *combat = Cast_Object (CombatAI *,leader);
			if (combat->OverridingMovement () && combat->Attacking ())
			{
				combat->OrderStopAttacking ();
			}
		}
		leader->orderMoveResumePatrolSemi (path,speed,type,true,clear);
		return leader->orderMoveToSemi (path,speed,type,true,clear);
	}

	void Form_On_Spot (stlport::vector<MoverAI *>& GroupList,long* loc,long* dir,int speed,int form,int sparse,bool clear)
	{
		LinearMatrix4D matrix;
		Point3D location,extra,direction;
		UnitVector3D heading;
		Scalar mult;
		stlport::vector<MoverAI *>::iterator groupiter;
		stlport::vector<Point3D>::iterator lociter;

		location.x = (float) loc[0];
		location.y = (float) loc[1];
		location.z = (float) loc[2];
		direction.x = (float) dir[0];
		direction.y = (float) dir[1];
		direction.z = (float) dir[2];
		extra.Subtract (direction,location);
		Verify (!Small_Enough (extra.GetLength ()));
		heading = extra;
		matrix.BuildRotation (heading);
		matrix.BuildTranslation (location);

		Verify (ValidFormation (form));
		if (!ValidFormation (form))
			return;
		if (GroupList.size () == 0)
			return;
		mult = 1;
		switch (sparse)
		{
		case FORMTYPE_SPARSE:
			mult = 15;
			break;
		case FORMTYPE_NORMAL:
			mult = 30;
			break;
		case FORMTYPE_DENSE:
			mult = 45;
			break;
		default:
				Verify (false);
			break;
		}

		groupiter = GroupList.begin ();
		groupiter++;
		lociter = (*g_Formations)[form].begin ();
		while ((groupiter != GroupList.end ()) && (lociter != (*g_Formations)[form].end ()))
		{
			Verify (*groupiter);
			Point3D newloc;

			extra.x = (lociter->x*mult);
			extra.y = 0;
			extra.z = (lociter->z*mult);
			newloc.Multiply (extra,matrix);
			if ((*groupiter)->IsDerivedFrom (CombatAI::DefaultData))
			{
				CombatAI *combat = Cast_Object (CombatAI *,(*groupiter));
				if (combat->OverridingMovement () && combat->Attacking ())
				{
					combat->OrderStopAttacking ();
				}
			}
			(*groupiter)->orderMoveToLocPoint (newloc,speed,true,clear);
 			groupiter++;
			lociter++;
		}
		while (groupiter != GroupList.end ())
		{
			if ((*groupiter)->IsDerivedFrom (CombatAI::DefaultData))
			{
				CombatAI *combat = Cast_Object (CombatAI *,(*groupiter));
				if (combat->OverridingMovement () && combat->Attacking ())
				{
					combat->OrderStopAttacking ();
				}
			}
			(*groupiter)->orderMoveToLocPoint (location,speed,true,clear);
			groupiter++;
		}
	
	}
}

