/* $Id: pkgcache.c,v 1.8 2004/05/25 22:22:30 stpohle Exp $
 * Resendcache work, We need this to resend lost packets over the network.
 * we will keep every packet with the PKGF_ackreq flag as long as we haven't
 * got any answer from the destination host. And resend the packet after a givin
 * time. The maximum time for a resend is 10 times if we haven't got any reply
 * by then we delete the packet from the cache. By doing this the game will not 
 * anymore sync. with all clients.
 */
#include "bomberclone.h"
#include "network.h"
#include "packets.h"

/*
	set pointers at the giving pos
*/
void
rscache_setpointer (int pos)
{
    resend_cache.entry = (struct _rscache_entry *) (((char *) resend_cache.data) + pos);
};


/*
   add a packet to the cache and sets the timestamp
*/
int
rscache_add (_net_addr * addr, struct pkg *packet)
{
    int newlen;

    if (resend_cache.fill + sizeof (struct _rscache_entry) > PKG_RESENDCACHE_SIZE)
        return -1;

    rscache_setpointer (resend_cache.fill);
    resend_cache.entry->retry = 0;
    resend_cache.entry->timestamp = timestamp;
    memcpy (&resend_cache.entry->addr, addr, sizeof (_net_addr));
    memcpy (&resend_cache.entry->packet, packet, NTOH16 (packet->h.len));

    newlen = resend_cache.fill + rscache_getcurlen ();

    resend_cache.fill = newlen;

    return 0;
};

/*
	deletes the packet at current pointer in the cache
*/
void
rscache_del ()
{
    int len,
      size;
    char *pos1;

    /* check if we are able to delete */
    if ((char *) resend_cache.data > (char *) resend_cache.entry ||
        (char *) resend_cache.entry > (char *) resend_cache.data + resend_cache.fill ||
        resend_cache.fill == -1 || resend_cache.data == NULL) {
        d_printf ("rscache_del: something wrong with the cache\n");
        exit (1);
        return;
    }

    /* newlen = size of the cacheentry we are going to delete */
    len = rscache_getcurlen ();

    if (resend_cache.fill <= len) /* something wrong?, fill is smaler as the cacheentry, delete the cache */
        resend_cache.fill = 0;
    else {
        pos1 = (char *) resend_cache.entry + len;
        size = PKG_RESENDCACHE_SIZE - (pos1 - resend_cache.data);

        if (resend_cache.fill < PKG_RESENDCACHE_SIZE) {
            memmove (resend_cache.entry, pos1, size);
            resend_cache.fill = resend_cache.fill - len;
        }
    }
};

/*
   get the position in the cache where our needed packet is
*/
int
rscache_getpos (_net_addr * addr, unsigned char typ, short int id)
{
    int len,
      pos,
      done = 0;

    for (pos = 0; (pos < resend_cache.fill && done == 0);) {

        rscache_setpointer (pos);

        /* get size of the cacheentry */
        len = rscache_getcurlen ();

        if (strcmp (addr->host, resend_cache.entry->addr.host) == 0 &&
            strcmp (addr->port, resend_cache.entry->addr.port) == 0 &&
            typ == resend_cache.entry->packet.h.typ && id == NTOH16 (resend_cache.entry->packet.h.id))
            done = 1;           /* we have found the old packet */
        else
            pos = pos + len;
    }

    if (done == 0)              /* we have found nothing */
        return -1;

    return pos;
};

/* get the len of the current entry */
int
rscache_getcurlen ()
{
    int len;

    len = sizeof (struct _rscache_entry) - sizeof (struct pkg) + NTOH16 (resend_cache.entry->packet.h.len);

    return len;
};


/*
  the loop for the cache.. to see what will have to be resend
*/
void
rscache_loop ()
{
    int len,
      pos,
      timeout;
	if (bman.state==GS_running) timeout = RESENDCACHE_TIMEOUT; else timeout=RESENDCACHE_TIMEOUT_MENU;
    for (pos = 0; pos < resend_cache.fill && pos < PKG_RESENDCACHE_SIZE && pos >= 0;) {

        rscache_setpointer (pos);
        len = rscache_getcurlen ();

        if (timestamp - resend_cache.entry->timestamp >= timeout
            && resend_cache.entry->retry < RESENDCACHE_RETRY) {
            /* send it again */
            d_printf
                ("Data Send Timeout (%s:%s) Resend now Package Fill %d, Pos %d\n",
                 resend_cache.entry->addr.host, resend_cache.entry->addr.port, resend_cache.fill,pos);
            udp_send (bman.sock, (char *) &resend_cache.entry->packet,
                      NTOH16 (resend_cache.entry->packet.h.len), &resend_cache.entry->addr.sAddr,
                      bman.net_ai_family);
            resend_cache.entry->timestamp = timestamp;
            resend_cache.entry->retry++;
            if (resend_cache.entry->addr.pl_nr >= 0 && resend_cache.entry->addr.pl_nr < MAX_PLAYERS)
				players[resend_cache.entry->addr.pl_nr].net.pkgopt.to_2sec++;
        }

        if (timestamp - resend_cache.entry->timestamp >= timeout
            && resend_cache.entry->retry >= RESENDCACHE_RETRY) {
            d_printf ("Data Send Timeout (%s:%s) Delete now Package Fill %d, Pos %d\n",
                      resend_cache.entry->addr.host, resend_cache.entry->addr.port,
                      resend_cache.fill, pos);
            if (resend_cache.entry->addr.pl_nr >= 0 && resend_cache.entry->addr.pl_nr < MAX_PLAYERS)
				players[resend_cache.entry->addr.pl_nr].net.pkgopt.to_2sec++;
			
            rscache_del ();
        }
        else
            pos = pos + len;
    }
};
