#ifndef NW_H_
#define NW_H_

#include "vgi.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <stdarg.h>

#define MBCAST_PORT  1211
#define SERVER_PORT  1212

#define NW_MAXPAKET  1024  /* max. size of an UDP-packet */
#define MAX_CLIENTS  8     /* number of bits must not exceed 1 byte */
#define MAX_ZDATA    255   /* must be greater than additional-data-header and less than 256 */
#define MAX_KEYS     40    /* 5 bytes for keys-data */
#define MOUSE_BYTES  3     /* 3 bytes for mouse-data */
#define MOUSE_DIVVAL 3     /* divisor for mouse-position */
#define MAX_KEYSBYT  8     /* MOUSE_BYTES + MAX_KEYS/8 */
#define MAX_XDATA    (NW_MAXPAKET - 20)  /* use nearly the full UDP-packet */

#define MAX_KEYSPACKETS  (NW_MAXPAKET / (2 + 1 + 1 + MAX_KEYSBYT * MAX_CLIENTS + 1) + 1)

#define NW_ICONN2CLNR(P1)  (P1 + 1)
#define NW_CLNR2ICONN(P1)  (P1 - 1)

#define USHORT_SET(P1, P2, P3)  (P1) = (((unsigned short)(unsigned char)(P2) << 8) | (unsigned short)(unsigned char)(P3))
#define USHORT_GET(P1, P2, P3)  do { \
                                  (P2) = (((P1) >> 8) & 0xff); \
                                  (P3) = ((P1) & 0xff); \
                                } while (0)

#define U3BYTE_SET(P1, P2, P3, P4)  (P1) = (((unsigned int)(unsigned char)(P2) << 16) \
                                           | ((unsigned int)(unsigned char)(P3) << 8) \
                                           | (unsigned int)(unsigned char)(P4))
#define U3BYTE_GET(P1, P2, P3, P4)  do { \
                                      (P2) = (((P1) >> 16) & 0xff); \
                                      (P3) = (((P1) >> 8) & 0xff); \
                                      (P4) = ((P1) & 0xff); \
                                    } while (0)

/* client writes sha1-log-file for each processed nw-package */
#undef NWPACK_CKSUM

/* dump to stderr */
#undef NWPACK_DUMP
#ifdef NWPACK_DUMP
# define outlog(priority, ...)  outerr(__VA_ARGS__)
#else
# define outlog(priority, ...)  sv_outlog(priority, __VA_ARGS__)
#endif


/* +++ struct for data packets +++ */

enum {
  NWDATA_KENNER_GETCONN = 1,  /* get connections */
  NWDATA_KENNER_RUNCONN,      /* run connections */
};

enum {
  NWDATA_ACTION_NEWCONN = 1,  /* kenner = NWDATA_KENNER_GETCONN:
                                  - (from client): request for new connection
                                  - (from server): got new connection
                              */
  NWDATA_ACTION_ENDCONN,      /* kenner = NWDATA_KENNER_GETCONN:
                                  - (from client): server shall end getting connections
                                  - (from server): end getting connections
                              */
  NWDATA_ACTION_CLOSE,        /* kenner = NWDATA_KENNER_RUNCONN:
                                  - (from client): client disconnects
                                  - (from server): client shall disconnect
                              */
  NWDATA_ACTION_PING,         /* kenner = NWDATA_KENNER_RUNCONN:
                                  - (from client): ping to server
                              */
  NWDATA_ACTION_PAUSE,        /* kenner = NWDATA_KENNER_RUNCONN:
                                  - (from client): wants to pause game
                                  - (from server): pause game
                              */
  NWDATA_ACTION_CONTINUE,     /* kenner = NWDATA_KENNER_RUNCONN:
                                  - (from client): wants to continue game
                                  - (from server): continue game
                              */
  NWDATA_ACTION_KEYS,         /* kenner = NWDATA_KENNER_RUNCONN:
                                  - (from client): send keys of client
                                  - (from server): send keys of all clients
                              */
  NWDATA_ACTION_CONNECTED,    /* kenner = NWDATA_KENNER_RUNCONN:
                                  - (from client): wants to receive connected-client-info
                                  - (from server): send connected-client-info
                              */
  NWDATA_ACTION_XDATA_SEND,   /* kenner = NWDATA_KENNER_RUNCONN:
                                  - (from client): send exchange-data
                                  - (from server): send exchange-data
                              */
  NWDATA_ACTION_XDATA_RECV,   /* kenner = NWDATA_KENNER_RUNCONN:
                                  - (from client): wants server to send exchange-data
                                  - (from server): acknowledgement for client's sent exchange-data
                              */
};

