#include <stdio.h>
#include "Error.h"
#include "Connect.h"
#include "protocol.h"
#include <netinet/in.h>

/* If protocol.h changes... */
char *protoTypes[14] = {
  "Error",
  "Orientation request",
  "Orientation response",
  "Access initialize",
  "Access change",
  "Access response",
  "Access upgrade",
  "Status query",
  "Status response",
  "Security",
  "State",
  "State reply",
  "Reduce",
  "Reduce reply"
  };

Trap expand();

/*
 * ERRORS:
 *
 * PROTO_PACKETTOOSHORT: FATAL
 * The protocol claimed the packet was longer than what was received.
 *
 * PROTO_ENDPACKET: FATAL
 * The packet received was too short to contain the requested item.
 *
 * PROTO_MISMATCH: FATAL
 * The protocol of the packet did not match.
 */
#ifdef 0
Trap P0_GetHeader(packet)
     P0Packet *packet;
{
  Card16 *ptr;

  ptr = (Card16 *)packet->packet->packet; /* heh */

  if (packet->packet->length < 8)
    Return(PROTO_ENDPACKET, NULL);

  packet->version = ntohs(*ptr);	ptr++;
  packet->type = ntohs(*ptr);		ptr++;
  packet->context = ntohs(*ptr);	ptr++;
  packet->length = ntohs(*ptr);		ptr++;

  if (packet->length > packet->packet->length)
    Return(PROTO_PACKETTOOSHORT, NULL);

  if (packet->version != 0)
    Return(PROTO_MISMATCH, NULL);

  packet->current = (char *)ptr;
  packet->end = packet->length + packet->packet->packet;
  return OK;
}

Trap P0_PutHeader(packet)
     P0Packet *packet;
{
  Card16 *ptr;

  packet->version = 0;
  packet->length = 8;
  ptr = (Card16 *)packet->packet->packet;

  *ptr = htons(packet->version);	ptr++;
  *ptr = htons(packet->type);		ptr++;
  *ptr = htons(packet->context);	ptr++;
/**ptr = htons(packet->length);	*/	ptr++;

  packet->end = (char *)ptr;
  return OK;
}

Trap P0_PutLength(packet)
     P0Packet *packet;
{
  packet->packet->length = packet->end - packet->packet->packet;
  *(short *)(&(packet->packet->packet[6])) =
    htons((short)(packet->packet->length));

  return OK;
}
#endif /* 0 */

Trap P0_GetString(block, string)
     P0Block *block;
     char **string;
{
  char *s;

  s = block->current;
  while (s - block->data < block->dataSize && *s != '\0') s++;

  if (s - block->data == block->dataSize)
    Return(PROTO_ENDPACKET, NULL);

  *string = block->current;
  block->current = s + 1;
  return OK;
}

Trap P0_PutString(block, string)
     P0Block *block;
     char *string;
{
  char *s;
  int offset;

  if (string == NULL)
    Return(BOZO_NULLPOINTER, NULL);

  s = block->current;

  if (s - block->data + strlen(string) + 1 > block->dataSize)
    {
      offset = s - block->data;
      if (expand(block))
	return CHECK;
      s = block->data + offset;
    }

  while (*string != '\0')
    *s++ = *string++;

  *s++ = '\0';
  block->current = s;

  return OK;
}

Trap P0_GetCard16(block, card)
     P0Block *block;
     Card16 *card;
{
  char *s;

  s = block->current;
  if ((int)(s) & 1) s++;		/* auto padding */

  if (s - block->data + 2 > block->dataSize)
    Return(PROTO_ENDPACKET, NULL);

  *card = ntohs(*(Card16 *)s);
  block->current = s + 2;
  return OK;
}

Trap P0_PutCard16(P0Block *block, Card16 card)
/*
Trap P0_PutCard16(block, card)
     P0Block *block;
     Card16 card;
*/
{
  char *s;
  int offset;

  s = block->current;
  if ((int)(s) & 1) s++;		/* auto padding */

  if (s - block->data + 2 > block->dataSize)
    {
      offset = s - block->data;
      if (expand(block))
	return CHECK;
      s = block->data + offset;
    }

  *(Card16 *)s = htons(card);
  block->current = s + 2;
  return OK;
}

Trap P0_GetCard32(block, card)
     P0Block *block;
     Card32 *card;
{
  char *l;

  l = block->current;
  if ((int)(l) & 3) l += 4 - ((int)(l) & 3);	/* auto padding */

  if (l - block->data + 4 > block->dataSize)
    Return(PROTO_ENDPACKET, NULL);

  *card = ntohl(*(Card32 *)l);
  block->current = l + 4;
  return OK;
}

Trap P0_PutCard32(block, card)
     P0Block *block;
     Card32 card;
{
  Trap retval;
  char *l;
  int offset;

  l = block->current;
  if ((int)(l) & 3) l += 4 - ((int)(l) & 3);	/* auto padding */

  if (l - block->data + 4 > block->dataSize)
    {
      offset = l - block->data;
      if (expand(block))
	return CHECK;
      l = block->data + offset;
    }

  *(Card32 *)l = htonl(card);
  block->current = l + 4;
  return OK;
}

Trap P0_PutBlock(block, blck)
     P0Block *block;
     P0Block *blck;
{
  Trap retval;
  char *b;
  int offset, size;

  if (P0_PutString(block, blck->type))
    return CHECK;

  b = block->current;
  if ((int)(b) & 3) b += 4 - ((int)(b) & 3);	/* auto padding */

  size = blck->current - blck->data;

  if (b - block->data + size > block->dataSize) /*XXX*/
    {
      offset = b - block->data;
      if (expand(block)) /* XXX make it take a minimum expansion parameter */
	return CHECK;
      b = block->data + offset;
    }

  bcopy(blck->data, b, size);
  block->current = b + size;
  return OK;
}

