/*
    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/NetModulesConf.c,v $
    Affiliation:           ETH Zuerich, TIK
    Version:               $Revision: 2936 $
    Last Date of Change:   $Date: 2007-03-21 15:23:42 +0100 (Mit, 21 Mär 2007) $  by: $Author: baderma $
 
*/
#include "NetModulesConf.h"
#include "NetModulesNew.h"
#include "NetModulesDef.h"
#include "Support.h"
#include "Syscall.h"
#include "NetSyscall.h"
#include "NetAttr.h"
#include "NetDebug.h"
#include "HashList.h"
#include "Distributor.h"
#include "ModuleLoader.h"
#include "ModuleLookup.h"
#include "NetMemoryManager.h"

NetModuleReg	netModuleRegistry = 0;
NetModule		netModuleList = 0;
HashList		netModuleHash;
unsigned long	maxModuleId = 0;

#define INFRONT(x) ((x) & 1<<30)

static int chainTypeCompare(NetModuleType type, unsigned short chainKey, int proto)
{
  if ( proto )
    return (((type>>31) & 1) &&
            ((type>>16) & 0x3FF) == (chainKey & 0x3FF));
  else
    return (( !(type>>31) & 1) &&
            ((type>>16) & 0x3FF) == (chainKey & 0x3FF));
}

static void insertDistributor(unsigned short chainKey, NetModule chain)
{
  unsigned short index = (chainKey >> 11) & 0x1F;


  if ( index >= 32 )
  {
    netdbgPrintf(NETDEBUG_CONFMNGR,
                 "insertDistributor: PPAANNIICC got more then expected\n");
    return;
  }
  for( ; chain->chain; chain = chain->chain )
    ;

  netdbgPrintf(NETDEBUG_CONFMNGR,
               "insertDistributor: try to use distributor %i\n",
               distributorTable[index]);
  if ( hashListGet(netModuleHash, (void **) &(chain->chain),
                   distributorTable[index]) != HASHOK )
  {
    netdbgPrintf(NETDEBUG_CONFMNGR,
                 "insertDistributor: no distributor found for chain %i\n",
                 chainKey);
  }
  return;
}

NetModule netChainAlloc(unsigned short chainKey)
{
  NetModule head, tail, proto = NULL, t;
  NetModuleReg reg = netModuleRegistry;

  /* first get protocoll regule */
  while ( reg )
  {
    if ( chainTypeCompare(reg->type, chainKey, 1 ) )
    {
      netdbgPrintf(NETDEBUG_CONFMNGR,
                   "netChainAlloc: module type %#x (%s) matches chain %#x\n",
                   reg->type, reg->name, chainKey);
      if (! netmodCreate(reg, &t) )
      {
        printf("netChainAlloc: can not create new protocol Module\n");
        return NULL;
      }
      if ( proto )
      {
        netdbgPrintf(NETDEBUG_CONFMNGR,
                     "netChainAlloc: UHOH allready got a protocol module\n");
      }
      proto = t;		// XXX check if unique match
    }
    reg = reg->next;
  }
  if (! proto )
  {
    printf("netChainAlloc: chain not available\n");
    return NULL;
  }
  head = NULL;
  tail = NULL;
  /* then request the other regules */
  reg = netModuleRegistry;
  while ( reg )
  {
    if ( chainTypeCompare(reg->type, chainKey, 0 ) )
    {
      if (! netmodCreate(reg, &t) )
      {
        printf("netChainAlloc: can not create new Module\n");
        return NULL;
      }
      if ( INFRONT(reg->type) )
      {
        t->chain = head;
        head = t;
      }
      else
      {
        t->chain = tail;
        tail = t;
      }
    }
    reg = reg->next;
  }

  if ( INFRONT(proto->head->type) )
  {
    proto->chain = head;
    head = proto;
  }
  else
  {
    if ( tail )
    {
      for ( t = tail; t->chain ; t = t->chain )
        ;
      t->chain = proto;
    }
    else
    {
      tail = proto;
    }
    proto->chain = NULL;
  }
  if ( head )
  {
    for ( t = head; t->chain ; t = t->chain )
      ;
    t->chain = tail;
  }
  else
  {
    head = tail;
  }

  insertDistributor(chainKey, head);
  return head;

}

void netmodLink(NetModule mod)
{
  mod->prev = NULL;
  mod->next = netModuleList;
  if ( netModuleList)
    netModuleList->prev = mod;
  netModuleList = mod;
}

