//===========================================================================//
// File:	mechai.hpp																	     //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
//	07/13/99  AHF	Created File                  //
//---------------------------------------------------------------------------//
// Copyright (C) 1999, Microsoft										 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#pragma once

#include "ai.hpp"
#include "aiutils.hpp"
#include "rail_move.hpp"
#include "vehicle.hpp"
#include "path.hpp"
#include "aiMoveData.hpp"
#include "obstacle.hpp"

namespace MechWarrior4
{
	class Dropship;
	class FieldBase;
	class CCheapMover;
	typedef MechWarrior4::AI__CreateMessage			MoverAI__CreateMessage;

	//##########################################################################
	//###########################   MoverAI    #################################
	//##########################################################################

	class MoverAI : public MechWarrior4::AI
	{
		typedef AI inherited;
		friend class CMoveData;
		friend class CPatrolData;
		friend class CSemiPatrolData;
		friend class CRigidPatrolData;
		friend class CFollowData;
		friend class CLookoutData;
		friend class CFleeData;
		friend class CSitData;
		friend class CMoveLocPointData;
		friend class CMoveObjectData;

	public:
		static void InitializeClass();
		static void TerminateClass();

		static ClassData *DefaultData;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Inheritance Support
	//
	public:
		typedef MoverAI__CreateMessage					CreateMessage;


	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Run-time Construction and Destruction Support
	//
	public:
		virtual void Save (MemoryStream *stream);
		virtual void Load (MemoryStream *stream);
		static MoverAI* Make(CreateMessage *message,ReplicatorID *base_id);
	
		virtual void NotifyRespawned();

	protected:
		MoverAI(ClassData *class_data,CreateMessage *message,ReplicatorID *base_id,ElementRenderer::Element *element);

		virtual ~MoverAI();

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Message Support
	//
	public:

		enum {
			DebugTextMessageID = MechWarrior4::AI::NextMessageID,
			NextMessageID
		};

		void DebugTextMessageHandler(Adept::ReceiverDataMessageOf<int> *message);

		protected:
			static const MessageEntry MessageEntries[];

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// MoveHead Support
	//
	protected:
		enum HeadHit {
			LEFT_INSIDE_HIT,
			RIGHT_INSIDE_HIT,
			TOP_INSIDE_HIT,
			BOTTOM_INSIDE_HIT,
			LEFT_OUTSIDE_HIT,
			RIGHT_OUTSIDE_HIT,
			TOP_OUTSIDE_HIT,
			BOTTOM_OUTSIDE_HIT,
			NO_HIT
		};
		Stuff::Point3D m_TopLeft,m_TopRight,m_BottomLeft,m_BottomRight;		// coords of rectangle top is start
		Stuff::Point3D m_StartPoint,m_EndPoint;

		void ConstructRect (const Stuff::Point3D& start,const Stuff::Point3D& end,Stuff::Scalar width);
		HeadHit HeadPointInRect (const LinearMatrix4D& loc,Scalar speed,Stuff::Scalar delta);
		bool Intersect (const Stuff::Point3D& start,const Stuff::Point3D& end,const Stuff::Point3D& p1,const Stuff::Point3D& p2);
		Stuff::Point3D LeftHeadPoint (const Point3D& loc,const Stuff::Point3D& head);
		Stuff::Point3D RightHeadPoint (const Point3D& loc,const Stuff::Point3D& head);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Execution Support
	//
	protected:
		static bool m_AIJumpAllowed;
		MechWarrior4::Vehicle *m_Vehicle;
		MechWarrior4::MWObject *m_MWObject;
		CMoveData *m_CurMove;
		MW4AI::CRailPath *m_CurPath;
		Stuff::Point3D m_MoveHead,m_OutVec,m_OldMoveHead;
		Stuff::Point3D m_NewMoveHead,m_NewOutVec;
		Stuff::Scalar m_SpeedRequest,m_NewSpeedRequest;
		Stuff::Scalar m_EndSpeedRequest, m_NewEndSpeedRequest;
		Stuff::Scalar m_OrderSpeed;
		Stuff::Time m_StartJump,m_BurnTime;
		bool m_NeedHeadingAdjust,m_NeedSpeedAdjust;
		bool m_Jumping;
		bool m_JumpRequested;
		int m_FailCount;							// # of times path calc has failed for current path
		CCheapMover *m_CheapMover;
		Stuff::Scalar m_TimeNotMoving;

