/*
 *  acm : an aerial combat simulator for X
 *  Copyright (C) 1991-1998  Riley Rainey
 *  Copyright (C) 1995  Mats Lofkvist  CelsiusTech Electronics AB
 *  Additions Copyright (c) 1998 Web Simulations, Inc.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; version 2 dated June, 1991.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program;  if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave., Cambridge, MA 02139, USA.
 */

/**
 * Interface routines between the ACM program and the DIS protocol that
 * manages an internal array of local and remote entities.
 * 
 * <h2>Entity ID or entity handle.</h2>
 * The "entity ID", or "eid" for short, is an handle of type int assigned by
 * this module to each tracked DIS entity, either local or remote, be it a craft,
 * a missile or a projectile. The zero value IS NOT a valid entity ID and can be
 * used to indicate "no DIS entity assigned"; the dis_if_ID_NONE macro makes
 * this value more apparent. This entity ID should not be confused with the
 * "DIS entity ID", this latter being part of the DIS protocol.
 * 
 * <h2>DIS entity identification</h2>
 * All the participants to the simulation must share the same exercise ID.
 * Each participant shall generate its own local entities and shall send its own
 * state to the other participants; each entity must be univocally identified
 * by: DIS application ID, DIS site ID and DIS entity ID. The first 2 parameters
 * can be set once for all in the initialization function of this module.
 * The DIS entity ID is a counter assigned by the DIS library to each new DIS
 * entity registered by the local application. Then, each participant MUST have
 * a distinct application and site IDs, and MUST share the same exercise ID.
 * 
 * <h2>Local entities.</h2>
 * A local entity (aircraft or missile) is registered in this module by ACM
 * by calling dis_if_entityEnter(), updated with dis_if_setEntityState() and finally
 * killed with dis_if_entityExit(). This module will take care to send all the
 * corresponding DIS packets to the participants.
 * The state of the local entities is broadcasted to the other players
 * periodically or after any meaningful state change according to the set
 * DR thresholds. The only exception to this rule are the cannon shells: each
 * cannon burst creates a "craft" that carries a certain number of rounds, but
 * each burst is not a tracked entity and each client is responsible for
 * simulating its dynamics once a "fire" event PDU is received from a remote
 * client.
 * 
 * <h2>Remote entities.</h2>
 * ACM must also call dis_if_receive() to process incoming DIS packets. New
 * entities entering and old entities exiting the simulation are notified to ACM
 * through the callback function it specified in the initialization functions.
 * ACM may then call dis_if_getEntityState() to retrieve a "dead reckoning" DR
 * updated state of the remote entity.
 * "Silent" remote entities are removed automatically after some time; ACM is
 * notified as usual about that.
 * 
 * <h2>Credits</h2>
 * This module was originally written by Mats Lofkvist specifically for the ACM
 * program and then maintained by Riley Rainey up to ACM 5. Source cleaning
 * and documentation integrated by Umberto Salsi for the ACM 6 release.
 * 
 * @author Mats Lofkvist -- CelsiusTech Electronics AB
 * @author Riley Rainey
 * @license GNU GPL
 * @version $Date: 2020/01/08 06:03:07 $
 * @file
 */

#ifndef dis_if_H
#define dis_if_H

#include "../util/varray.h"
#include "pm.h"
#include "../dis/dis/disx.h"

#ifdef dis_if_IMPORT
	#define EXTERN
#else
	#define EXTERN extern
#endif

/* fire types */
//#define dis_if_FIRE_OTHER      0  /* FIXME: not used */
#define dis_if_FIRE_M61A1      1
#define dis_if_FIRE_AIM9M      2
#define dis_if_FIRE_AIM120     3

/**
 * Unassigned entity ID.
 */
#define dis_if_ID_NONE	(0)

#define MARKINGS_LEN 11

typedef int (*dis_if_RequestControlCallback)(dis_pdu *, void *);

