#include "tcpflow.h"

static flow_state_t *flow_hash[HASH_SIZE];
extern stats_t stats;
extern int tcp_timeout;

void init_flow_state()
{
int i;

   for( i = 0; i < HASH_SIZE; i++ ) 
      flow_hash[i] = NULL;
}

/* Create a new flow state structure, initialize its contents, and add
 * it to its hash bucket.  It is prepended to the hash bucket because
 * 1) doing so is fast (requiring constant time regardless of bucket
 * size; and 2) it'll tend to make lookups faster for more recently
 * added state, which will probably be more often used state.
 *
 * Returns a pointer to the new state. */
flow_state_t *create_flow_state(flow_t flow)
{
time_t current_time;
flow_state_t *state;

  time( &current_time );

  /* determine where in the hash this goes */
  int index = HASH_FLOW(flow);

  /* Can I reuse the first bucket (tcp timeout)? */
  if( flow_hash[index] )
     if( flow_hash[index]->time_last_access-current_time > tcp_timeout ) {

        state = flow_hash[index]->next;
        bzero( state, sizeof(flow_state_t) );
        flow_hash[index]->next = state;

        /* statistics */
        stats.reuse++;

        return;
     }

  /* statistics */
  stats.mallocs++;

  /* create space for the new state */
  flow_state_t *new_flow = MALLOC(flow_state_t, 1);
  bzero( new_flow, sizeof(flow_state_t) );

  /* link it in to the hash bucket at the beginning */
  if( flow_hash[index] )
     flow_hash[index]->prev = new_flow;

  new_flow->prev = NULL;
  new_flow->next = flow_hash[index];
  flow_hash[index] = new_flow;

  DEBUG(5) ("%s: new flow", flow_name(flow));

return new_flow;
}

/* Find previously a previously created flow state structure by
 * jumping to its hash bucket, and linearly searching everything in
 * the bucket.  Returns NULL if the state is not found, and type
*/
flow_state_t *find_flow_state(flow_t flow_tx, flow_t flow_rx, int *flow_type)
{
flow_state_t *ptr;
int index = HASH_FLOW(flow_tx);

  *flow_type = 0;
  for (ptr = flow_hash[index]; ptr != NULL; ptr = ptr->next) {

    /* It's a tx flow? */
    if (!memcmp((char *) &flow_tx, (char *) &(ptr->flow), sizeof(flow_tx))) {
      *flow_type = FLOW_TX;
      DEBUG(8) ("%s: flow TX", flow_name(flow_tx));
      return ptr;
    }

    /* It's a rx flow? */
    if (!memcmp((char *) &flow_rx, (char *) &(ptr->flow), sizeof(flow_rx))) {
      *flow_type = FLOW_RX;
      DEBUG(8) ("%s: flow RX", flow_name(flow_rx));
      return ptr;
    }

  }

return NULL;
}

/* Delete a flow state structure */
void distroy_flow_state(flow_state_t *state)
{
  DEBUG(8) ("Clearing flow %s", flow_name(state->flow));

  if( state->prev ) {
     (state->prev)->next = state->next;
  }
  else {
     flow_hash[HASH_FLOW(state->flow)] = NULL;
  }

  free(state);

  /* Statistics */
  stats.cleans++;
}