		Stuff::Point3D m_CombatLeash;
		Stuff::Scalar m_CombatLeashRadius;

		virtual void SetOrderSpeed (Stuff::Scalar newvalue);
		virtual void ExecutePath (Stuff::Time till);
		virtual void ExecuteMoveCommand (Stuff::Time till);
		virtual void ExecuteMoveHead (Stuff::Time till);
		virtual void ExecuteMoveHeadCollision (Stuff::Time till);
		virtual void ExecutePostPath (Stuff::Time till);
		virtual void PathDone (Stuff::Time till,bool failed=false);
		virtual bool Jump (Stuff::Time till,const Stuff::Point3D& dest,bool request=false);

		virtual bool RequestJump (Stuff::Time till);

		virtual bool AllowTenMeter (void);

	public:
		virtual void PreCollisionExecute(Stuff::Time till);
		virtual void Info (void (*printCallback)(char* s));
		virtual void Die (void);
//		LinearMatrix4D GetAILocalToWorld (void);
		bool ReactToCollision(Stuff::DynamicArrayOf<CollisionData> *collisions,bool& shoulddamage);		// return false to not handle collision

		MechWarrior4::Vehicle *getVehicle (void)
		{ return m_Vehicle;	}

		MW4AI::CRailPath* getCurPath (void)
		{ return m_CurPath; }
		
		virtual void TurnOn (void);
		virtual void TurnOff (void);

		inline void SetCombatLeash(const Stuff::Point3D& center, Stuff::Scalar radius);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Dropship Support
	//
	public:
		enum DROP_STATE {
			DROP_NOT_ACTIVE,				// not boarding a dropship
			DROP_FIRST_POINT,				// approaching wait point
			DROP_SECOND_POINT,			// approaching point outside doors
			DROP_THIRD_POINT,				// approaching center of dropship
			DROP_FOURTH_POINT,			// approaching final point
			DROP_DONE								// done boarding
		};
	protected:
		Stuff::Point3D m_DropShipWait;				// point to wait to board drop ship
		Stuff::Point3D m_DropShipBoard;				// point outside doors
		Stuff::Point3D m_DropShipCenter;			// point at center of dropship
		Stuff::Point3D m_DropShipFinal;				// final point to stand in drop ship
		DROP_STATE m_BoardingDropShip;
		std::vector<MoverAI *> m_OtherDropShipBoarders;
		int m_DropShipID;											// which of the five we are
		Dropship *m_DropShip;
		bool m_ReadyToStart;									// can be told to start boarding have reached wait point
		bool m_CanStartBoard;									// previous person has reached center, so can start boarding
		
		void NextBoardDropShip (void);
		void CheckDropBoard (Stuff::Time delta);
		void StartDropShipBoard (void)
		{ m_CanStartBoard = true; }


	public:
		void AddDropshipBoarder (int id,MoverAI *who);
		void CreateDropShipPoints (int id,Dropship *drop);
		void PrepareBoardDropShip (void);
		DROP_STATE DropState (void) const
		{ return m_BoardingDropShip; }

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// FieldBase Support
	//
	public:
		enum FIELDBASE_STATE {
			FIELDBASE_NOT_ACTIVE,				// not entering a field base
			FIELDBASE_ENTER,						// entering a fieldbase
			FIELDBASE_HEAL,							// healing in fieldbase
			FIELDBASE_EXIT,							// exiting a fieldbase
			FIELDBASE_DEAD							// exiting from dead fieldbase
		};
	protected:
		Stuff::Point3D m_FieldBaseEnter;				// point to wait to board drop ship
		Stuff::Point3D m_FieldBaseExit;				// point outside doors
		FIELDBASE_STATE m_EnteringFieldBase;
		FieldBase *m_FieldBase;
		bool m_FieldBaseReverse;

