//
// HPB_bot - botman's High Ping Bastard bot
//
// (http://planethalflife.com/botman/)
//
// waypoint.h
//

#ifndef WAYPOINT_H
#define WAYPOINT_H

#include <limits.h>

#define MAX_WAYPOINTS 1024

#define REACHABLE_RANGE 1750.0

// defines for waypoint flags field (32 bits are available)
#define W_FL_TEAM        ((1<<0) + (1<<1))  /* allow for 4 teams (0-3) */
#define W_FL_TEAM_SPECIFIC (1<<2)  /* waypoint only for specified team */
#define W_FL_CROUCH      (1<<3)  /* must crouch to reach this waypoint */
#define W_FL_LADDER      (1<<4)  /* waypoint on a ladder */
#define W_FL_LIFT        (1<<5)  /* wait for lift to be down before approaching this waypoint */
#define W_FL_DOOR        (1<<6)  /* wait for door to open */
#define W_FL_HEALTH      (1<<7)  /* health kit (or wall mounted) location */
#define W_FL_ARMOR       (1<<8)  /* armor (or HEV) location */
#define W_FL_AMMO        (1<<9)  /* ammo location */
#define W_FL_SNIPER      (1<<10) /* sniper waypoint (a good sniper spot) */

#define W_FL_FLAG        (1<<11) /* flag position (or hostage or president) */
#define W_FL_FLF_CAP     (1<<11) /* Front Line Force capture point */

#define W_FL_FLAG_GOAL   (1<<12) /* flag return position (or rescue zone) */
#define W_FL_FLF_DEFEND  (1<<12) /* Front Line Force defend point */

#define W_FL_PRONE       (1<<13) /* go prone (laying down) */
#define W_FL_AIMING      (1<<14) /* aiming waypoint */

#define W_FL_SENTRYGUN   (1<<15) /* sentry gun waypoint for TFC */
#define W_FL_DISPENSER   (1<<16) /* dispenser waypoint for TFC */

#define W_FL_WEAPON      (1<<17) /* weapon_ entity location */
#define W_FL_JUMP        (1<<18) /* jump waypoint */

#define W_FL_DELETED     (1<<31) /* used by waypoint allocation code */


#define WAYPOINT_VERSION 4

#define WAYPOINT_UNREACHABLE   USHRT_MAX
#define WAYPOINT_MAX_DISTANCE (USHRT_MAX-1)

#define MAX_PATH_INDEX 4

struct waypoint_header_s
{
   char szFileType[8];  // should be "HPB_bot\0"
   int iWPFileVersion;
   int iWPFileFlags;  // not currently used
   int iWPCount;
   char szMapname[32];  // name of map for these waypoints
};


struct waypoint_s
{
	int iFlags;
	Vector v_origin;
};

// define the structure for waypoint paths (paths are connections between
// two waypoint nodes that indicates the bot can get from point A to point B.
// note that paths DON'T have to be two-way.  sometimes they are just one-way
// connections between two points.  There is an array called "paths" that
// contains head pointers to these structures for each waypoint index.
struct waypoint_path_s
{
	short sIndex[MAX_PATH_INDEX]; // indexes of waypoints (index -1 means not used)
	waypoint_path_s *next; // link to next structure
};

struct waypoint_info_s
{
	waypoint_s Waypoint;
	waypoint_path_s *pWaypointPaths;
	float flLastDrawTime;
};

class CWaypointClass
{
	bool m_bDrawWaypoints;
	bool m_bDrawWPPaths;
	bool m_bAutoWaypoint;
	bool m_bAutoPlacePaths;
	Vector m_vLastCreatedWP;
	float m_fPathDrawTime;
	unsigned int m_iRouteWaypointCount;

	// Floyd path stuff
	unsigned short *m_pFromTo[4]; // 4 teams, incase esf will ever have more then 2 :)
	unsigned short *m_pShortestPath[4]; // 4 teams, incase esf will ever have more then 2 :)

public:
	waypoint_info_s m_Waypoints[MAX_WAYPOINTS];
	int m_iWaypointCount; // number of waypoints currently in use

	CWaypointClass(void);
	~CWaypointClass(void) { Clear(); };

	void Think(void);
	void Init(void);
	void Clear(void);
	bool LoadWaypoints(void);
	void SaveWaypoints(void);

	void SetWaypointsVisible(bool Visible) { m_bDrawWaypoints = Visible; };
	void SetPathsVisible(bool Visible) { m_bDrawWPPaths = Visible; };
	void SetAutoWaypoint(bool On) { m_bAutoWaypoint = On; };
	bool WaypointsAreVisible(void) { return m_bDrawWaypoints; };
	void AddWaypoint(CBasePlayer *pPlayer);
	void DeleteWaypoint(CBasePlayer *pPlayer);
	void AddPath(short sWPIndex, short sWPIndex2);
	void DeletePath(short Index);
	void DeletePath(short WPIndex1, short WPIndex2);
	void ManuallyCreatePath(CBasePlayer *pPlayer, int iCmd, bool TwoWay);
	void ManuallyDeletePath(CBasePlayer *pPlayer, int iCmd, bool TwoWay);

