/*
> The one problem I see is that the list is getting sorted via the IP address &
> port. Are we going this route or sorting by player number? I strongly suggest
> sorting by player number. Which makes updating servers a little more CPU 
> intensive but the majority of the work is going to be user requests.

The internal index is for updating.  The list will have to be resorted on every
change to player counts anyway.

*/

#include "svr.h"

static struct server *servers[MAXSERVERS];
static int count;


/*
**  Internal comparison function.  Compare one server entry with another and
**  return zero if they are for the same server and port number, or either 1
**  or -1 according to how they are different.  Used in calls to qsort()
**  and bsearch().
*/
static int compar0(struct server *one, struct server *two)
{
    int value;
    
    if ( one == NULL && two != NULL ) return 1;
    if ( one != NULL && two == NULL ) return -1;
    if ( one == NULL && two == NULL ) return 0;
    
    value = memcmp ( one->source, two->source );
    if ( value != 0 ) return value;
    
    value = strcmp ( one->server.port, two->server.port );
    return value;
}
/*
**  Internal comparison function.  Compare one server entry with another and
**  for the purposes of ordering the server list for output to the clients.
**  Used in calls to qsort() and bsearch().
*/
static int compar1(struct server *one, struct server *two)
{
    int value;
    
    if ( one == NULL && two != NULL ) return 1;
    if ( one != NULL && two == NULL ) return -1;
    if ( one == NULL && two == NULL ) return 0;
    
    value = strcmp ( one->server.free, two->server.free );
    if ( value != 0 ) return value;
    
    value = strcmp ( one->server.address, two->server.address );
    return value;
}

int svr_initialise()
{
    int i;
    for(i=0;i<MAXSERVERS;i++) servers[i] = NULL;
    count = 0;
}

struct server *svr_find(struct server *server)
{
    return (struct server *) bsearch(server, servers, count, 
        sizeof(struct server *), compar0 );
}

/*
** svr_update
** 
** Update the server list given a new decoded server packet received from
** the network. Expire any servers that have not sent recent updates.
** 
** Note: the server structure and the packet buffer to which it points must
** have been allocated by the caller using malloc(). This module calls free()
** on behalf of the caller.
*/
void svr_update(struct server *server)
{
    struct server *old; /* pointer to current server    */
    int i;              /* entry number in array        */
    int expired = 0;    /* count of expired entries     */
    int inserted = 0;   /* whether to inserted index    */
    time_t cutoff;      /* cutoff date time for expiry  */
    
    /* find old entry that matches the entry being updated */
    old = svr_find(server);
    
    /* if server is known, update it, otherwise insert it */
    if(old!=NULL)
    {
        /* if this update is too recent, mark the server as bogus */
        server->bogus = 
            ( server->received - old->received < man_get_minupdsecs() );

        /* free the old entry's packet buffer */
        free(old->buffer);
           
        /* overwrite the new entry */
        memcpy(old,server,sizeof(struct server));
    }
    else
    {
        /* ignore insert if array full */
        if(count<MAXSERVERS)
        {
            /* append new server to end of array */
            servers[count++] = server;
            
            /* flag resort is required */
            inserted++;
        }
        else
        {
            /* send a gripe to the log */
            log_send ( "Error: new server could not be inserted, table full." );
        }
    }
    
    /* calculate cutoff date from current time and expiry value */
    cutoff = time(NULL) - man_get_expiry();
    
    /* check each entry for expiry */
    for(i=0;i<count;i++)
    {
        /* is this server entry older than the cutoff? */
        old = servers[i];
        if(old->received < cutoff)
        {
            /* yes; clear the array entry */
            servers[i] = NULL;
            
            /* free the allocated storage */
            free ( old->buffer );
            free ( old );
    
            /* count the expired entries */
            expired++;
        }
    }
    
    /* if entries were expired or inserted, resort the array */
    if(expired!=0 || inserted!=0)
    {
        /* brutally resort the primary array */
        qsort(servers, count, sizeof(struct server *), compar0);
        count = count - expired;
    }
}

int svr_traverse
(
    int ((*compar)(const struct server *, const struct server *)),
    int ((*action)(const struct server *))
)
{
    int i;
    
    /* allocate a private copy of the server list */
    auto struct server *ours[MAXSERVERS];
    auto int tally;
    
    /* copy the real one into our copy */
    tally = count;
    memcpy(ours, servers, sizeof(struct server *)* tally);
    
    /* sort the copy using the caller's comparison routine */
    qsort(ours, tally, sizeof(struct server *), compar );
    
    /* call the caller's action routine for every packet */
    for(i=0;i<tally;i++)
        if(ours[i]!=NULL) (*action)(ours[i]);
}

int svr_save()
{
    int i;                              /* index into server array      */
    char *name = man_get_database();    /* name of disk file for data   */
    FILE *file;
    struct server *server;
    
    /* create the disk file */
    file = fopen(name, "w");
    if(file==NULL) return 0;
    
    /* process each entry in the array */
    for(i=0;i<count;i++)
    {
        server = servers[i];
        fprintf(file, "~~~\n%08.8x\n%d\n%s\n", server->source, server->received,
            server->buffer);
    }
    
    /* close file and return success */
    fclose(file);
    return 1;
}

int svr_load()
{
    char *name = man_get_database();
    FILE *file;
    struct server *server;
    char buffer[MAXBUFFER];
    char *line;
    
    file = fopen(name, "r");
    if(file==NULL) return 0;
    
    /* first line must be our special marker, or we give up */
    line = fgets(buffer,MAXBUFFER,file);
    if(line==NULL) { fclose(file); return 0; }
    if(strcmp(line,"~~~\n")) { fclose(file); return 0; }
        
    while(!feof(file))
    {
        server = malloc(sizeof(server));
        sscanf(file,"%08.8x\n",&server->source);
        sscanf(file,"%d\n",&server->received);
        
        line = fgets(buffer,MAXBUFFER,file);
        if (line==NULL) break;
        
        while(!strcmp(line,"~~~\n"))
        {
            line = fgets(line,MAXBUFFER-strlen(buffer),file);
            if (line==NULL) break;
        }
        
        server->length = strlen(buffer);
        server->buffer = malloc(server->length+1);
        memcpy(server->buffer,buffer,server->length+1);
        svr_update(server);
    }
    
    fclose(file);
    return 1;
}