		void CheckFieldBase (Stuff::Time delta);

	public:
		void FieldBaseDead (void);
		void NextFieldBase (bool frommover=false);
		bool CreateFieldBasePoints (FieldBase *base);
		void PrepareEnterFieldBase (void);
		FIELDBASE_STATE FieldBaseState (void) const
		{ return m_EnteringFieldBase; }
		virtual void FinishFieldBase (void);

		static bool g_AlwaysIgnoreObstacles;

		bool ExitedFieldBaseRecently() const;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Obstacle Support
	//
		protected:
			bool m_PathBlocked;
			MW4AI::CPathRequest *m_ObstacleRequest;

			MWObject *m_RamTarget;
			Stuff::Point3D m_PathBlockedCell;
			MW4AI::Rect4D *m_PathBlockedRect;
			int	m_PathBlockedRectTimeDelta;
			stlport::vector<Entity *> m_ObstacleList;
			stlport::vector<MW4AI::LockData*> m_PathLock;				// in BLOCK_GRID_RES coordinates
			stlport::vector<MW4AI::Rect4D *> m_LockedRects;
			Stuff::Time m_LockTime;
			Stuff::Time m_RecalcTime;
			bool m_PathBad;
			stlport::vector<MW4AI::Rect4D *> m_TempLockRects;
//			bool m_ObstacleAvoidMode;
			Point3D m_ObstacleAvoidPoint;
			stlport::vector<MW4AI::LockData *> m_TempLock;				// in BLOCK_GRID_RES coordinates

			Stuff::Scalar m_LastTimeFinishedFieldBase;
			bool m_ShouldEndFieldBase;

//			void CheckObstacle (Stuff::Time till);
//			void AddObstacle (Entity *);
			virtual bool PointIsOK(const Stuff::Point3D& point) const;
			bool LockSquare (const Point3D& loc,Stuff::Scalar rad,Point3D& culprit);

			bool ObstacleNearBy (const Point3D& loc);
			void LancemateFieldBasePoint (Stuff::Point3D &pt,FieldBase *base);

			bool CheckSquare (const Point3D& loc,Stuff::Scalar rad,int dir,int mask,bool tenmeter);

			void AddTempLockRect (MW4AI::Rect4D *area)
			{ m_TempLockRects.push_back (area); }
			void AddTempLockRect (const MW4AI::Rect4D& area)
			{
				MW4AI::Rect4D *rect;
				rect = new MW4AI::Rect4D (area);
				m_TempLockRects.push_back (rect);
			}
			void ClearTempLockRects (void);
			void CalcTempLock (void);
			void CalcUnTempLock (void);

			virtual void NotifyNoPath(void);
			virtual void NotifyReachedDestination(void);
			virtual void NotifyCollided(Entity *who);
			bool LockedByMe (const Stuff::Point3D& loc) const
			{
				return (MW4AI::Blocker (loc) == (MWObject *) this);
			}
			bool AddLockCell (const Stuff::Point3D& loc);			// true if can lock cell
			bool AddLockRect (MW4AI::Rect4D *p1,bool carecollide,Stuff::Time till);

			void ClearRamLock (void);
			void RamLock (void);
			bool EqualRamTarget (Entity *target);
			virtual void HandleObstacle (MW4AI::Rect4D *p1,Adept::Entity *p2);

