/* -----------------------------------------------------------------------
 * GGPO.net (http://ggpo.net)  -  Copyright 2009 GroundStorm Studios, LLC.
 *
 * Use of this software is governed by the MIT license that can be found
 * in the LICENSE file.
 */

#ifndef _GGPONET_H_
#define _GGPONET_H_

#ifdef __cplusplus
extern "C" {
#endif

#include <stdarg.h>

// On windows, export at build time and import at runtime.
// ELF systems don't need an explicit export/import.
#ifdef _WIN32
#  if defined(GGPO_SHARED_LIB)
#  ifdef GGPO_SDK_EXPORT
#       define GGPO_API __declspec(dllexport)
#     else
#       define GGPO_API __declspec(dllimport)
#     endif
#  else
#     define GGPO_API
#  endif
#else
#  define GGPO_API
#endif

#ifdef __GNUC__
#if defined(_WIN32) && !defined(_WIN64)
#define __cdecl __attribute__((cdecl))
#elif !defined(__cdecl)
#define __cdecl
#endif
#endif

#define GGPO_MAX_PLAYERS                  4
#define GGPO_MAX_PREDICTION_FRAMES        8
#define GGPO_MAX_SPECTATORS              32

#define GGPO_SPECTATOR_INPUT_INTERVAL     4

typedef struct GGPOSession GGPOSession;

typedef int GGPOPlayerHandle;

typedef enum {
   GGPO_PLAYERTYPE_LOCAL,
   GGPO_PLAYERTYPE_REMOTE,
   GGPO_PLAYERTYPE_SPECTATOR,
} GGPOPlayerType;

/*
 * The GGPOPlayer structure used to describe players in ggpo_add_player
 *
 * size: Should be set to the sizeof(GGPOPlayer)
 *
 * type: One of the GGPOPlayerType values describing how inputs should be handled
 *       Local players must have their inputs updated every frame via
 *       ggpo_add_local_inputs.  Remote players values will come over the
 *       network.
 *
 * player_num: The player number.  Should be between 1 and the number of players
 *       In the game (e.g. in a 2 player game, either 1 or 2).
 *
 * If type == GGPO_PLAYERTYPE_REMOTE:
 * 
 * u.remote.ip_address:  The ip address of the ggpo session which will host this
 *       player.
 *
 * u.remote.port: The port where udp packets should be sent to reach this player.
 *       All the local inputs for this session will be sent to this player at
 *       ip_address:port.
 *
 */

typedef struct GGPOPlayer {
   int               size;
   GGPOPlayerType    type;
   int               player_num;
   union {
      struct {
         bool _unused;
      } local;
      struct {
         char           ip_address[32];
         unsigned short port;
      } remote;
   } u;
} GGPOPlayer;

typedef struct GGPOLocalEndpoint {
   int      player_num;
} GGPOLocalEndpoint;


#define GGPO_ERRORLIST                                               \
   GGPO_ERRORLIST_ENTRY(GGPO_OK,                               0)    \
   GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_SUCCESS,                0)    \
   GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_GENERAL_FAILURE,        -1)   \
   GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_INVALID_SESSION,        1)    \
   GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_INVALID_PLAYER_HANDLE,  2)    \
   GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_PLAYER_OUT_OF_RANGE,    3)    \
   GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_PREDICTION_THRESHOLD,   4)    \
   GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_UNSUPPORTED,            5)    \
   GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_NOT_SYNCHRONIZED,       6)    \
   GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_IN_ROLLBACK,            7)    \
   GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_INPUT_DROPPED,          8)    \
   GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_PLAYER_DISCONNECTED,    9)    \
   GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_TOO_MANY_SPECTATORS,   10)    \
   GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_INVALID_REQUEST,       11)    \
   GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_INPUT_SIZE_DIFF,       12)    \
   GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_NETWORK_ERROR,         13)    \
   GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_VERIFICATION_ERROR,    14)

#define GGPO_ERRORLIST_ENTRY(name, value)       name = value,
typedef enum {
   GGPO_ERRORLIST
} GGPOErrorCode;
#undef GGPO_ERRORLIST_ENTRY

#define GGPO_SUCCEEDED(result)      ((result) == GGPO_ERRORCODE_SUCCESS)


#define GGPO_INVALID_HANDLE      (-1)


