/*
 * qstat 2.14
 * by Steve Jankowski
 *
 * qserver functions and definitions
 * Copyright 2004 Ludwig Nussel
 *
 * Licensed under the Artistic License, see LICENSE.txt for license terms
 */
#ifndef QSTAT_QSERVER_H
#define QSTAT_QSERVER_H

struct query_param  {
    char *key;
    char *value;
    int i_value;
    unsigned int ui_value;
    struct query_param *next;
};

typedef struct SavedData  {
    char *data;
    int datalen;
    int pkt_index;
    int pkt_max;
    unsigned int pkt_id;
    struct SavedData *next;
} SavedData;

typedef enum {
    STATE_INIT			=  0,
    STATE_CONNECTING	=  1,
    STATE_CONNECTED		=  2,
    STATE_QUERYING		=  3,
    STATE_QUERIED		=  4,

    STATE_TIMEOUT		= -1,
    STATE_SYS_ERROR		= -2,
} server_state_t;

struct qserver {
    char *arg;
    char *host_name;
    unsigned int ipaddr;
    int flags;
    server_type * type;
    int fd;
	server_state_t state;
    char *outfilename;
    char *query_arg;
    char *challenge_string;
    struct query_param *params;
    unsigned long challenge;
    unsigned short port;
    unsigned short orig_port; // This port is always constant from creation where as port can be updated based on the query results
    unsigned short query_port;

	// State variable to flag if the current processing call is a call from combine_packets
	// This should really by done via a flag to the method itself but that would require changes
	// to all handlers :(
	unsigned short combined;

    /** \brief number of retries _left_ for status query or rule query.
     *
     * That means
     * if s->retry1 == (global)n_retries then no retries were necessary so far.
     * if s->retry1 == 0 then the server has to be cleaned up after timeout */
    int retry1;
    /** \brief number retries _left_ for player query. @see retry1 */
    int retry2;
    /** \brief how much retry packets were sent */
    int n_retries;
    /** \brief time when the last packet to the server was sent */
    struct timeval packet_time1;
    struct timeval packet_time2;
    /** \brief sum of packet deltas
     *
     * average server ping is ping_total / n_requests
     */
    int ping_total;

    /** \brief number of requests send to a server.
     *
     * used for ping calculation
     * @see ping_total
     */
    int n_requests;

    /** \brief number of packets already sent to this server
     *
     * doesn't seemt to have any use
     */
    int n_packets;

    /** \brief number of servers in master_pkt
     *
     * normally master_pkt_len/6
     */
    int n_servers;
    /** \brief length of master_pkt */
    int master_pkt_len;
    /** \brief IPs received from master.
     *
     * array of four bytes ip, two bytes port in network byte order
     */
    char *master_pkt;
    /** \brief state info
     *
     * used for progressive master 4 bytes for WON 22 for Steam
     */
    char master_query_tag[22];
    char *error;

    /** \brief in-game name of the server.
     *
     * A server that has a NULL name did not receive any packets yet and is
     * considered down after a timeout.
     */
    char *server_name;
    char *address;
    char *map_name;
    char *game;
    int max_players;
    int num_players;
    int protocol_version;
	int max_spectators;
	int num_spectators;

    SavedData saved_data;

    /** \brief number of the next player to retrieve info for.
     *
     * Only meaningful for servers that have type->player_packet.
     * This is used by q1 as it sends packets for each player individually.
     * cleanup_qserver() cleans up a server if next_rule == NULL and
     * next_player_info >= num_players
     */
    int next_player_info;
    /** \brief number of player info packets received */
    int n_player_info;
    struct player *players;

    /** \brief name of next rule to retreive
     *
     * Used by Q1 as it needs to send a packet for each rule. Other games would
     * set this to an empty string to indicate that rules need to be retrieved.
     * After rule packet is received set this to NULL.
     */
    char *next_rule;
    int n_rules;
    struct rule *rules;
    struct rule **last_rule;
    int missing_rules;

    struct qserver *next;
    struct qserver *prev;
};

void qserver_disconnect(struct qserver* server);

/* server specific query parameters */

void add_query_param( struct qserver *server, char *arg);

/** \brief get a parameter for the server as string
 *
 * @param server the server to get the value from
 * @param key which key to get
 * @param default_value value to return if key was not found
 * @return value for key or default_value
 */
char * get_param_value( struct qserver *server, const char *key, char *default_value);

/** @see get_param_value */
int get_param_i_value( struct qserver *server, char *key, int default_value);

/** @see get_param_value */
unsigned int get_param_ui_value( struct qserver *server, char *key,
		unsigned int default_value);

/** \brief send an initial query packet to a server
 *
 * Sends a unicast packet to the server's file descriptor. The descriptor must
 * be connected. Updates n_requests or n_retries, retry1, n_packets, packet_time1
 *
 * \param server the server
 * \param data data to send
 * \param len length of data
 * \returns number of bytes sent or SOCKET_ERROR
 */
query_status_t qserver_send_initial(struct qserver* server, const char* data, size_t len);

/** \brief send an initial query packet to a server
 *
 * Sends a unicast packet to the server's file descriptor. The descriptor must
 * be connected. Updates n_requests, n_packets, packet_time1. Sets retry1 to
 * (global) n_retries
 *
 * @see qserver_send_initial
 */
query_status_t qserver_send(struct qserver* server, const char* data, size_t len);

int send_broadcast( struct qserver *server, const char *pkt, size_t pktlen);

/**
 * Registers the send of a request packet.
 *
 * This updates n_requests, n_packets, packet_time1 and decrements n_retries
 */
int register_send( struct qserver *server );

/**
 * Sends a packet to the server either direct or via broadcast.
 *
 * Once sent calls register_send to register the send of the packet
 */
query_status_t send_packet( struct qserver* server, const char* data, size_t len );

/**
 * Logs the error from a socket send
 */
query_status_t send_error( struct qserver *server, int rc );

#endif