		public:
			virtual void ClearPathLock (bool quitting,bool fromram);
			virtual bool PathLock (bool secondpass,bool fromram);
			virtual void CalcPathLock (MW4AI::CPathRequest *reg);
			virtual void CalcPathUnlock (MW4AI::CPathRequest *reg);
			void RecalcPath (Stuff::Time till);
			void SetRamTarget (MWObject *obj);
			void SlopeCollide (void);
			void HandleObstacle (MW4AI::Rect4D *p1)
			{ HandleObstacle (p1,NULL); }
			void HandleObstacle (Adept::Entity *p1)
			{ HandleObstacle (NULL,p1); }
			void HandleObstacle (void)
			{ HandleObstacle (NULL,NULL); }


		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Order Support
		//
		public:
		static void DisableAIJumping (void)
		{		
			m_AIJumpAllowed = false;
		}

		virtual void Shutdown (void)
		{
			ClearMoveOrder ();
			inherited::Shutdown ();
		}

		virtual void 	orderMoveLookOut (void);
		virtual bool	orderMoveTo (Path *,int speed,int type,bool forwards,bool clear);
		virtual bool	orderMoveToSemi (Path *,int speed,int type,bool forwards,bool clear);
		virtual bool	orderMoveToRigid (Path *,int speed,int type,bool forwards,bool clear);
		virtual int 	orderMoveFlee (Entity *away,int speed);
		virtual bool 	orderMoveResumePatrol (Path *,int speed,int type,bool forwards,bool clear);
		virtual bool 	orderMoveResumePatrolSemi (Path *,int speed,int type,bool forwards,bool clear);
		virtual bool 	orderMoveResumePatrolRigid (Path *,int speed,int type,bool forwards,bool clear);
		virtual void 	orderMoveFollow (Entity *who,int offsetx,int offsetz,int maxspeed);
		virtual void 	orderMoveSit (bool shutdown);
		virtual bool 	orderMoveToLocPoint (const Stuff::Point3D& where,int speed,bool forwards,bool clear);
		virtual bool 	orderMoveToObject (Entity *what,int speed);
		virtual void ClearMoveOrder (void);
		virtual void NewPath (MW4AI::CRailPath *path,bool overridemove=false);

		protected:
			virtual Stuff::Scalar GetMinSpeed() const { return (0);	}

		public:
			virtual void SetMoveHead (const Stuff::Point3D& head,Stuff::Scalar speed,const Stuff::Point3D& outvec = Stuff::Point3D (-1,-1,-1),Stuff::Scalar endspeed = 0);

		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Command Support
		//

	protected:

		virtual void ExecuteMoveCommand (MW4AI::CommandEntry *command);

		virtual void ExecuteHighCommand (MW4AI::CommandEntry *command);
		virtual void ExecuteLowCommand (MW4AI::CommandEntry *command);
		