Trap expand(block)
     P0Block *block;
{
  if (block->malloced == 0)
    Return(MEMORY_ALLOC, 0);	/* XXX inaccurate */

  block->data = (char *)realloc(block->data, block->dataSize + block->initialSize);
  if (block->data == 0)
    Return(MEMORY_ALLOC, block->dataSize + block->initialSize);	/* XXX also */

  block->dataSize += block->initialSize;
  return OK;
}

Trap P0_FreeBlock(block)
     P0Block *block;
{
  if (block->malloced)
    free(block->data);

  free(block->type);
  free(block);
  return OK;
}

Trap P0_InitBlock(type, block, data, sizeHint)
     char *type;
     P0Block *block;
     char *data;
     int sizeHint;
{
  block->type = (char *)malloc(strlen(type) + 1);
  if (block->type == NULL)
    Return(MEMORY_ALLOC, strlen(type) + 1);

  strcpy(block->type, type);

  if (data != NULL)
    {
      block->data = data;
      block->dataSize = sizeHint;
      block->malloced = 0;
    }
  else
    {
      if (sizeHint == 0)
	sizeHint = 512;
      block->data = (char *)malloc(sizeHint);
      block->dataSize = sizeHint;
      block->malloced = 1;
    }

  block->current = block->data;
  block->initialSize = block->dataSize;

  if (block->data == NULL)
    {
      free(block->type);
      Return(MEMORY_ALLOC, sizeHint);
    }

  return OK;
}

Trap P0_CreateBlock(type, block, data, sizeHint)
     char *type;
     P0Block **block;
     char *data;
     int sizeHint;
{
  P0Block *b;

  b = (P0Block *)malloc(sizeof(P0Block));
  if (b == NULL)
    Return(MEMORY_ALLOC, sizeof(P0Block));

  if (P0_InitBlock(type, b, data, sizeHint) &&
      Error_Severity == S_FATAL)
    {
      free(b);
      return CHECK;
    }

  *block = b;
}

Trap P0_InitMessage(P0Message *message, Card16 type, Card16 context)
{
  message->type = type;
  message->context = context;
  message->numBlocks = 0;
  message->firstBlock = NULL;
  message->lastBlock = NULL;
  return OK;
}

Trap P0_CreateMessage(P0Message **message, Card16 type, Card16 context)
/*
Trap P0_CreateMessage(message, type, context)
     P0Message **message;
     Card16 type;
     Card16 context;
*/
{
  P0Message *m;

  m = (P0Message *)malloc(sizeof(P0Message));
  if (m == NULL)
    Return(MEMORY_ALLOC, sizeof(P0Message));

  P0_InitMessage(m, type, context);

  *message = m;
  return OK;
}

Trap P0_AddBlockToMessage(message, block)
     P0Message *message;
     P0Block *block;
{
  if (message == NULL || block == NULL)
    Return(BOZO_NULLPOINTER, NULL);

  block->message = message;
  message->numBlocks++;

  if (message->firstBlock == NULL)
    {
      message->firstBlock = block;
      message->lastBlock = block;
      return OK;
    }

  message->lastBlock->nextBlock = block;
  message->lastBlock = block;
  return OK;
}

/*
 * Convenience routine for CreateBlock + AddBlockToMessage
 * Ack. Maybe this is just stupid, or maybe it's a good
 * place to put block caching. But maybe malloc isn't so
 * bad.
 */
Trap P0_NewBlock(type, message, block)
     char *type;
     P0Message *message;
     P0Block **block;
{
  if (P0_CreateBlock(type, block, NULL, 0))
    return CHECK;
  return P0_AddBlockToMessage(message, *block);
}

/*
 * Convert a message (a sequence of blocks) into a single block
 * with packet header and table of contents. This generates
 * a block suitable for use as a packet.
 * The block to be written into should already be allocated.
 * This decision is made by the expected use of this routine -
 * I want to pass some static area to be used to buffer outgoing
 * packets, not mallocing all the time.
 */
Trap P0_CvtMessageToBlock(message, block)
     P0Message *message;
     P0Block *block;
{
  int i;
  P0Block header, *b;

  if (message == NULL || block == NULL)
    Return(BOZO_NULLPOINTER, NULL);

  P0_ResetBlock(block);

  header.data = block->data;
  header.current = header.data;
  /*
   * Header:
   *             Card16 version
   *             Card16 type
   *             Card16 context
   *             Card16 numBlocks
   * numBlocks * Card16 offset
   *             Card16 length
   */
  header.dataSize = (message->numBlocks + 5) * sizeof(Card16);
  block->current += header.dataSize;

  P0_PutCard16(&header, 0); /* XXX all of these could return errors. sigh. */
  P0_PutCard16(&header, message->type);
  P0_PutCard16(&header, message->context);
  P0_PutCard16(&header, message->numBlocks);

  for (i = 0, b = message->firstBlock;
       i < message->numBlocks;
       i++, b = b->nextBlock)
    {
      P0_PutCard16(&header, (Card16)(block->current - block->data));
      P0_PutString(block, b->type);
      P0_PutBlock(block, b);
    }

  P0_PutCard16(&header, (Card16)(block->current - block->data)); /* length */
}

/*
 * Break a block up into its consituent blocks (a message).
 */
Trap P0_CvtBlockToMessage(block, message)
     P0Block *block;
     P0Message **message;
{
}

Trap P0_FreeMessage(message)
     P0Message *message;
{
}