struct nwdata_run_keys_s2c {  /* keys-packet for server to client */
  unsigned short seqnr;         /* sequence-number client expects */
  unsigned char conncl;         /* bitwise which clients are connected */
  unsigned char keyset;         /* bitwise which clients has keys set */
  unsigned char keysdata[MAX_KEYSBYT * MAX_CLIENTS];  /* all client's keys */
  int zdatalen;                 /* number of bytes in additional-data */
  char zdata[MAX_ZDATA];        /* additional-data */
};

struct nwdata_packets {  /* all packets for server and client */
  int kenner;  /* one of NWDATA_KENNER_* */
  int action;  /* one of NWDATA_ACTION_* */

  union {      /* kenner-union */
    union {      /* action-union for kenner = NWDATA_KENNER_GETCONN */
      union {      /* union for NWDATA_ACTION_NEWCONN */
        struct {     /* server to client */
          int hnamelen;           /* bytes of hostnames */
          char hname[MAX_ZDATA];  /* hostnames (each newline-terminated) */
        } s2c;
        struct {     /* client to server */
          int maxcl;       /* max. number of clients, or 0 = don't matter */
          char hname[MAX_ZDATA / MAX_CLIENTS - 1];  /* client's hostname (nullterminated) */
        } c2s;
      } a_newconn;

      union {      /* union for NWDATA_ACTION_ENDCONN */
        struct {     /* server to client */
          int clnr;               /* client-number */
          char nw_seed[6];        /* network seed start-number in hex */
          int hnamelen;           /* bytes of hostnames */
          char hname[MAX_ZDATA];  /* hostnames (each newline-terminated) */
        } s2c;
      } a_endconn;
    } k_getconn;

    union {      /* action-union for kenner = NWDATA_KENNER_RUNCONN */
      union {      /* union for NWDATA_ACTION_KEYS */
        struct {     /* server to client */
          int anz;     /* number of elements */
          struct nwdata_run_keys_s2c data[MAX_KEYSPACKETS];  /* keys-packet-array */
        } s2c;
        struct {     /* client to server */
          int clnr;                /* client-number (> 0) */
          unsigned short seqnr;    /* next expected sequence-number */
          unsigned char keysdata[MAX_KEYSBYT];  /* client's keys */
          int zdatalen;            /* number of bytes in additional-data */
          char zdata[MAX_ZDATA];   /* additional-data */
        } c2s;
      } a_keys;

      union {      /* union for NWDATA_ACTION_CLOSE, NWDATA_ACTION_PAUSE, NWDATA_ACTION_CONTINUE */
        struct {     /* client to server */
          int clnr;    /* client-number (> 0) */
        } c2s;
      } a_close, a_ping, a_pause, a_continue;

      union {      /* union for NWDATA_ACTION_CONNECTED */
        struct {     /* server to client */
          unsigned char conncl;  /* bitwise which clients are connected */
        } s2c;
        struct {     /* client to server */
          int clnr;    /* client-number (> 0) */
        } c2s;
      } a_connected;

      union {      /* union for NWDATA_ACTION_XDATA_RECV */
        struct {     /* server to client */
          unsigned short seqnr;  /* sequence-number client expects */
        } s2c;
        struct {     /* client to server */
          int clnr;              /* client-number (> 0) */
          unsigned short seqnr;  /* sequence-number */
        } c2s;
      } a_xdata_recv;

      union {      /* union for NWDATA_ACTION_XDATA_SEND */
        struct {     /* server to client */
          unsigned short seqnr;   /* sequence-number client expects */
          int xdatalen;           /* number of bytes in exchange-data */
          char xdata[MAX_XDATA];  /* exchange-data */
        } s2c;
        struct {     /* client to server */
          int clnr;               /* client-number (> 0) */
          unsigned short seqnr;   /* sequence-number */
          int xdatalen;           /* number of bytes in exchange-data */
          char xdata[MAX_XDATA];  /* exchange-data */
        } c2s;
      } a_xdata_send;
    } k_runconn;
  } u;
};

struct nwdata_zdata {  /* additional-data list */
  struct nwdata_zdata *next;  /* next element */
  char *data;                 /* data */
  size_t size;                /* number of bytes in data */
  size_t pos;                 /* actual position in data */
  int clnr;                   /* client-number */
};