/*
 * The GGPOEventCode enumeration describes what type of event just happened.
 *
 * GGPO_EVENTCODE_CONNECTED_TO_PEER - Handshake with the game running on the
 * other side of the network has been completed.
 * 
 * GGPO_EVENTCODE_SYNCHRONIZING_WITH_PEER - Beginning the synchronization
 * process with the client on the other end of the networking.  The count
 * and total fields in the u.synchronizing struct of the GGPOEvent
 * object indicate progress.
 *
 * GGPO_EVENTCODE_SYNCHRONIZED_WITH_PEER - The synchronziation with this
 * peer has finished.
 *
 * GGPO_EVENTCODE_RUNNING - All the clients have synchronized.  You may begin
 * sending inputs with ggpo_synchronize_inputs.
 *
 * GGPO_EVENTCODE_DISCONNECTED_FROM_PEER - The network connection on 
 * the other end of the network has closed.
 *
 * GGPO_EVENTCODE_TIMESYNC - The time synchronziation code has determined
 * that this client is too far ahead of the other one and should slow
 * down to ensure fairness.  The u.timesync.frames_ahead parameter in
 * the GGPOEvent object indicates how many frames the client is.
 *
 */
typedef enum {
   GGPO_EVENTCODE_CONNECTED_TO_PEER            = 1000,
   GGPO_EVENTCODE_SYNCHRONIZING_WITH_PEER      = 1001,
   GGPO_EVENTCODE_SYNCHRONIZED_WITH_PEER       = 1002,
   GGPO_EVENTCODE_RUNNING                      = 1003,
   GGPO_EVENTCODE_DISCONNECTED_FROM_PEER       = 1004,
   GGPO_EVENTCODE_TIMESYNC                     = 1005,
   GGPO_EVENTCODE_CONNECTION_INTERRUPTED       = 1006,
   GGPO_EVENTCODE_CONNECTION_RESUMED           = 1007,
} GGPOEventCode;

/*
 * The GGPOEvent structure contains an asynchronous event notification sent
 * by the on_event callback.  See GGPOEventCode, above, for a detailed
 * explanation of each event.
 */
typedef struct {
   GGPOEventCode code;
   union {
      struct {
         GGPOPlayerHandle  player;
      } connected;
      struct {
         GGPOPlayerHandle  player;
         int               count;
         int               total;
      } synchronizing;
      struct {
         GGPOPlayerHandle  player;
      } synchronized;
      struct {
         GGPOPlayerHandle  player;
      } disconnected;
      struct {
         int               frames_ahead;
      } timesync;
      struct {
         GGPOPlayerHandle  player;
         int               disconnect_timeout;
      } connection_interrupted;
      struct {
         GGPOPlayerHandle  player;
      } connection_resumed;
   } u;
} GGPOEvent;

/*
 * The GGPOSessionCallbacks structure contains the callback functions that
 * your application must implement.  GGPO.net will periodically call these
 * functions during the game.  All callback functions must be implemented.
 */
typedef struct {
   /*
    * begin_game callback - This callback has been deprecated.  You must
    * implement it, but should ignore the 'game' parameter.
    */
   bool (__cdecl *begin_game)(const char *game);

   /*
    * save_game_state - The client should allocate a buffer, copy the
    * entire contents of the current game state into it, and copy the
    * length into the *len parameter.  Optionally, the client can compute
    * a checksum of the data and store it in the *checksum argument.
    */
   bool (__cdecl *save_game_state)(unsigned char **buffer, int *len, int *checksum, int frame);

   /*
    * load_game_state - GGPO.net will call this function at the beginning
    * of a rollback.  The buffer and len parameters contain a previously
    * saved state returned from the save_game_state function.  The client
    * should make the current game state match the state contained in the
    * buffer.
    */
   bool (__cdecl *load_game_state)(unsigned char *buffer, int len);

   /*
    * log_game_state - Used in diagnostic testing.  The client should use
    * the ggpo_log function to write the contents of the specified save
    * state in a human readible form.
    */
   bool (__cdecl *log_game_state)(char *filename, unsigned char *buffer, int len);

   /*
    * free_buffer - Frees a game state allocated in save_game_state.  You
    * should deallocate the memory contained in the buffer.
    */
   void (__cdecl *free_buffer)(void *buffer);

   /*
    * advance_frame - Called during a rollback.  You should advance your game
    * state by exactly one frame.  Before each frame, call ggpo_synchronize_input
    * to retrieve the inputs you should use for that frame.  After each frame,
    * you should call ggpo_advance_frame to notify GGPO.net that you're
    * finished.
    *
    * The flags parameter is reserved.  It can safely be ignored at this time.
    */
   bool (__cdecl *advance_frame)(int flags);

   /* 
    * on_event - Notification that something has happened.  See the GGPOEventCode
    * structure above for more information.
    */
   bool (__cdecl *on_event)(GGPOEvent *info);

   /*
    * on_message - Delivers an application message. Allows arbitrary data to be
    * exchanged with peers. This callback is optional.
    */
   void (__cdecl *on_message)(unsigned char *msg, int len);
} GGPOSessionCallbacks;