typedef struct {
	/** Current mode. */
	int       mode;
	/** Current radar target (ptbl index). */
	int       cur_target;
	/** Time of last update. */
	double    lastTime;
	/** Last emission PDU sent or received for this emitter. */
	dis_em_emission_pdu em;
	/** Systems passed to us. */
	/** Track/jam target info. */
	dis_track_info *target;
} dis_if_EntityEM;

typedef enum {
	/** Created, but non participating. */
	dis_if_ENTITY_STATE_WAIT,
	/** Actively simulating. */
	dis_if_ENTITY_STATE_SIMULATING,
	/** Stopped/Frozen. */
	dis_if_ENTITY_STATE_STOPPED,
	/** used in pending state. */
	dis_if_ENTITY_STATE_NONE
} dis_if_EntytyState;

/**
 * Local or remote DIS entity.
 */
typedef struct {
	/** If this entity is local or remote. */
	int isLocal;
	/** Entity state; per [1] 4.5.5.5.3. */
	dis_if_EntytyState state;
	dis_if_EntytyState pending_state;
	/** Time of state change. */
	double    pending_time;
	/** 1 to send entity state updates. */
	int		  emit_while_frozen;
	/** Pointer to craft structure  in ptbl. */
	craft    *c;
	/**
	 * For local entity, it is the last time a packet has been sent.
	 * For remote entity, it is the last time a packet has been receive.
	 * Currently both times are set using our timestamp (seconds since Unix
	 * epoch).
	 */
	double    lastTime;
	/** DISForce enum. */
	u_char    forceId;
	dis_entity_id entityId;
	dis_entity_type entityType;
	dis_entity_type altEntityType;
	/** Dead reckoning parameters. */
	dis_dr_parameters dr;
	/** EM emission PDU information, or NULL if not available. */
	dis_if_EntityEM *em;
	unsigned char markings[MARKINGS_LEN+1];
	unsigned int appearance;
	double    location[3];
	double    velocity[3];
	double    linearAcceleration[3];
	double    orientation[3];
	double    angularVelocity[3];
	double    lastLocation[3];
	double    lastVelocity[3];
	double    lastLinearAcc[3];
	double    lastOrientation[3];
	double    lastAngularVel[3];
	dis_if_RequestControlCallback controlRequestCallback;
	void      *callbackData;
} dis_if_Entity;

typedef enum {
	OUTSTANDING_REQUEST_TRANSFER_CONTROL = 0,
} dis_if_OutstandingRequest;

typedef struct dis_if_OutstandingRequestInfo {
	/** Type of pending request. */
	dis_if_OutstandingRequest request_type;
	/** DIS request identifier. */
	dis_request_id request_id;
	/** Entity involved in request. */
	dis_if_Entity *e;
	/** When will the request expire. */
	double timeout_time;
	struct dis_if_OutstandingRequestInfo *next;
	struct dis_if_OutstandingRequestInfo *prev;
} dis_if_OutstandingRequestInfo;

/**
 *  Callback invoked by this module when a new remote entity enter the simulation.
 *  The arguments are the id of the new entity and its type
 *  (one of dis_if_ENTITY_XXX).
 */
typedef void (*dis_if_EntityEnterCb) (int eid,
		dis_entity_type * etype, DISForce force, craft ** cptr);

/**
 *  Callback invoked by this module when a remote detonation occurs.
 *  The arguments are the type of fire detonating (one of dis_if_FIRE_XXX),
 *  the id of the firing entity, the id of the target entity,
 *  the time of the detonation and the location of the detonation in
 *  world coordinates and in target body coordinates.
 * @param ftype dis_if_FIRE_M61A1 or dis_if_FIRE_AIM9M.
 * @param firing Firing entity.
 * @param target Target craft that might so result damaged or destroyed.
 * @param time DIS PDU timestamp.
 * @param worldLocation Point of impact, that is where the missile was when
 *        it exploded.
 * @param entityLocation Ignored by ACM.
 * @param m The missile or munition that hit the target.
 * @param dpdu Detonation DIS PDU.
 */
