/*
    NetQueue.c, Copyright  (c) by Boris Lutz,
    Swiss Federal Institute of Technology,
    Computer Engineering and Networks Laboratory.
 
    TOPSY -- A Teachable Operating System.
             Implementation of a tiny and simple
             micro kernel for teaching purposes.
 
    For further information, please visit http://www.tik.ee.ethz.ch/~topsy
 
    This software is provided under the terms of the GNU General Public 
    Licence. A full copy of the GNU GPL is provided in the file COPYING 
    found in the development root of Topsy.
*/
/*
	
    File:                  $Source: /home/ruf/cvs_Topsy/cvs/Topsy/Net/NetQueue.c,v $
    Affiliation:           ETH Zuerich, TIK
    Version:               $Revision: 2922 $
    Last Date of Change:   $Date: 2007-03-19 17:48:52 +0100 (Mon, 19 Mär 2007) $  by: $Author: baderma $
 
*/


#include "Support.h"
#include "Topsy.h"
#include "Syscall.h"
#include "NetSyscall.h"
#include "NetQueue.h"
#include "NetMemoryManager.h"

NetQueue netQueueNew (int options)
{
  NetQueue	q;
  Error		err;


  if (options != TYPE_DGRAM && options != TYPE_STREAM)
  {
    printf("NetQueue: invalid options\n");
    return NULL;
  }

  err = netMMAlloc ((Address*) &q, sizeof(NetQueueDesc) );
  if (err != NMM_OK)
  {
    printf("NetQueue failed to allocate memory\n");
    return NULL;
  }
  q->options = options;
  q->first = NULL;
  q->last = NULL;

  return q;
}


void netQueueFree (NetQueue my)
{
  NetQueueElem	element, t;

  if (my == NULL)
    return;
  for ( element = my->first; element; )
  {
    t = element;
    element = element->next;
    netMMFree(t);
  }
  netMMFree(my);
  return;
}

Error netQueueAdd (NetQueue my, NetBuf data, NetBuf ext)
{
  NetQueueElem	element;
  Error			err;


  switch (my->options)
  {
  case TYPE_DGRAM:
    err = netMMAlloc ((Address*) &element, sizeof(NetQueueElemDesc) );
    if (err != NMM_OK)
    {
      printf("netQueueAdd: failed to allocate memory\n");
      return NETQ_FAILED;
    }
    element->next = NULL;
    element->prev = my->last;
    if ( my->last )
    {
      my->last->next = element;
    }
    else
    {
      my->first = element;
      my->last = element;
    }

    element->data = data;
    element->ext.metaData = (NetAttrs) ext;
    break;
  case TYPE_STREAM:
    if ( (element = my->first) == NULL )
    {
      err = netMMAlloc ((Address*)&element, sizeof(NetQueueElemDesc));
      if (err != NMM_OK)
      {
        printf("netQueueAdd: failed to allocate memory\n");
        return NETQ_FAILED;
      }
      element->next = NULL;
      element->prev = my->last;
      my->first = element;
      my->last = element;
      element->data = NULL;
      element->ext.oobData = NULL;
    }
    if (data)
    {
      netbufAddTail(&(element->data), data);
    }
    if (ext)
    {
      netbufAddTail(&(element->ext.oobData), ext);
    }
    break;
  default:
    printf("netQueueAdd: invalid options \"%i\"\n", my->options);
    return NETQ_FAILED;
  }

  return NETQ_OK;
}


int netQueueIsEmpty (NetQueue my)
{
  return (my == NULL || my->first == NULL);
}


Error netQueuePeek(NetQueue my, NetBuf *data, NetBuf *ext)
{
  NetQueueElem	element;


  if (my == NULL || my->first == NULL )
  {
    printf("netQueueGet: Queue is empty or not initialized\n");
    return NETQ_FAILED;
  }

  element = my->first;
  switch (my->options)
  {
  case TYPE_DGRAM:
    *data = element->data;
    *ext = (NetBuf) element->ext.metaData;
    break;
  case TYPE_STREAM:
    *data = element->data;
    *ext = element->ext.oobData;
    break;
  default:
    printf("netQueueGet: invalid options \"%i\"\n", my->options);
    return NETQ_FAILED;
  }

  return NETQ_OK;
}


Error netQueueGet (NetQueue my, NetBuf *data, NetBuf *ext)
{
  NetQueueElem	element;


  if (my == NULL || my->first == NULL )
  {
    printf("netQueueGet: Queue is empty or not initialized\n");
    return NETQ_FAILED;
  }

  element = my->first;
  switch (my->options)
  {
  case TYPE_DGRAM:
    *data = element->data;
    *ext = (NetBuf) element->ext.metaData;
    my->first = element->next;
    netMMFree(element);
    break;
  case TYPE_STREAM:
    *data = element->data;
    *ext = element->ext.oobData;
    my->first = element->next;
    netMMFree(element);
    break;
  default:
    printf("netQueueGet: invalid options \"%i\"\n", my->options);
    return NETQ_FAILED;
  }

  return NETQ_OK;
}

Error netQueueRead (NetQueue my, char* buf, unsigned int *len,
                    NetBuf *ext, int type)
{
  NetQueueElem	element;


  if (my == NULL || my->first == NULL )
  {
    printf("netQueueRead: Queue is empty or not initialized\n");
    return NETQ_FAILED;
  }

  element = my->first;
  switch (my->options)
  {
  case TYPE_DGRAM:
    if (! netbufRead( &(element->data), buf, len, 0, SEEK_SET) )
    {
      printf("netQueueRead: netbufRead failed\n");
      return NETQ_FAILED;
    }
    if (! (type & DONT_SEEK) )
    {
      if (! netbufChop( &(element->data), *len, FROM_START) )
      {
        printf("netQueueRead: netbufChop failed\n");
        return NETQ_FAILED;
      }
    }
    if (ext)
      *ext = (NetBuf) element->ext.metaData;
    if ( element->data == NULL )
    { /* no more data in this DGRAM */
      my->first = element->next;
      if (my->first == NULL)
      {
        my->last = NULL;
      }
      netMMFree(element);
    }
    break;
  case TYPE_STREAM:
    if (! (type & OOB_DATA) )
    {
      if (! netbufRead( &(element->data), buf, len, 0, SEEK_SET) )
      {
        printf("netQueueRead: netbufRead failed\n");
        return NETQ_FAILED;
      }
      if (! type & DONT_SEEK )
      {
        if (! netbufChop( &(element->data), *len, FROM_START) )
        {
          printf("netQueueRead: netbufChop failed\n");
          return NETQ_FAILED;
        }
      }
      if (ext)
        *ext = NULL;
    }
    else
    {
      if (! netbufRead( &(element->ext.oobData), buf, len, 0,
                        SEEK_SET))
      {
        printf("netQueueRead: netbufRead failed\n");
        return NETQ_FAILED;
      }
      if (! type & DONT_SEEK )
      {
        if (! netbufChop( &(element->ext.oobData), *len,
                          FROM_START) )
        {
          printf("netQueueRead: netbufChop failed\n");
          return NETQ_FAILED;
        }
      }
      if (ext)
        *ext = NULL;
    }
    break;
  default:
    printf("netQueueGet: invalid options \"%i\"\n", my->options);
    return NETQ_FAILED;
  }

  return NETQ_OK;
}