/*
 * The GGPONetworkStats function contains some statistics about the current
 * session.
 *
 * network.send_queue_len - The length of the queue containing UDP packets
 * which have not yet been acknowledged by the end client.  The length of
 * the send queue is a rough indication of the quality of the connection.
 * The longer the send queue, the higher the round-trip time between the
 * clients.  The send queue will also be longer than usual during high
 * packet loss situations.
 *
 * network.recv_queue_len - The number of inputs currently buffered by the
 * GGPO.net network layer which have yet to be validated.  The length of
 * the prediction queue is roughly equal to the current frame number
 * minus the frame number of the last packet in the remote queue.
 *
 * network.ping - The roundtrip packet transmission time as calcuated
 * by GGPO.net.  This will be roughly equal to the actual round trip
 * packet transmission time + 2 the interval at which you call ggpo_idle
 * or ggpo_advance_frame.
 *
 * network.kbps_sent - The estimated bandwidth used between the two
 * clients, in kilobits per second.
 *
 * timesync.local_frames_behind - The number of frames GGPO.net calculates
 * that the local client is behind the remote client at this instant in
 * time.  For example, if at this instant the current game client is running
 * frame 1002 and the remote game client is running frame 1009, this value
 * will mostly likely roughly equal 7.
 *
 * timesync.remote_frames_behind - The same as local_frames_behind, but
 * calculated from the perspective of the remote player.
 *
 */
typedef struct GGPONetworkStats {
   struct {
      int   send_queue_len;
      int   recv_queue_len;
      int   ping;
      int   kbps_sent;
   } network;
   struct {
      int   local_frames_behind;
      int   remote_frames_behind;
   } timesync;
   struct {
	   int predicted_frames;
   } sync;
} GGPONetworkStats;

/*
 * ggpo_start_session --
 *
 * Used to being a new GGPO.net session.  The ggpo object returned by ggpo_start_session
 * uniquely identifies the state for this session and should be passed to all other
 * functions.
 *
 * session - An out parameter to the new ggpo session object.
 *
 * cb - A GGPOSessionCallbacks structure which contains the callbacks you implement
 * to help GGPO.net synchronize the two games.  You must implement all functions in
 * cb, even if they do nothing but 'return true';
 *
 * game - The name of the game.  This is used internally for GGPO for logging purposes only.
 *
 * num_players - The number of players which will be in this game.  The number of players
 * per session is fixed.  If you need to change the number of players or any player
 * disconnects, you must start a new session.
 *
 * input_size - The size of the game inputs which will be passsed to ggpo_add_local_input.
 *
 * local_port - The port GGPO should bind to for UDP traffic.
 *
 * verification - Some optional data that will be matched with the peers during initial sync.
 * Can be set to null if verification_size is zero. This can be used to check that all peers are
 * running the same version of the application and of the game, are using the same settings, etc.
 *
 * verification_size - Size of the verification data. Can be set to zero to not exchange verification data.
 * Maximum size is 256 bytes.
 */
GGPO_API GGPOErrorCode __cdecl ggpo_start_session(GGPOSession **session,
                                                  GGPOSessionCallbacks *cb,
                                                  const char *game,
                                                  int num_players,
                                                  int input_size,
                                                  unsigned short localport,
												  const void *verification,
												  int verification_size);