void netmodGetModule(Message *msg, NetModuleId id)
{
  NetModule t;

  if ( hashListGet(netModuleHash, (void *) &t, id) != HASHOK )
  {
    printf("netmodGetModule: failed to get Module %i from HashList.\n",
           (int) id);
    msg->msg.netConf.ChainReply.len = 0;
    msg->msg.netConf.ChainReply.chain = NULL;
    return;
  }
  if ( netMMAlloc((Address*) &(msg->msg.netConf.ChainReply.chain),
                  2*sizeof(NetChain) ) != NMM_OK )
  {
    msg->msg.netConf.ChainReply.len = 0;
    msg->msg.netConf.ChainReply.chain = NULL;
    return;
  }
  msg->msg.netConf.ChainReply.len = 1;
  if ( id != t->id )
  {
    printf("netmodGetModule: What the hell is going on reqId %i modId %i\n",
           (int) id, (int) t->id);
  }
  ((NetChain*) msg->msg.netConf.ChainReply.chain)[0].function =
    t->head->mainFunc;
  ((NetChain*) msg->msg.netConf.ChainReply.chain)[0].id = t->id;
  ((NetChain*) msg->msg.netConf.ChainReply.chain)[0].arg = t;
  ((NetChain*) msg->msg.netConf.ChainReply.chain)[1].function = NULL;
  ((NetChain*) msg->msg.netConf.ChainReply.chain)[1].id = 0;
  ((NetChain*) msg->msg.netConf.ChainReply.chain)[1].arg = NULL;
  return;
}


int netmodCreate(NetModuleReg reg, NetModule *mod)
{
  NetModule t;

  if ( netMMAlloc((Address *) mod, sizeof(NetModuleDesc) ) != NMM_OK )
  {
    *mod = NULL;
    return 0;
  }
  t = *mod;
  t->chain = NULL;
  netmodLink(t);
  t->id = ++maxModuleId;
  t->head = reg;
  if (! netattrNew( &(t->attributes) ) )
  {
    netMMFree(t);
    return 0;
  }
  reg->count++;
  t->count = 0;
  t->extra = NULL;

  if ( hashListAdd(netModuleHash, (void *) t, t->id) != HASHOK )
  {
    printf("netmodCreate: failed to add Module %i to HashList. Ignored\n",
           (int) t->id);
  }

  /* init the Module */
  if ( reg->initFunc)
    reg->initFunc(t);
  return 1;
}

static int netmodCreateId(NetModuleReg reg, NetModule *mod, NetModuleId id)
{
  NetModule t;

  if ( netMMAlloc((Address *) mod, sizeof(NetModuleDesc) ) != NMM_OK )
  {
    *mod = NULL;
    return 0;
  }
  t = *mod;
  t->chain = NULL;
  netmodLink(t);
  t->id = id;
  t->head = reg;
  if (! netattrNew( &(t->attributes) ) )
  {
    netMMFree(t);
    return 0;
  }
  reg->count++;
  t->count = 0;
  t->extra = NULL;

  if ( hashListAdd(netModuleHash, (void *) t, t->id) != HASHOK )
  {
    printf("netmodCreate: failed to add Module %i to HashList. Ignored\n",
           (int) t->id);
  }

  /* init the Module */
  if ( reg->initFunc)
    reg->initFunc(t);
  return 1;
}

int netmodRegAdd(NetModuleReg reg, int copy)
{
  NetModuleReg t;

  if ( copy == 0 )
  {
    t = reg;
    t->prev = NULL;
    t->next = netModuleRegistry;
    if ( netModuleRegistry )
      netModuleRegistry->prev = t;
    netModuleRegistry = t;

    t->count = 0;
    return 1;
  }

  if ( netMMAlloc((Address *) &t, sizeof(NetModuleRegDesc) ) != NMM_OK )
    return 0;

  t->prev = NULL;
  t->next = netModuleRegistry;
  if ( netModuleRegistry )
    netModuleRegistry->prev = t;
  netModuleRegistry = t;

  t->type = reg->type;
  t->count = 0;
  t->mainFunc = reg->mainFunc;
  t->confFunc = reg->confFunc;
  t->initFunc = reg->initFunc;
  str_ncpy(t->name, reg->name, NET_MOD_NAMESIZE);

  return 1;
}

int netmodInit()
{
  int i = 0, j = 0;
  NetModule mod;

  netModuleRegistry = NULL; //(jb) testing initialization 9.5.01

  if ((netModuleHash = hashListNew()) == NULL)
    return 0;

  /* add lookup and loader module first */
  if ( netmodRegAdd( &lookupModuleRegistry, 0) != 1 )
  {
    printf("netmodInit: netmodRegAdd failed ???\n");
    return 0; /* this can not happen, but offensive programming */
  }
  if (! netmodCreateId( &lookupModuleRegistry, &mod, NET_MODULE_LOOKUP_ID) )
  {
    printf("netmodInit: netmodCreate of %s failed\n",
           lookupModuleRegistry.name );
    return 0;
  }

  if ( netmodRegAdd( &loaderModuleRegistry, 0) != 1 )
  {
    printf("netmodInit: netmodRegAdd failed ???\n");
    return 0; /* this can not happen, but offensive programming */
  }
  if (! netmodCreateId( &loaderModuleRegistry, &mod, NET_MODULE_LOADER_ID) )
  {
    printf("netmodInit: netmodCreate of %s failed\n",
           loaderModuleRegistry.name );
    return 0;
  }
  /* now add the distributors */
  for ( i = 0; distributorModuleRegistry[i].type != 0; i++ )
  {
    if ( netmodRegAdd( &(distributorModuleRegistry[i]), 0) != 1 )
    {
      printf("netmodInit: netmodRegAdd failed ???\n");
      return 0; /* this can not happen, but offensive programming */
    }
    if ( distributorModuleRegistry[i].type == IN_DISTRIBUTOR_TYPE )
    {
      /* the in distributor needs a well known NetModuleId */
      if (! netmodCreateId( &(distributorModuleRegistry[i]), &mod,
                            NET_MODULE_IN_DISTRIBUTOR_ID) )
      {
        printf("netmodInit: netmodCreate of %s failed\n",
               distributorModuleRegistry[i].name );
        return 0;
      }
    }
    else
    {
      if (! netmodCreate( &(distributorModuleRegistry[i]), &mod) )
      {
        printf("netmodInit: netmodCreate of %s failed\n",
               distributorModuleRegistry[i].name );
        return 0;
      }
    }
    for ( j = 0; j < sizeof(distributorTable)/sizeof(NetModuleId); j++ )
    {
      if ( distributorModuleRegistry[i].type == distributorTable[j] )
        distributorTable[j] = mod->id;
    }
  }
  mod = netModuleList;
  while ( mod )
  {
    netdbgPrintf(NETDEBUG_CONFMNGR,
                 "%s: %#x, %#x\n", mod->head->name, mod->id, mod->head->type);
    mod = mod->next;
  }
  return 1;
}

