/*
    Copyright 2000 (c) by Claudio Jeker,
    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/Modules/ModuleLoader.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 "Topsy.h"
#include "NetModulesNew.h"
#include "NetModulesDef.h"
#include "NetDebug.h"
#include "LWThreads.h"
#include "HashList.h"
#include "Syscall.h"
#include "Support.h"
#include "NetConfigMngr.h"
#include "ModuleLoader.h"
#include "NetMemoryManager.h"

void netModuleLoaderInit(NetModule this)
{
  /* dummy function */
  return;
}


int netModuleLoaderConfig(NetModule this, Message *msg)
{
  /* dummy function */
  return 0;
}

void netModuleLoaderMain(LWPool lw, ThreadArg arg)
{
  HashList lookupTable = (HashList) arg;
  ThreadId requestor, from, module;
  Message msg;
  long i, len, result;
  NetChain *chain;


  /* XXX init */

  while (1)
  {
    if (lwMsgRecv(lw, &requestor, 0, &msg, 0) != LW_OK)
    {
      netdbgPrintf(0, "netModuleLoader: failed to receive\n");
      continue;
    }
    netdbgPrintf(NETDEBUG_MODULES,
                 "netModuleLoader: message from %i\n", (int) requestor);
    switch (msg.id)
    {
    case NET_CONF_CHAINREQ:
    case NET_CONF_MODULEREQ:
      from = netConfigThread;
      if (tmMsgSend (from, &msg) != TM_MSGSENDOK)
      {
        netdbgPrintf(0, "netModuleLoader: failed to send msg to netConfigMngr\n");
        msg.id = UNKNOWN_SYSCALL; /* temporary failure */
        break;
      }
      netdbgPrintf(NETDEBUG_MODULES,
                   "netModuleLoader: message sent to config\n");
      if (tmMsgRecv (&from, ANYMSGTYPE, &msg, INFINITY) != TM_MSGRECVOK)
      {
        netdbgPrintf(0, "netModuleLoader: failed to receive msg from netConfigureMngr\n");
        msg.id = UNKNOWN_SYSCALL;
        break;
      }
      netdbgPrintf(NETDEBUG_MODULES,
                   "netModuleLoader: netConfigureMngr reply, type %i (%i %i)\n",
                   (int) msg.id, (int) NET_CONF_CHAINREPLY,
                   (int)NET_CONF_MODULEREPLY );
      if ( (MessageId) msg.id != (MessageId) NET_CONF_CHAINREPLY &&
           ((MessageId) msg.id != (MessageId) NET_CONF_MODULEREPLY ||
            msg.msg.netConf.ChainReply.len != 1 ) )
      {
        netdbgPrintf(NETDEBUG_MODULES,
                     "netModuleLoader: unknown type %i (len %i)\n",
                     (int) msg.id, (int) msg.msg.netConf.ChainReply.len);
        msg.id = UNKNOWN_SYSCALL;
        break;
      }
      chain = msg.msg.netConf.ChainReply.chain;
      len = msg.msg.netConf.ChainReply.len;
      netdbgPrintf(NETDEBUG_MODULES,
                   "netModuleLoader: try to load chain %p len %i\n",
                   chain, len);

      if ( chain == NULL )
      {
        /* no such module or chain, hard error */
        if ( msg.id == NET_CONF_CHAINREPLY )
        {
          msg.msg.netConf.ChainReq.id = -1;
        }
        else
        {
          msg.msg.netConf.ModuleReq.id = -1;
        }
        break;
      }

      if ( msg.id == NET_CONF_MODULEREPLY )
        len++; /* this is a hack
      						to simulate the missing distributor */
      for (i = 0; i < len - 1; i++)
      {
        /* we do not load the distributor that is at
         * the end of the chain */
        if (lwStart(lw, &module, (chain[i]).function,
                    (chain[i]).arg, NULL) != LW_OK)
        {
          netdbgPrintf(NETDEBUG_MODULES, "netModuleLoader: failed to start module\n");
          netMMFree(chain);
          msg.id = UNKNOWN_SYSCALL;
          break;

        }
        netdbgPrintf(NETDEBUG_MODULES,
                     "netModuleLoader: lookuptable add %i -> %i (%s) \n",
                     chain[i].id, module,
                     ((NetModule) chain[i].arg)->head->name);
        //**************(jb)RACE CONDITION!!!********************
        /*  					if ( hashListAdd(lookupTable, (void*) module,  */
        /*  								chain[i].id) != HASHOK ) { */
        /*  						netdbgPrintf(NETDEBUG_MODULES,  */
        /*  							"netModuleLoader: failed to update lookup table (table = %p)\n", lookupTable); */
        /*  						netMMFree(chain); */
        /*  						msg.id = UNKNOWN_SYSCALL; */
        /*  						break; */
        /*  					} */

        //(jb, 20.6.01) somehow it occurs that we get a  HASHDUPLICATEKEY back !?!
        //this is a hack...
        result = hashListAdd(lookupTable, (void*) module, chain[i].id);
        if ( !(result == HASHOK || result == HASHDUPLICATEKEY) )
        {
          netdbgPrintf(NETDEBUG_MODULES,
                       "netModuleLoader: failed to update lookup table (table = %p), result:%d\n", lookupTable, result);
          netMMFree(chain);
          msg.id = UNKNOWN_SYSCALL;
          break;
        }
        //end of hack...

        netdbgPrintf(NETDEBUG_MODULES,
                     "netModuleLoader: loaded module %i -> %i (%s)\n",
                     (int) chain[i].id, (int) module,
                     ((NetModule) chain[i].arg)->head->name);
      }
      if ( len > 0 )
      {
        if ( msg.id == NET_CONF_CHAINREPLY )
        {
          msg.msg.netConf.ChainReq.id = (chain[0]).id;
        }
        else
        {
          msg.msg.netConf.ModuleReq.id = module;
        }
        netMMFree(chain);
      }
      else
      {
        netdbgPrintf(NETDEBUG_MODULES,
                     "netModuleLoader: empty chain\n");
        msg.id = UNKNOWN_SYSCALL;
        break;
      }
      break;
    default:
      netdbgPrintf(0, "netModuleLoader: wrong message type received\n");
      msg.id = UNKNOWN_SYSCALL;
      break;
    }
    netdbgPrintf(NETDEBUG_MODULES,
                 "netModuleLoader: sending msg back to %i, type %i\n",
                 (int) requestor, (int) msg.id);
    if (lwMsgSend (lw, requestor, &msg) != TM_MSGSENDOK)
    {
      netdbgPrintf(0, "netModuleLoader: failed to send back reply\n");
    }
  }
}

NetModuleRegDesc loaderModuleRegistry = {
                                          NULL, NULL,
                                          MODULE_LOADER_TYPE,
                                          0,
                                          netModuleLoaderMain,
                                          netModuleLoaderConfig,
                                          netModuleLoaderInit,
                                          "Modul Loader"
                                        };