		MW4AI::CommandEntry *CreateMoveToCommand (const Stuff::Point3D& where,Stuff::Time start = gos_GetElapsedTime ())
		{
			MW4AI::CommandEntry *toret;

			toret = CreateBaseCommand (MOVETO_LOWID,false,start);

			Check_Pointer (toret);
			toret->DataPoint3D (where);
			return toret;
		}
		MW4AI::CommandEntry *CreateTurnToCommand (const Stuff::Point3D& where,bool reverse,Stuff::Time start = gos_GetElapsedTime ())
		{
			MW4AI::CommandEntry *toret;

			toret = CreateBaseCommand (TURNTO_LOWID,false,start);

			Check_Pointer (toret);
			toret->DataPoint3D (where);
			toret->DataBoolean (reverse);
			return toret;
		}
		MW4AI::CommandEntry *CreateTrackCommand (const Stuff::Point3D& where,Stuff::Time start = gos_GetElapsedTime ())
		{
			MW4AI::CommandEntry *toret;

			toret = CreateBaseCommand (TRACK_LOWID,false,start);

			Check_Pointer (toret);
			toret->DataPoint3D (where);
			return toret;
		}
		MW4AI::CommandEntry *CreateKillTurnToCommand (Stuff::Time start = gos_GetElapsedTime ())
		{
			MW4AI::CommandEntry *toret;

			toret = CreateBaseCommand (KILLTURNTO_LOWID,false,start);

			Check_Pointer (toret);
			return toret;
		}
		MW4AI::CommandEntry *CreateKillTrackCommand (Stuff::Time start = gos_GetElapsedTime ())
		{
			MW4AI::CommandEntry *toret;

			toret = CreateBaseCommand (KILLTRACK_LOWID,false,start);

			Check_Pointer (toret);
			return toret;
		}
		MW4AI::CommandEntry *CreateTrackToCommand (Stuff::Scalar amount,Stuff::Time start = gos_GetElapsedTime ())
		{
			MW4AI::CommandEntry *toret;

			toret = CreateBaseCommand (TRACKTO_LOWID,false,start);

			Check_Pointer (toret);
			toret->DataScalar (amount);
			return toret;
		}
		MW4AI::CommandEntry *CreateTurnCommand (Stuff::Scalar amount,Stuff::Time start = gos_GetElapsedTime ())
		{
			MW4AI::CommandEntry *toret;

			toret = CreateBaseCommand (TURN_LOWID,false,start);

			Check_Pointer (toret);
			toret->DataScalar (amount);
			return toret;
		}
		MW4AI::CommandEntry *CreateSetSpeedCommand (Stuff::Scalar amount,Stuff::Time start = gos_GetElapsedTime ())
		{
			MW4AI::CommandEntry *toret;

			toret = CreateBaseCommand (SETSPEED_LOWID,false,start);

			Check_Pointer (toret);
			toret->DataScalar (amount);
			return toret;
		}

	public:
		enum
		{
			TELEPORT_HIGHID = MechWarrior4::AI::NextHighCommandID,
			MOVE_HIGHID,
			FLEE_HIGHID,
			FORMONSPOT_HIGHID,
			FOLLOW_HIGHID,
			NextHighCommandID
		};

		enum
		{
			MOVETO_LOWID = MechWarrior4::AI::NextLowCommandID,
			TURNTO_LOWID,
			KILLTURNTO_LOWID,
			TRACK_LOWID,
			TRACKTO_LOWID,
			KILLTRACK_LOWID,
			TURN_LOWID,
			SETSPEED_LOWID,
			NextLowCommandID
		};
		MW4AI::CommandEntry *CreateTeleportCommand (Stuff::Point3D where,Stuff::Time start = gos_GetElapsedTime ())
		{
			MW4AI::CommandEntry *toret;

			toret = CreateBaseCommand (TELEPORT_HIGHID,true,start);

			Check_Pointer (toret);
			toret->DataPoint3D (where);
			return toret;
		}
		MW4AI::CommandEntry *CreateMoveCommand (CMoveData *data,Stuff::Time start = gos_GetElapsedTime ())
		{
			MW4AI::CommandEntry *toret;

			toret = CreateBaseCommand (MOVE_HIGHID,true,start);

			Check_Pointer (toret);
			toret->DataPtr (data);
			return toret;
		}
		MW4AI::CommandEntry *CreateFleeCommand (Stuff::Scalar amount,Stuff::Time start = gos_GetElapsedTime ())
		{
			return CreateBaseCommand  (FLEE_HIGHID,true,start);
		}
		MW4AI::CommandEntry *CreateFormOnSpotCommand (Stuff::Scalar amount,Stuff::Time start = gos_GetElapsedTime ())
		{
			return CreateBaseCommand  (FORMONSPOT_HIGHID,true,start);
		}
		MW4AI::CommandEntry *CreateFollowCommand (Stuff::Scalar amount,Stuff::Time start = gos_GetElapsedTime ())
		{
			return CreateBaseCommand  (FOLLOW_HIGHID,true,start);
		}

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Test Support
	//
	public:
		void TestInstance() const;
	};

	inline void MoverAI::SetCombatLeash(const Stuff::Point3D& center, Stuff::Scalar radius)
	{
		m_CombatLeash = center;
		m_CombatLeashRadius = radius;
	}
}