/*
 * ggpo_add_player --
 *
 * Must be called for each player in the session (e.g. in a 3 player session, must
 * be called 3 times).
 *
 * player - A GGPOPlayer struct used to describe the player.
 *
 * handle - An out parameter to a handle used to identify this player in the future.
 * (e.g. in the on_event callbacks).
 */
GGPO_API GGPOErrorCode __cdecl ggpo_add_player(GGPOSession *session,
                                               GGPOPlayer *player,
                                               GGPOPlayerHandle *handle);


/*
 * ggpo_start_synctest --
 *
 * Used to being a new GGPO.net sync test session.  During a sync test, every
 * frame of execution is run twice: once in prediction mode and once again to
 * verify the result of the prediction.  If the checksums of your save states
 * do not match, the test is aborted.
 *
 * cb - A GGPOSessionCallbacks structure which contains the callbacks you implement
 * to help GGPO.net synchronize the two games.  You must implement all functions in
 * cb, even if they do nothing but 'return true';
 *
 * game - The name of the game.  This is used internally for GGPO for logging purposes only.
 *
 * num_players - The number of players which will be in this game.  The number of players
 * per session is fixed.  If you need to change the number of players or any player
 * disconnects, you must start a new session.
 *
 * input_size - The size of the game inputs which will be passsed to ggpo_add_local_input.
 *
 * frames - The number of frames to run before verifying the prediction.  The
 * recommended value is 1.
 *
 */
GGPO_API GGPOErrorCode __cdecl ggpo_start_synctest(GGPOSession **session,
                                                   GGPOSessionCallbacks *cb,
                                                   const char *game,
                                                   int num_players,
                                                   int input_size,
                                                   int frames);


/*
 * ggpo_start_spectating --
 *
 * Start a spectator session.
 *
 * cb - A GGPOSessionCallbacks structure which contains the callbacks you implement
 * to help GGPO.net synchronize the two games.  You must implement all functions in
 * cb, even if they do nothing but 'return true';
 *
 * game - The name of the game.  This is used internally for GGPO for logging purposes only.
 *
 * num_players - The number of players which will be in this game.  The number of players
 * per session is fixed.  If you need to change the number of players or any player
 * disconnects, you must start a new session.
 *
 * input_size - The size of the game inputs which will be passsed to ggpo_add_local_input.
 *
 * local_port - The port GGPO should bind to for UDP traffic.
 *
 * host_ip - The IP address of the host who will serve you the inputs for the game.  Any
 * player partcipating in the session can serve as a host.
 *
 * host_port - The port of the session on the host
 *
 * verification - Some optional data that will be matched with the peers during initial sync.
 * Can be set to null if verification_size is zero. This can be used to check that all peers are
 * running the same version of the application and of the game, are using the same settings, etc.
 *
 * verification_size - Size of the verification data. Can be set to zero to not exchange verification data.
 * Maximum size is 256 bytes.
 */
GGPO_API GGPOErrorCode __cdecl ggpo_start_spectating(GGPOSession **session,
                                                     GGPOSessionCallbacks *cb,
                                                     const char *game,
                                                     int num_players,
                                                     int input_size,
                                                     unsigned short local_port,
                                                     char *host_ip,
                                                     unsigned short host_port,
													 const void *verification,
													 int verification_size);

/*
 * ggpo_close_session --
 * Used to close a session.  You must call ggpo_close_session to
 * free the resources allocated in ggpo_start_session.
 */
GGPO_API GGPOErrorCode __cdecl ggpo_close_session(GGPOSession *);


/*
 * ggpo_set_frame_delay --
 *
 * Change the amount of frames ggpo will delay local input.  Must be called
 * before the first call to ggpo_synchronize_input.
 */
GGPO_API GGPOErrorCode __cdecl ggpo_set_frame_delay(GGPOSession *,
                                                    GGPOPlayerHandle player,
                                                    int frame_delay);

/*
 * ggpo_idle --
 * Should be called periodically by your application to give GGPO.net
 * a chance to do some work.  Most packet transmissions and rollbacks occur
 * in ggpo_idle.
 *
 * timeout - The amount of time GGPO.net is allowed to spend in this function,
 * in milliseconds.
 */
GGPO_API GGPOErrorCode __cdecl ggpo_idle(GGPOSession *,
                                         int timeout);