typedef void (*dis_if_DetonationCb) (int ftype, 
		craft *firing, 
		craft *target,
		double time, 
		double worldLocation[3],
		double entityLocation[3], 
		craft * munition,
		dis_detonation_pdu *dpdu);

/**
 * Callback invoked by this module when the remote cannon firing burst DIS PDU
 * arrives. The m61a1 modules already provides a function for that.
 */
typedef void (*dis_if_CannonFireCb) (int owner, VPoint *pos, VPoint *vel, int rounds);

/** This host have true UTC time. */
EXTERN int dis_if_haveAbsoluteTime;

/**
 * Tells if networking is currently configured and ready to receive DIS packets.
 * If packets do really arrive is another story, but there are other monitoring
 * functions available to check that.
 * @return True if ready to receive DIS packets.
 */
EXTERN int dis_if_readyToReceive(void);

/**
 * When initialized with site ID -1, this function tells if currently still
 * trying to validate a randomly generated site ID for possible collisions with
 * already used site IDs on the same exercise. While validating, no packet is
 * sent and the user is not really visible from the other participants.
 * @return True if still validating a randomly generated site ID.
 */
EXTERN int dis_if_isValidatingSiteId(void);

/**
 * Initialize the DIS library. Network communication can be configured in one of
 * these modes:
 * 
 * 1. Stand-alone mode: no networking. Client must call dis_if_enableNetwork(0)
 * to enable this mode; the other parameters of this function are then ignored.
 * 
 * 2. Broadcast mode: all the capable interfaces are configured in
 * broadcasting mode and bind to the specified UDP port number for both sending
 * and receiving.
 * 
 * 3. Relay mode: packets are sent to the specified relay host and port number;
 * the local computer gets an ephemeral UDP port number from the operating
 * system.
 * 
 * Each participant MUST have a distinct application and site IDs, and MUST
 * share the same exercise ID. Received packets with different exercise ID are
 * simply discarded.
 * 
 * If the site ID is set to -1, a random value is chosen and a validation period
 * of 15 seconds starts. During the validation period packets are received and
 * decoded as usual, but not packet is sent; if a site ID collision is detected,
 * another random value is chosen and the validation period restarts.
 * Client program may poll the dis_if_isValidationSiteId() function to check
 * if this module is currently sending packets or not.
 * 
 * If the application ID is set to -1, the current process number is assigned.
 * 
 * @param relay_host Name or network address of the relay server. If NULL or
 * empty, uses packets broadcasting instead.
 * @param relay_port UDP port of the relay server if in relay mode, or UDP port
 * number shared for local network broadcasting ([0,65535]).
 * @param exercise The exercise ID of the simulation application in [0,255].
 * Simulation applications participating in the same simulation MUST have
 * the same exercise IDs. 
 * @param site Site number in [-1,65535].
 * @param application The DIS application id in [-1,65535].
 * @param entityEnterCb User callback for entity enter.
 * @param detonationCb User callback for detonation.
 * @param cannonFireCb Cannon fire callback. The m61a1 modules already provides
 * a suitable function that can be passed here.
 * @return Zero is returned on success, -1 on failure.
 */
EXTERN int dis_if_init(char *relay_host, int relay_port,
		int exercise, int site, int application,
		dis_if_EntityEnterCb entityEnterCb,
		dis_if_DetonationCb detonationCb,
		dis_if_CannonFireCb cannonFireCb);

/**
 *  Close down the DIS library.
 *  @return Zero is returned on success.
 */
EXTERN int dis_if_close(void);

/**
 *  Set the dead reckoning thresholds for location and orientation.
 *  The values shall be given in meters and radians (phi, theta, psi).
 */
