#include <stdio.h>
#include "Error.h"
#include "server.h"

int numcontexts = 0;
Context *contexts[CONTEXTTABLESIZE];
#define hash_cont(conn, cont) ((conn)%CONTEXTTABLESIZE)


Trap InitializeContexts()
{
  int i;

  for (i = 0; i < CONTEXTTABLESIZE; i++)
    contexts[i] = NULL;

  return OK;
}

/*
 * Look up a context. Create one if it doesn't exist, and
 * create is set. On creation, allocates and zeroes a context
 * structure, enters it into a hash table, and initializes
 * the connection and contextNum fields.
 *
 * ERRORS:
 *
 * CONTEXT_NEW: INFO
 * A new context was created.
 *
 * CONTEXT_NONE: INFO
 * The context requested did not exist.
 */
Trap FindContext(context, connection, shcontextNum, create)
     Context **context;
     Addr connection;
     Card16 shcontextNum;
     int create;
{
  Context *current, *last;
  int contextNum;
  int h;
  Trap ret;

  contextNum = shcontextNum;

  ret = OK;
  h = hash_cont(connection, contextNum);

  last = NULL;
  current = contexts[h];

  while (current != NULL &&
	 (current->connection != connection ||
	  current->contextNum != contextNum))
    last = current, current = current->next;

  if (current == NULL)
    {
      if (create == NOALLOC)
	Return(CONTEXT_NONE, NULL);

      current = (Context *)malloc(sizeof(Context));
      if (current == NULL)
	Return(MEMORY_ALLOC, sizeof(Context));
      Error_Push(CONTEXT_NEW, NULL);
      ret = CHECK;
      bzero(current, sizeof(Context));
      current->connection = connection;
      current->contextNum = contextNum;
      if (last == NULL)
	contexts[h] = current;
      else
	last->next = current;
    }

  *context = current;
  return ret;
}

/*
 * Free a context.
 * Removes it from the hash table, and frees the associated memory.
 *
 * ERRORS:
 *
 * CONTEXT_BADCONTEXT: FATAL
 * Attempt to free a context not in the hash table.
 */
Trap FreeContext(context)
     Context *context;
{
  Context *current;
  int h;

  h = hash_cont(context->connection, context->contextNum);

  current = contexts[h];
  if (current == context)
    {
      contexts[h] = current->next;
      free(context);
      return OK;
    }
  else
    {
      while (current != NULL && current->next != context)
	current = current->next;

      if (current != NULL)
	{
	  current->next = context->next;
	  free(context);
	  return OK;
	}
    }

  Return(CONTEXT_BADCONTEXT, NULL);
}

/*
 * Remove a context from the stale list.
 */
RemoveStale(s, c)
     srvinfo *s;
     Context *c;
{
  if (s->nextTimeout == c)
    s->nextTimeout = c->lessStale;

  if (s->mostStale == c && s->leastStale == c)
    {
      s->mostStale = NULL;
      s->leastStale = NULL;
      return;
    }

  if (s->mostStale == c)
    {
      s->mostStale = c->lessStale;
      c->lessStale->moreStale = NULL;
      return;
    }

  if (s->leastStale == c)
    {
      s->leastStale = c->moreStale;
      c->moreStale->lessStale = NULL;
      return;
    }

  c->lessStale->moreStale = c->moreStale;
  c->moreStale->lessStale = c->lessStale;
  return;
}

/*
 * Add a context to the stale list - as the least stale item.
 */
AddStale(s, c, when, where)
     srvinfo *s;
     Context *c;
     time_t when;
     Addr where;
{
  c->activity = when;
  c->activitySource = where;

  if (s->nextTimeout == NULL)
    s->nextTimeout = c;

  if (s->mostStale == NULL)
    {
      s->mostStale = c;
      s->leastStale = c;
      c->moreStale = NULL;
      c->lessStale = NULL;
    }
  else
    {
      c->lessStale = NULL;
      c->moreStale = s->leastStale;
      c->moreStale->lessStale = c;
      s->leastStale = c;
    }
}

/*
 * Freezes timeouts of contexts whose last activity was prior to t.
 */
staleFreeze(s, t)
     srvinfo *s;
     time_t t;
{
  s->lastFreeze = time((time_t *)0);

  while (s->nextTimeout != NULL &&
	 s->nextTimeout->activity <= t)
    s->nextTimeout = s->nextTimeout->lessStale;
}

staleUnfreeze(s)
     srvinfo *s;
{
  s->lastFreeze = (time_t)0;
  s->nextTimeout = s->mostStale;
}

/*
 * Move a context to the head of the stale list.
 */
TouchContext(s, c, when, where)
     srvinfo *s;
     Context *c;
     time_t when;
     Addr where;
{
  RemoveStale(s, c);		/* take it out from whereever it is */
  AddStale(s, c, when, where);	/* put it back at the beginning */
}

/*
 * Add a context to a license queue.
 */
Enqueue(c)
     Context *c;
{
  License *l;

  l = c->what->license;
  c->nextQueue = NULL;

  if (l->queue == NULL)
    l->queue = c;
  else
    l->queueEnd->nextQueue = c;

  l->queueEnd = c;
}

/*
 * Remove a context from a license queue.
 */
Dequeue(c)
     Context *c;
{
  Context *s;

  s = c->what->license->queue;
  if (s == c)
    {
      c->what->license->queue = c->nextQueue;
      s = NULL;
    }
  else
    {
      while (s != NULL && s->nextQueue != c)
	s = s->nextQueue;

      if (s != NULL) /* && s->nextQueue == c - it does */
	s->nextQueue = c->nextQueue;
      else
	return; /* wasn't in the queue at all; shouldn't happen */
    }

  if (c->what->license->queueEnd == c)
    c->what->license->queueEnd = s; /* whether S is NULL or not :-) */
}