	bool WPIsReachable(CBasePlayer *pPlayer, short sWaypointIndex);
	int GetNearestWaypoint(CBasePlayer *pPlayer, float flRange, short sTeam);
	int GetNearestWaypoint(Vector v_src, CBaseEntity *pEntity, float range, int team);

	// (Floyds) Path functions
	void RouteInit(void);
	void CalculatePaths(unsigned short *pShortestPath, unsigned short *pFromTo);
	unsigned short RouteFromTo(int src, int dest, int Team);
	int FindPath(waypoint_path_s **pPath, int *path_index, int waypoint_index, int team);
};

// experimental stuff
#define MAX_FLOOD_WPS			25000
#define FLOODWP_UNASSIGNED		-1
#define FLOODWP_UNREACHEBLE		-2
#define MAX_MAP_GRIDS			32
#define SECTOR_SIZE				256
#define MAX_FLOODWPCONNECTDIST	80.0f
#define MIN_FLOODWPDIST			150.0f
#define MAX_FLOODWPDIST			250.0f
#define START_NEXTNODEDIST		50.0f

// Flood WP flags
#define FL_INOPEN		(1<<0)
#define FL_INCLOSED		(1<<1)
#define FL_TELEPORT		(1<<2)
#define FL_TELEPORTDEST	(1<<3)

struct flood_wp_s
{
	Vector v_origin;
	flood_wp_s *pConnectedWPs[6];
	byte Flags;
	float flNextNodeDist;

	// A* stuff
	float f, g;
	flood_wp_s *pParent;
	flood_wp_s *pGoalParent;
	int iClosedIndexes; // Contains the bot ent indexes in which closed list this node is
	int iOpenIndexes; // Contains the bot ent indexes in which closed list this node is
	int iGoalClosedIndexes; // Contains the bot ent indexes in which closed list this node is
	int iGoalOpenIndexes; // Contains the bot ent indexes in which closed list this node is

	flood_wp_s(void) : v_origin(Vector(0, 0, 0)), Flags(0), pParent(NULL), f(0.0f), g(0.0f),
					   iClosedIndexes(0), iOpenIndexes(0), iGoalClosedIndexes(0),
					   iGoalOpenIndexes(0), pGoalParent(NULL) {};
};

enum EFloodDir
{
	FDIR_LEFT=0,
	FDIR_RIGHT,
	FDIR_FORWARD,
	FDIR_BACK,
	FDIR_UP,
	FDIR_DOWN,
	FDIR_NONE
};

class CWaypointFlooder
{
	Vector m_vLookAngles;
	TLinkedList<flood_wp_s*> m_FloodWPs[MAX_MAP_GRIDS][MAX_MAP_GRIDS][MAX_MAP_GRIDS];
	TLinkedList<flood_wp_s*> m_FloodWPsToCheck;
	bool m_bFlooding;
	float m_fWPDrawDelay;

	flood_wp_s *GetNearest(Vector v_origin, float flRange, flood_wp_s *pIgnore);
	flood_wp_s *GetNearestToConnectWith(flood_wp_s *pCurrent, Vector v_dest, float flRange);
	flood_wp_s *GetNearestTeleDestFloodWP(Vector v_origin, float flRange);
	flood_wp_s *GetNearestFromDir(Vector v_origin, float flRange, EFloodDir Dir,
								  flood_wp_s *pIgnore);
	flood_wp_s *PlaceFloodWP(Vector v_origin, flood_wp_s *pParent, EFloodDir dir);
	CBaseEntity *GetTeleporterFromWP(flood_wp_s *pFloodWP);
	short GetFreeSlot(flood_wp_s *pFloodWP);
	bool FloodLevel(void);
	void GetFloodWPIndexes(Vector v_origin, short *i, short *j, short *k);
	bool CanPlaceNodeAt(Vector v_origin);

public:
	int m_iFloodWPCount;

	CWaypointFlooder(void) : m_iFloodWPCount(0), m_bFlooding(false), m_fWPDrawDelay(0.0f) {};
	~CWaypointFlooder(void) { DestroyFloodWps(); };
	void StartFlood(void);
	void FloodThink(void);
	void DrawNearFloodWPs(void);
	void DestroyFloodWps(void);
	int GetNodeCountWithnConnections(short n);
	bool ConnectionsAreValid(void);

	flood_wp_s *GetNearestFloodWP(Vector v_origin, float flRange);
};

#endif // WAYPOINT_H