EXTERN void dis_if_setDRThresholds(double location, double orientation);

/**
 *  Set the current time in the DIS library.
 */
EXTERN void dis_if_setTime(double time);

/**
 * Set the current time in the DIS library using the system time,
 * and mark the time as absolute, i.e. true UTC time.
 * This will improve the dead reckoning performance on networks
 * with significant delays between players _iff_ all hosts have
 * true UTC time (with millisecond precision!).
 *
 * If this is used when players do _not_ have synchronized clocks,
 * the result will be very strange positions for external players!!
 *
 * @return Zero is returned on success.
 */
EXTERN void dis_if_setTimeAbsolute(void);

/**
 * Process all available incoming PDU's from the network.
 * User callbacks will be called for entering entities,
 * exiting entities, firing entities and detonations.
 *
 * @return Zero is returned on success.
 */
EXTERN int dis_if_receive(void);

/**
 * Returns true if the entity ID is a valid handle, it is local and it is in
 * the simulating state.
 */
EXTERN int dis_if_canSimulate ( int eid );

/**
 * Enter a local entity.
 * The initial location, velocity, linear acceleration, orientation
 * and angular velocity will be set from the corresponding arguments.
 * Velocity and acceleration shall be given in world coordinates.
 * All parameter units are based on meters, radians and seconds.
 *
 * The world coordinate system used in DIS is GCC (geocentric Cartesian
 * coordinates), an earth-centered right-handed Cartesian system with
 * the positive X-axis passing through the Prime Meridian at the Equator,
 * with the positive Y-axis passing through 90 degrees East longitude
 * at the Equator and with the positive Z-axis passing through the
 * North Pole.
 *
 * The body coordinate system used in DIS is centered at the center of
 * the entity's bounding volume (excluding articulated parts) and have
 * the positive x-axis pointing to the front of the entity, the positive
 * y-axis pointing to the right side of the entity and the positive z-axis
 * pointing out of the bottom of the entity.
 *
 * @param force
 * @param c
 * @param e1 Primary entity type.
 * @param e2 Secondary entity type.
 * @param location Geocentric Cartesian coordinates (m).
 * @param velocity Velocity (m/s).
 * @param linearAcceleration
 * @param orientation Phi, theta and psi angles, in the order.
 * @param angularVelocity Angular velocity is given as [angular velocity around
 * body x-axis, ditto y, ditto z].
 * @param eid Here returns the handle to be used for further reference.
 */
EXTERN void dis_if_entityEnter(DISForce force, 
		craft * c,
		dis_entity_type * e1,
		dis_entity_type * e2,
		double location[3],
		double velocity[3],
		double linearAcceleration[3],
		double orientation[3],
		double angularVelocity[3],
		int *eid);

/**
 * Remove the entity with id eid from the simulation. If local entity, sends a
 * "destroyed" DIS PDU if it is a local entity.
 * @param eid Entity ID.
 */
EXTERN void dis_if_entityExit(int eid);

/**
 * Update the state information for a local entity.  The information will be
 * broadcasted on the network only if it is necessary to keep the other hosts
 * dead reckoning from exceeding the thresholds.
 * See dis_if_entityEnter for information about the arguments.
 * @param eid Entity ID.
 * @param location
 * @param velocity
 * @param linearAcceleration
 * @param orientation Phi, theta and psi angles, in the order.
 * @param angularVelocity
 * @return True if a DIS packet of type Entity State and/or a DIS packet of
 * type Emission has been sent.
 */
EXTERN int dis_if_setEntityState(int eid, double location[3], double velocity[3],
		double linearAcceleration[3],
		double orientation[3], double angularVelocity[3]);

/**
 * Return state information for a remote or local entity. For local entity, the
 * last registered state informations are returned. For remote entity, the state
 * information is dead reckoned from the last received data on the entity.
 * @param eid Entity handle.
 * @param location
 * @param velocity
 * @param orientation
 * @return True if entity available and the its state set, false if the entity
 * does not exist (anymore).
 */
