/*
    NetInQueueMngr.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/NetInQueueMngr.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 "List.h"
#include "Lifo.h"
#include "Support.h"
#include "Syscall.h"
#include "NetSyscall.h"
#include "Topsy.h"
#include "NetQueue.h"
#include "NetPoolThread.h"
#include "NetInQueueMngr.h"
#include "NetDebug.h"
#include "NetMemoryManager.h"

void netInQueueManager (ThreadArg NumOfPT)
{
  List			AllPTList;
  NetQueue		DataQueue;
  Lifo			IdlePTLifo;
  Message			QueueMsg,
  DataMsg;
  ThreadId		SenderTID,
  PoolTID,
  Parent_TID;
  ThreadInfoElem	StartedTIf;
  Error			err;
  unsigned long	numOfQueued;


  /* Init: start specifyed number of threads and set up list of threads */
  /*       and list of idle threads                                     */

  AllPTList = listNew ();
  if (AllPTList == NULL)
  {
    printf("InQMngr: Init failed (List)\n");
    tmExit();
  }
  DataQueue = netQueueNew (TYPE_DGRAM);
  if (DataQueue == NULL)
  {
    printf("InQMngr: Init failed (Queue)\n");
    tmExit();
  }
  IdlePTLifo = NULL;
  numOfQueued = 0;

  while (NumOfPT > 0)
  {
    NumOfPT--; /* do not move this into the while statement!
    					  This is a gcc bug fix :-) */
    err = tmStart (&PoolTID, netPoolThreadMain, 0, "PoolThread");
    netdbgPrintf(NETDEBUG_INQUEUE,
                 "InqueueManager: starting PoolThread %i (left %i)\n",
                 PoolTID,
                 NumOfPT);
    if ( err != TM_STARTOK)
    {
      printf("InQMngr: Init failed (tmStart)\n");
      tmExit();
    }

    err = netMMAlloc((Address*)&StartedTIf, sizeof(ThreadInfoDesc));
    if ( err != NMM_OK)
    {
      printf("InQMngr: Init failed (netAlloc)\n");
      tmExit();
    }

    StartedTIf->tid = PoolTID;
    StartedTIf->state = TIF_UNKNOWN;	// right now this list ist not used nor updated so the state is really unknown XXX
    err = listAddInFront (AllPTList, StartedTIf, NULL);
    if ( err != LIST_OK)
    {
      printf("InQMngr: Init failed (ListAdd)\n");
      tmExit();
    }

    err = lifoPush (&IdlePTLifo, (void*) PoolTID);
    if ( err != LIFO_OK)
    {
      printf("InQMngr: Init failed (LifoPush)\n");
      tmExit();
    }
  }

  /* Loop: Wait for input containing meta and possibly real data, get   */
  /*       an idle thread, remove it from the idle list and send the    */
  /*       data to this PoolThread. If its a message from a PoolThread  */
  /*       that is returning to it's idle state add it to the idle list */

  while (1)
  {
    SenderTID = ANY;
    err = tmMsgRecv (&SenderTID, ANYMSGTYPE, &QueueMsg, INFINITY);
    if (err != TM_MSGRECVOK)
    {
      printf("InQMngr: Receive failed (tmMsgRecv)\n");
      tmExit();
    }
    netdbgPrintf(NETDEBUG_INQUEUE,
                 "InqueueManager: recieved message from %i, type %i\n",
                 SenderTID, QueueMsg.id);

    switch (QueueMsg.id)
    {
    case NET_QUEUE_DATA:
      netdbgPrintf(NETDEBUG_INQUEUE,
                   "InqueueManager: recieved NET_QUEUE_DATA\n");
      if  (lifoIsEmpty(&IdlePTLifo) )
      {
        numOfQueued++;
        err = netQueueAdd (DataQueue,
                           QueueMsg.msg.netqMsg.netData,
                           QueueMsg.msg.netqMsg.netMeta );
        if ( err != NETQ_OK )
        {
          printf("InqueueManager: netQueueAdd failed\n");
          numOfQueued--;
        }
        netdbgPrintf(NETDEBUG_INQUEUE,
                     "InqueueManager: data queued (left %i)\n", numOfQueued);
      }
      else
      {
        err = lifoPop (&IdlePTLifo, (void**) &PoolTID);
        if ( err != LIFO_OK)
        {
          printf("InQMngr: Receive failed (LifoPop)\n");
          tmExit();
        }
        /*-CJ*/					netdbgPrintf(NETDEBUG_INQUEUE,
                                 "InqueueManager: data sent to %i\n", PoolTID);
        netdbgPrintf(NETDEBUG_INQUEUE,
                     "Packet attributes:\n");
        netdbgDumpAttr(NETDEBUG_INQUEUE, QueueMsg.msg.netqMsg.netMeta);
        /**/
        err = tmMsgSend (PoolTID, &QueueMsg);
        if (err != TM_MSGSENDOK)
        {
          printf("InQMngr: Receive failed (tmMsgSend)\n");
          tmExit();
        }
      }
      break;
    case NET_INQUEUE_PT_IDLE:
      netdbgPrintf(NETDEBUG_INQUEUE,
                   "InqueueManager: recieved NET_INQUEUE_PT_IDLE\n");
      if (netQueueIsEmpty (DataQueue))
      {
        err = lifoPush (&IdlePTLifo, (void*) QueueMsg.from);
        if ( err != LIFO_OK)
        {
          printf("InQMngr: Receive failed (LifoPush)\n");
          tmExit();
        }
        netdbgPrintf(NETDEBUG_INQUEUE,
                     "InqueueManager: Thread %i idle again\n",
                     QueueMsg.from);
      }
      else
      {
        err = netQueueGet (DataQueue, (NetBuf*) &(DataMsg.msg.netqMsg.netData), (NetBuf*) &(DataMsg.msg.netqMsg.netMeta) );
        if (err != NETQ_OK)
        {
          printf("InQMngr: Receive failed (QueueGet)\n");
          tmExit();
        }
        numOfQueued--;
        netdbgPrintf(NETDEBUG_INQUEUE,
                     "InqueueManager: data dequeued (left %i)\n",
                     numOfQueued);
        DataMsg.id = NET_QUEUE_DATA;
        err = tmMsgSend (QueueMsg.from, &DataMsg);
        if (err != TM_MSGSENDOK)
        {
          printf("InQMngr: Receive failed (tmMsgSend)\n");
          tmExit();
        }
      }
      break;
      /* XXX not needed right now, may prove necessary later
       * see NetPoolThread.c "case NET_CONFIG_REQUEST
      case NET_INQUEUE_PT_BUSY:
      		err = lifoSelectivePop(&IdlePTLifo, (void*) QueueMsg.from);
      		if (err != LIFO_OK) {
      			// This migh happen in high traffic situations when
      			// InQMngr schedules a PT while there is a busy
      			// message pending (however this wont do any harm)
      			// it will just take a little longer for the packet
      			// to be delivered
      			printf("InQMngr: Failed to remove PoolThread from idle list\n");	
      		}
      	break;
      	*/
    default:
      printf("InQMngr: Receive failed (unexpected message %i)\n",
             QueueMsg.id);
    }
  }
}