struct nwdata_xdata {  /* exchange-data list */
  struct nwdata_xdata *next;  /* next element */
  char *data;                 /* data */
  size_t size;                /* number of bytes in data */
  size_t pos;                 /* unused or actual position in data */
  int tag;                    /* tag of data */
  int clnr;                   /* client-number */
  unsigned char conncl;       /* bitwise which clients are connected */
};


/* +++ client-struct +++ */

struct vgi_nw {  /* network struct for client */
  int svport, mbport;                /* UDP-ports for server and broadcast/multicast, or 0 = default */
  int ipv;                           /* 4 or 6 */
  int sockfd;                        /* non-blocking UDP-socket-descriptor */
  socklen_t salen;                   /* length for the client-address */
  struct sockaddr *sad_send;         /* for client-address */
  struct sockaddr *sad_recv;         /* for receiving from nw-server */
  int clnr;                          /* client-number */
  unsigned short seqnr;              /* next expected sequence-number */
  unsigned short seqnr_xdata;        /* next expected sequence-number for exchange-data */
  unsigned char xdata_tag;           /* tag for actual type of exchange-data */
  unsigned char conncl;              /* bitwise which clients are connected */
  unsigned char dgram[NW_MAXPAKET];  /* network-packet */
  VG_BOOL is_paused;                 /* whether is paused */
  int anzcl;                         /* number of clients */
  struct {                           /* clients (client-number is index+1) */
    char name[MAX_ZDATA / MAX_CLIENTS - 1];  /* client's name */
    struct {                         /* pressed network-keys */
      int mouse_x, mouse_y;            /* mouse position */
      char mouse_left;                 /* mouse left (0 = no, 1 = yes, 2 = yes and queried) */
      char mouse_middle;               /* mouse middle (0 = no, 1 = yes, 2 = yes and queried) */
      char mouse_right;                /* mouse right (0 = no, 1 = yes, 2 = yes and queried) */
      char key_pressed[MAX_KEYS];      /* network-key key-entries */
    } keys;
  } *clients;
  struct {                           /* received keys-data */
    int anz;                           /* received elements of data */
    int pos;                           /* next element of data to process */
    struct nwdata_run_keys_s2c data[MAX_KEYSPACKETS];  /* data */
  } nwkeys;
  struct {                           /* additional-data */
    struct nwdata_zdata *slist;        /* additional-data list to be sent */
    char data_chunk[MAX_ZDATA];        /* data-chunk to be sent actually */
    int chunk_len;                     /* number of bytes in data-chunk */
    struct nwdata_zdata *rdata;        /* actual receiving additional-data */
  } zdata;
  struct {                           /* pause struct */
    int pause_ping;                    /* counter for pinging nw-server */
    int keyref_cont[3];                /* keys for continue game */
    struct VG_ImagecopyAttrPixel wattr_pixel;  /* window attributes */
    struct VG_Image *wclone;           /* clone of window-contents */
    struct VG_ImagecopyAttr iattr;     /* window-attributes for clone */
    VG_BOOL au_susp;                   /* audio-suspend */
    struct VG_Sprite *sprt_zzz;        /* pause-sleeping sprite */
    struct VG_Image *imgp_cont;        /* continue-text */
    struct VG_Position cont_posi;      /* position for continue-text */
    int cont_ilauf;                    /* for blinking continue-text */
  } pause;
};


extern void init_nw_server(void);
extern void init_nw_xdatalist(void);

extern void sv_outlog(int, const char *, ...);
extern VG_BOOL nw_sv_runlocal(void);

extern VG_BOOL nw_addr_equal(int, struct sockaddr *, struct sockaddr *, socklen_t);
extern void nw_conncl_set(unsigned char *, int, int);
extern VG_BOOL nw_conncl_get(unsigned char *, int);
extern void nw_free_nwdata_zdata(struct nwdata_zdata *);
extern void nw_free_nwdata_xdata(struct nwdata_xdata *);
extern int nw_zheader_set(const struct nwdata_zdata *, char *);
extern int nw_zheader_get(struct nwdata_zdata *, const char *);
extern int nw_xheader_set(const struct nwdata_xdata *, char *);
extern int nw_xheader_get(struct nwdata_xdata *, const char *);

extern void cl_dgram2nwpack(const char *, const unsigned char *, size_t, struct nwdata_packets *);
extern size_t cl_nwpack2dgram(const struct nwdata_packets *, unsigned char *);
extern void sv_dgram2nwpack(const char *, const unsigned char *, size_t, struct nwdata_packets *);
extern size_t sv_nwpack2dgram(const char *, const struct nwdata_packets *, unsigned char *, int);

#endif /* NW_H_ */