EXTERN int dis_if_getEntityState(int eid, double location[3], double velocity[3],
		double orientation[3]);

/**
 * Returns the entity given its DIS entity ID.
 * @param id
 * @return Entity or NULL if not found.
 */
EXTERN dis_if_Entity *dis_if_findEntityByDISID(dis_entity_id * id);

/**
 * Returns the entity with given its index.
 * @param eid
 * @return Entity or NULL if not found.
 */
EXTERN dis_if_Entity *dis_if_findEntityByID(int eid);

/**
 * Broadcast information about an entity firing a weapon.
 * The type of fire is given by ftype as one of the dis_if_FIRE_XXX types.
 * The id's of the firing entity and the target entity are given with
 * firingEid and targetEid or as dis_if_ID_NONE if not known.
 * The number of rounds, location of the source of fire, the velocity
 * vector of the rounds and the range of the rounds are given with
 * the corresponding arguments.
 * The id of the event generated is returned in eventId.
 * If the fire type is a missile, a missile entity is created and its
 * id is returned in missileEid. The user program should generate
 * position data for the missile during its lifetime by calling
 * dis_if_setEntityState().
 *
 * FIXME: dis_if_fire() not used. Not yet implemented.
 *
 * @return Zero is returned on success.
 */
EXTERN int dis_if_fire(int ftype, int firingEid, int targetEid, int rounds,
		double location[3], 
		double velocity[3], 
		double range,
		int *eventId, int *missileEid);

/**
 * Broadcast information about a detonation of a local entity.
 * The type of fire is given by ftype as one of the dis_if_FIRE_XXX types.
 * The id's of the firing entity and the target entity are given with
 * firingEid and targetEid or as dis_if_ID_NONE if not known.
 * The id of the corresponding fire event is given as eventId or as
 * dis_if_ID_NONE if not known.
 * If the detonation is from a missile, the id of the missile is given
 * as missileEid  or as dis_if_ID_NONE. The library will exit the
 * missile entity.
 * The location of the detonation in world coordinates and in target
 * body coordinates are given as worldLocation and entityLocation.
 *
 * @param munition_type Munition DIS entity ID.
 * @param firingEid Firing craft (this module's ID).
 * @param targetEid Target craft (this module's ID).
 * @param munitionEid Munition (this module's ID).
 * @param worldLocation Location of the explosion.
 * @param entityLocation Location of the detonation in the reference system of
 * the target (m). Used for damage assessment of missiles and bombs.
 * @param vel Velocity of the munition (world coord., m/s). Damages due to
 * kinetic weapons (that is, cannon shells) depends on it.
 * @return
 * 0 = Network disabled or successfully sent PDU.
 * -1 = Firing entity is not local.
 * -2 = Target isn't available anymore in our table.
 * -3 = Firing entity not available (player died in the meanwhile?)
 */
EXTERN int dis_if_detonation(dis_entity_type * munition_type,
		int firingEid, 
		int targetEid,
		int munitionEid, 
		double worldLocation[3],
		double entityLocation[3],
		double vel[3]);

/**
 * Networking is enabled by default. Disabling networking no attempt will be
 * made to transmit or receive anything. All the rest will work as expected.
 * If networking disabled before initializing this module, the DIS protocol
 * is not initialized and an attempt to re-enable the network brings to abort.
 */
EXTERN void dis_if_enableNetwork(int enabled);

EXTERN void dis_if_setEntityMarkings(int eid, char *markings);

EXTERN void dis_if_getEntityMarkings(int eid, char *markings, int max);

EXTERN void dis_if_setEntityAppearance(int eid, dis_entity_appearance x);

EXTERN dis_entity_appearance dis_if_getEntityAppearance(int eid);

/**
 * Notify the world that our current radar target changed.
 */