NetModuleId netmodChainNext( NetModuleId this )
{
  NetModule next;

  if ( hashListGet(netModuleHash, (void **) &next, this) != HASHOK )
  {
    return 0;
  }
  else
  {
    return next->chain->id;
  }
}

void netmodConfigure(Message *msg)
{
  NetModule this, temp;
  NetModuleId id = msg->msg.netConf.ModuleConf.id;
  NetModuleType tp = msg->msg.netConf.TypeConf.type;
  Message reply;
  ThreadId	from = msg->from;
  MessageId	msgId;
  int			exitCode;

  reply.id = NET_CONF_MODULEREPLY;
  switch (msg->id)
  {
  case NET_CONF_MODULESET:
  case NET_CONF_MODULEGET:
    if ( hashListGet(netModuleHash, (void **) &this, id) != HASHOK )
    {
      printf("netmodConfigure: unknown Module %i\n", (int) id);
      reply.msg.netConf.ModuleConf.attrVec = NULL;
      reply.msg.netConf.ModuleConf.len = 0;
      reply.msg.netConf.ModuleConf.id = id;
      tmMsgSend(from, &reply);
      return;
    }

    /* call the module configuration function */
    if (this->head->confFunc)
    {
      exitCode = this->head->confFunc(this, msg);
      // this is real C :-)
      ((NetAttr*) msg->msg.netConf.ModuleConf.attrVec)[msg->msg.netConf.ModuleConf.len].data = exitCode;
      msg->id = NET_CONF_MODULEREPLY;
      tmMsgSend(from, msg);
      return;
    }
    else
    {
      printf("netmodConfigure: can not configure Module %i\n", (int) id);
      reply.msg.netConf.ModuleConf.attrVec = NULL;
      reply.msg.netConf.ModuleConf.len = 0;
      reply.msg.netConf.ModuleConf.id = id;
      tmMsgSend(from, &reply);
      return;
    }
    break;
  case NET_CONF_TYPEGET:
  case NET_CONF_TYPESET:
    if ( msg->id == NET_CONF_TYPEGET )
    {
      msg->id = NET_CONF_MODULEGET;
    }
    else if (msg->id == NET_CONF_TYPESET)
    {
      msg->id = NET_CONF_MODULESET;
    }
    // loop untill type matches
    // wie gahts das mit registry und module chetti?
    // module id isch fortlaufend, muess also registry type vergliche
    // und det Bits 31 und Bit 16-25 (Bit 30? Bit 26-29?)
    temp = netModuleList;
    while (temp)
    {
      if (temp->head->type == tp )
      {
        if (temp->head->confFunc)
        {
          exitCode = temp->head->confFunc(temp, msg);
          // this is real C :-)
          ((NetAttr*) msg->msg.netConf.TypeConf.attrVec)[msg->msg.netConf.TypeConf.len].data = exitCode;
          msgId = msg->id;
          msg->id = NET_CONF_MODULEREPLY;
          tmMsgSend(from, msg);
          msg->id = msgId;
        }
        else
        {
          printf("netmodConfigure: can not configure Module %i\n", (int) temp->id);
          reply.msg.netConf.ModuleConf.attrVec = NULL;
          reply.msg.netConf.ModuleConf.len = 0;
          reply.msg.netConf.ModuleConf.id = temp->id;
          tmMsgSend(from, &reply);
          return;
        }
      }
      temp = temp->next;
    }
    // send a terminator element
    reply.msg.netConf.ModuleConf.attrVec = NULL;
    reply.msg.netConf.ModuleConf.len = 0;
    reply.msg.netConf.ModuleConf.id = 0;
    tmMsgSend(from, &reply);
    break;
  default:
    /* will never get here since NetConfigMngr filters the id
     * already but gcc needs this */
    break;
  }

}