/*
 * ggpo_add_local_input --
 *
 * Used to notify GGPO.net of inputs that should be trasmitted to remote
 * players.  ggpo_add_local_input must be called once every frame for
 * all player of type GGPO_PLAYERTYPE_LOCAL.
 *
 * player - The player handle returned for this player when you called
 * ggpo_add_local_player.
 *
 * values - The controller inputs for this player.
 *
 * size - The size of the controller inputs.  This must be exactly equal to the
 * size passed into ggpo_start_session.
 */
GGPO_API GGPOErrorCode __cdecl ggpo_add_local_input(GGPOSession *,
                                                    GGPOPlayerHandle player,
                                                    void *values,
                                                    int size);

/*
 * ggpo_synchronize_input --
 *
 * You should call ggpo_synchronize_input before every frame of execution,
 * including those frames which happen during rollback.
 *
 * values - When the function returns, the values parameter will contain
 * inputs for this frame for all players.  The values array must be at
 * least (size * players) large.
 *
 * size - The size of the values array.
 *
 * disconnect_flags - Indicated whether the input in slot (1 << flag) is
 * valid.  If a player has disconnected, the input in the values array for
 * that player will be zeroed and the i-th flag will be set.  For example,
 * if only player 3 has disconnected, disconnect flags will be 8 (i.e. 1 << 3).
 */
GGPO_API GGPOErrorCode __cdecl ggpo_synchronize_input(GGPOSession *,
                                                      void *values,
                                                      int size,
                                                      int *disconnect_flags);

/*
 * ggpo_disconnect_player --
 *
 * Disconnects a remote player from a game.  Will return GGPO_ERRORCODE_PLAYER_DISCONNECTED
 * if you try to disconnect a player who has already been disconnected.
 */
GGPO_API GGPOErrorCode __cdecl ggpo_disconnect_player(GGPOSession *,
                                                      GGPOPlayerHandle player);

/*
 * ggpo_advance_frame --
 *
 * You should call ggpo_advance_frame to notify GGPO.net that you have
 * advanced your gamestate by a single frame.  You should call this everytime
 * you advance the gamestate by a frame, even during rollbacks.  GGPO.net
 * may call your save_state callback before this function returns.
 */
GGPO_API GGPOErrorCode __cdecl ggpo_advance_frame(GGPOSession *);

/*
 * ggpo_get_network_stats --
 *
 * Used to fetch some statistics about the quality of the network connection.
 *
 * player - The player handle returned from the ggpo_add_player function you used
 * to add the remote player.
 *
 * stats - Out parameter to the network statistics.
 */
GGPO_API GGPOErrorCode __cdecl ggpo_get_network_stats(GGPOSession *,
                                                      GGPOPlayerHandle player,
                                                      GGPONetworkStats *stats);

/*
 * ggpo_set_disconnect_timeout --
 *
 * Sets the disconnect timeout.  The session will automatically disconnect
 * from a remote peer if it has not received a packet in the timeout window.
 * You will be notified of the disconnect via a GGPO_EVENTCODE_DISCONNECTED_FROM_PEER
 * event.
 *
 * Setting a timeout value of 0 will disable automatic disconnects.
 *
 * timeout - The time in milliseconds to wait before disconnecting a peer.
 */
GGPO_API GGPOErrorCode __cdecl ggpo_set_disconnect_timeout(GGPOSession *,
                                                           int timeout);

/*
 * ggpo_set_disconnect_notify_start --
 *
 * The time to wait before the first GGPO_EVENTCODE_NETWORK_INTERRUPTED timeout
 * will be sent.
 *
 * timeout - The amount of time which needs to elapse without receiving a packet
 *           before the GGPO_EVENTCODE_NETWORK_INTERRUPTED event is sent.
 */
GGPO_API GGPOErrorCode __cdecl ggpo_set_disconnect_notify_start(GGPOSession *,
                                                                int timeout);

/*
 * ggpo_send_message --
 *
 * Sends application-specific data to other peers. The message is
 * delivered by the on_user_message callback.
 * If spectators is true, the message is also sent to spectators.
 */
GGPO_API GGPOErrorCode __cdecl ggpo_send_message(GGPOSession *,
                                                 const void *msg,
                                                 int len,
												 bool spectators);

#ifdef __cplusplus
};
#endif

#endif