EXTERN int dis_if_radarTargetChanged(craft * c);

/**
 * Set local entities current radar mode (modes are 0=off, 1=wide scan).
 */
EXTERN int dis_if_setRadarMode(craft * c, int mode, int update);

/**
 * Get the number of radar beams emitted from this aircraft.
 * The dis_if_getRadarParameters() will return each specific beam.
 */
EXTERN int dis_if_getBeamCount(craft * c);

/**
 * Get parameters describing the specified radar beam.
 * @param c Emitter.
 * @param j Beam number, in the range from 0 up to dis_if_getBeamCount(c)-1.
 * @param az_center Azimuth relative to the emitter (RAD).
 * @param az_width Beam half width (RAD).
 * @param el_center Elevation relative to the emitter (RAD).
 * @param el_width Beam half height (RAD).
 * @param erp Energy, dBm = 10.0 * log10 (e_watts / 0.001).
 */
EXTERN void dis_if_getRadarParameters(craft * c, 
		int j, 
		double *az_center, 
		double *az_width,
		double *el_center, 
		double *el_width, 
		double *erp);


/**
 * Set and estimate of the available network bandwidth
 * in bits per second.  This value is used to limit entity
 * state transmissions in an effort to keep UDP traffic
 * as close to real-time as possible.
 */
EXTERN void dis_if_setBandwidth(double bps);

/**
 * Process incoming PDUs for the specified number of milliseconds, thus
 * priming the entity table.  This is called only during program
 * initialization, when a complete entity table is required.
 */
EXTERN int dis_if_snoop ( int millisec );

EXTERN int dis_if_requestControl (dis_if_Entity *e, 
		dis_if_RequestControlCallback callbackFn, 
		void *arg);

typedef int (*dis_if_TransferControlRequestCallback)(dis_if_Entity *, dis_transfer_control_pdu *);

EXTERN void dis_if_setTransferControlRequestCallback ( dis_if_TransferControlRequestCallback  );


#define dis_if_RESULT_REQUEST_OK 0
#define dis_if_RESULT_UNABLE     1

/**
 * This routine is responsible for handling entity transfer control requests
 * at the simulation level.  It determines if the control transfer is
 * feasible, adjusts simulation level data structures as needed, and returns
 * an indication to the caller (the DIS interface) whether the request
 * should proceed, or not.
 */
EXTERN int dis_if_transferControlRequestHandler(dis_if_Entity *e, dis_transfer_control_pdu *pdu);

EXTERN int dis_if_fireCannon(craft * c, VPoint * pos, VPoint * vel, int quantity, int rate);

EXTERN disx_ApplicationInfo * dis_if_getApplicationInfo(void);


EXTERN varray_Type * dis_if_getEntityTable(void);

/**
 * Returns the number of local entities currently handled.
 * @return 
 */
EXTERN int dis_if_getNumberOfLocalEntities(void);

/**
 * Returns the number of remote entities currently handled.
 * @return 
 */
EXTERN int dis_if_getNumberOfRemoteEntities(void);

/**
 * Returns the current number of received DIS packets processed per second
 * (estimated).
 * @return 
 */
EXTERN double dis_if_getProcessedPacketsPerSecond(void);

/**
 * Updates the state of the remote craft according to the most updated DIS data
 * available.
 * @param c Remote craft to update.
 * @return NULL on success, or "not tracked anymore" if no data available.
 */
EXTERN char *dis_if_updateRemote(craft *c);

/**
 * Acquires the state of the local craft, possibly broadcasting a DIS state
 * packet.
 * @param c Local craft.
 */
EXTERN void dis_if_updateLocal(craft *c);

/**
 * Sends a broadcast text message all the users will see in their emulated
 * terminal (F1).
 * EXPERIMENTAL, does not work yet.
 */
EXTERN int dis_if_sendMessage(int senderEid, char *s);

#undef EXTERN
#endif
