/*
 * sim_init_create.c
 */

unsigned long getRate(char *);
unsigned long getTime(char *, int);
void          getCommonNetArgs(NET *, Net *);
void          getTraceSize(char *, int *, int *);
void          doITrace(char *, ROUTER_STATE *, IPhost *, long , char *);
void          doMaxMinTrace(char *, ROUTER_STATE *, IPhost *, long, char *);

unsigned long getRate(char *cp)
{
  unsigned long d=0, dc=1, r=1, s=1, rate;

  if (*cp == 't'  ||  *cp == 'T') {
    if (*(cp+1) == '1') 
      return 1536000/8;
    else if (*(cp+1) == '3')
      return 44210000/8;
    else
      error1("ERROR: cannot interpret rate: %s\n", cp);
  }
  sscanf(cp, "%lu", &rate);
  while(isdigit(*cp))
    cp++;
  if (*cp == '.')
    *cp++;	/* VENKAT: added this */
    while (isdigit(*cp))
      d = d*10 + (*cp++ - '0'), dc *= 10;
  while (*cp != '\0') {
    if (*cp == 'K'  ||  *cp == 'k')
      s = 1000;
    else if (*cp == 'M'  ||  *cp == 'm')
      s = 1000000;
    else if (*cp == 'G'  ||  *cp == 'g')
      s = 1000000000;
    else if (*cp == 'B')
      ;
    else if (*cp == 'b')
      r = 8;
    else 
      break;
    cp++;
  }
  rate = (rate*s + d*s/dc)/r;
  return rate;
}

unsigned long getTime(char *cp, int defs)
{
  unsigned long d=0, dc=1, t, s=defs;

  sscanf(cp, "%lu", &t);
  while(isdigit(*cp))
    cp++;
  if (*cp == '.')
    *cp++;	/* VENKAT: Fixed this */
    while (isdigit(*cp))
      d = d*10 + (*cp++ - '0'), dc *= 10;
  if (*cp == 'm' && *(cp+1) != 'i')
    s = 1000;
  else if (*cp == 'u')
    s = 1;
  else if (*cp == 's')
    s = 1000000;
  else if (*cp == 'm'  &&  *(cp+1) == 'i')
    s = 60000000;
  t = t*s + d*s/dc;

  return t;
}

void getCommonNetArgs(NET *net, Net *ns)
{
  char *cp;

  if ((cp = getArg(net->argc, net->argv, "rate")) != NULL)
    sscanf(cp, "%d", &ns->rate);
  else if ((cp = getnArg(net->argc, net->argv, "rate=", 5)) != NULL) 
    ns->rate = getRate(cp);
  if ((cp = getArg(net->argc, net->argv, "delay")) != NULL) 
    ns->delay = getTime(cp, 1);
  else if ((cp = getnArg(net->argc, net->argv, "delay=", 6)) != NULL)
    ns->delay = getTime(cp, 1);
  if ((cp = getArg(net->argc, net->argv, "delayVar")) != NULL)
    ns->delayVar = getTime(cp, 1);
  else if ((cp = getnArg(net->argc, net->argv, "delayVar=", 9)) != NULL)
    ns->delayVar = getTime(cp, 1);
  if ((cp = getArg(net->argc, net->argv, "minPacket")) != NULL)  
    sscanf(cp, "%d", &ns->minPacketSize);
  else if ((cp = getnArg(net->argc, net->argv, "minPacket=", 10)) != NULL)
    sscanf(cp, "%d", &ns->minPacketSize);
  if ((cp = getArg(net->argc, net->argv, "hdrSize")) != NULL)
    sscanf(cp, "%d", &ns->packetHeader);
  else if ((cp = getnArg(net->argc, net->argv, "hdrSize=", 8)) != NULL)
    sscanf(cp, "%d", &ns->packetHeader);
  if ((cp = getArg(net->argc, net->argv, "pktOverhead")) != NULL)
    sscanf(cp, "%d", &ns->packetOverhead);
  else if ((cp = getnArg(net->argc, net->argv, "pktOverhead=", 12)) != NULL)
    sscanf(cp, "%d", &ns->packetOverhead);
  if ((cp = getArg(net->argc, net->argv, "maxPacket")) != NULL)
    sscanf(cp, "%d", &ns->maxPacket);
  else if ((cp = getnArg(net->argc, net->argv, "maxPacket=", 10)) != NULL)
    sscanf(cp, "%d", &ns->maxPacket);
  if ((cp = getArg(net->argc, net->argv, "collisions")) != NULL  &&
      (!strcmp(cp, "on") || !strcmp(cp, "ON"))) {
    ns->collisionDelay = 51;
    ns->time2bBusy = 51;
  }
  else if ((cp = getnArg(net->argc, net->argv, "collisions=", 11)) != NULL
	   && (!strcmp(cp, "on") || !strcmp(cp, "ON"))) {
    ns->collisionDelay = 51;
    ns->time2bBusy = 51;
  }
}

void getTraceSize(char *cp, int *nEl, int *nBufs) 
{
  sscanf(cp, "%d", nEl);
  while (*cp != ':'  &&  *cp != ' '  &&  *cp != '\0')
    cp++;
  if (*cp == ':') {
    cp++;
    sscanf(cp, "%d", nBufs);
  }
  else
    *nBufs = 1;
}

void doITrace(char *cp, ROUTER_STATE *rs, IPhost *iph, long timeInc, char *type)
{
  unsigned long t;
  IPhost iph2;
  ITrace *tp;

  str2ipHost(&iph2, cp);
  if (IP_EQUAL(*iph, iph2)) {
    tp = (ITrace *) xMallocZero(sizeof(ITrace));
    if (!strcmp(type, "in"))
      rs->traceIn[rs->connectCnt] = tp;
    else if (!strcmp(type, "out"))
      rs->traceOut[rs->connectCnt] = tp;
    else if (!strcmp(type, "inout"))
      rs->traceInOut[rs->connectCnt] = tp;
    tp->stype = TRACE_STRUCT_I;
    t = timeInc > 0 ? timeInc : 100000;
    tp->nextTime = t;
    tp->timeInc = t;
    tp->m_factor = 1000000/t;
    tp->d_factor = 1024;
    sprintf(tp->id, "%s-%s", type, cp);
    tp->arrayNum = 0;
    if (traceRegisterAddBuf(rs->ts.traceObj, (char *)tp, sizeof(ITrace)))
      printf("\nERROR: traceRegisterAddBuf fails for %s router %s!\n",
	     type, rs->name);
  }  
}

void doMaxMinTrace(char *cp, ROUTER_STATE *rs, IPhost *iph, long timeInc, 
		   char *type)
{
  unsigned long t;
  IPhost iph2;
  MaxMinTrace *tp;

  str2ipHost(&iph2, cp);
  if (IP_EQUAL(*iph, iph2)) {
    tp = (MaxMinTrace *) xMallocZero(sizeof(MaxMinTrace));
    if (!strcmp(type, "queueCnt")) {
      rs->traceQueue[rs->connectCnt] = tp;
      tp->div = (rs->defBufferCnt + 256)/256;
    }
    else if (!strcmp(type, "queueLen")) {
      rs->traceQueueLen[rs->connectCnt] = tp;
      tp->div = (rs->defQueueLen + 256)/256;
    }
    tp->stype = TRACE_STRUCT_MINMAX;
    t = timeInc > 0 ? timeInc : 100000;
    tp->nextTime = t;
    tp->timeInc = t;
    tp->max = 0;
    tp->min = 64000;
    tp->otime = 0;
    tp->sum = 0;
    tp->cnt = 0;

    sprintf( tp->id, "%s-%s", type, cp);
    tp->arrayNum = 0;
    if (traceRegisterAddBuf(rs->ts.traceObj, (char *)tp, sizeof(MaxMinTrace)))
      printf("\nERROR: traceRegisterAddBuf fails for %s router %s!\n",
	     type, rs->name);
  }  
}

static void
create(SIM_PSTATE *ps)
{
  char		*cp;
  int		i, k, n, m, netId=0;
  long 	        timeInc=-1; /* VENKAT */
  Net 		*ns;
  NET		*net;
  CONNECT 	*con;
  OBJ		*obj;
  IPhost	iph, iph2;
  ROUTER_STATE  *rs;
  RouteEntry	*re;

  ps->numNets = 0;
  ps->numHosts = 0;
  for (net=netList; net; net=net->next) {
    ns = (Net *)xMallocZero(sizeof(Net));
    if (netId > MAX_NETS)
      Kabort("ERROR: Need to increase MAX_NETS!\n");
    ps->Nets[netId] = ns;
    ps->numNets++;
    ns->id = netId++;
    ns->ps = ps;
    if (!strcmp(net->type, "ETH")) {
      ns->type = TYPE_ETH;
      ns->handler = sim_ethHandler;
      ns->rate = 1250000;
      ns->delay = 0;
      ns->delayVar = 0;
      ns->collisionDelay = ns->time2bBusy = 0;
      ns->minPacketSize = 64;
      ns->packetOverhead = 8;
      ns->packetHeader = 0;
      ns->maxPacket = 1518;
      getCommonNetArgs(net, ns);
    }
    else if (!strcmp(net->type, "PP")  ||  !strcmp(net->type, "IP")  ||
	     !strcmp(net->type, "EPP")) {
      if (!strcmp(net->type, "EPP"))
	ns->type = TYPE_ETH;
      else
	ns->type = TYPE_IP;
      ns->handler = sim_ipHandler;
      ns->minPacketSize = 0;
      ns->packetOverhead = 0;
      ns->packetHeader = 0;
      ns->maxPacket = 1500;
      ns->delay = 0;
      ns->delayVar = 0;
      getCommonNetArgs(net, ns);
      ns->collisionDelay = ns->time2bBusy = 0;
      if (ns->rate == 0)
	error1("ERROR: rate missing for net\n", NULL);
    }
    else if (!strcmp(net->type, "ABS")) {
      ns->type = TYPE_ETH;
      ns->handler = sim_ethHandler;
      ns->minPacketSize = 0;
      ns->packetOverhead = 0;
      ns->maxPacket = 1500;
      ns->delay = 0;
      ns->delayVar = 0;
      getCommonNetArgs(net, ns);
      if (ns->rate == 0)
	error1("ERROR: rate missing for net\n", NULL);
      ns->packetOverhead -= sizeof(ETHhdr);
    }   
    else {
      error1("ERROR: Unknown type of net: %s\n", net->type);
    }
    ns->control = defaultNetControl;
    if (net->netAddr == NULL) {
      ns->net.a = 192;
      ns->net.b = ns->net.d;
      ns->net.c = ns->id;
    }
    else
      str2ipHost(&ns->net, net->netAddr);
    getIpMask(&ns->mask, &ns->net);
    ns->allGroups = NULL;
    ns->numHosts = net->connectCnt;
/*     ns->host2idMap = mapCreate(2*ns->numHosts, sizeof(Simhost)); */
    ns->allHosts = (Simhost *) xMallocZero(sizeof(Simhost)*ns->numHosts);

    /* --- Do connections for net */
    for (i=0,con=net->connections; i<net->connectCnt; i++,con=con->next) {
      ns->allHosts[i].type = ns->type;
      if (con->hostAddr == NULL) {
	iph = ns->net;
	iph.d = i;
      }
      else {
	str2ipHost(&iph, con->hostAddr);
	iph.a |= ns->net.a, iph.b |= ns->net.b, iph.c |= ns->net.c;
	iph.d |= ns->net.d;
      }
      if (ns->type == TYPE_ETH) {
	ip2eth(&iph, &ns->allHosts[i].addr.eth);
      }
      else if (ns->type == TYPE_IP) {
	ns->allHosts[i].addr.ip = iph;
      }
      else
	error1("ERROR: Unknown Network type: %d\n", (char *)ns->type);
/*       mapBind(ns->host2idMap, (void *)(ns->allHosts+i), (void *)i); */
      if (con->obj->type == HOST_TYPE) 
	addKey(ps, con->prot->name, con->prot->instance, 
	       &iph, OBJTYPE_XOBJ,
	       NULL, 0, ns->id, con->obj->id);
      else if (con->obj->type == ROUTER_TYPE) {
	if ((rs=(ROUTER_STATE *)con->obj->state) == NULL) {
	  rs = (ROUTER_STATE *)xMallocZero(sizeof(ROUTER_STATE));
	  if (RoutersCnt >= MAX_ROUTERS)
	    error1("ERROR: Too many routers (> %d)\n", (char *)MAX_ROUTERS);
	  Routers[RoutersCnt++] = rs;
	  rs->name = con->obj->name;
	  rs->ps = ps;
	  con->obj->state = (void *)rs;
	  rs->connectCnt = 0;
          rs->defQueueLen = 200;
          rs->traceType = -1;
	  for (k=0; k<con->obj->argc; k++) {
	    if (!strcmp(con->obj->argv[k], "buf"))
	      sscanf(con->obj->argv[++k], "%d", &rs->defBufferCnt);
	    else if (!strncmp(con->obj->argv[k], "buf=", 4))
              sscanf(con->obj->argv[k]+4, "%d", &rs->defBufferCnt);
            else if (!strncmp(con->obj->argv[k], "maxlen=", 7))
              sscanf(con->obj->argv[k]+7, "%d", &rs->defQueueLen);
	    else if (!strcmp(con->obj->argv[k], "trace")  ||
	             !strncmp(con->obj->argv[k], "trace=", 6)) {
	      rs->traceType = TRACE_TYPE_DETAILED;
	      if (!strcmp(con->obj->argv[k], "trace")) 
		cp = con->obj->argv[++k];
	      else
	        cp = con->obj->argv[k]+6;
	      getTraceSize(cp, &n, &m);
	      if (n > 0) {
		TRACE_MALLOC(rs->ts, n, TRACE_ROUTER_FCFS, con->obj->name, 
			     con->obj->routerType, m);
	      }
	    }
	    else if (!strcmp(con->obj->argv[k], "shortTrace")  ||
		     !strncmp(con->obj->argv[k], "shortTrace=", 11)) {
	      rs->traceType = TRACE_TYPE_SHORT;
	      if (!strcmp(con->obj->argv[k], "shortTrace")) 
                cp = con->obj->argv[++k];
	      else
	        cp=con->obj->argv[k]+11;
	      getTraceSize(cp, &n, &m);
	      if (n > 0) {
	        TRACE_MALLOC(rs->ts, n, TRACE_ROUTER_FCFS, con->obj->name, 
			     con->obj->routerType, m);
	      }
	    }
            else if (!strcmp(con->obj->argv[k], "pktTrace") ||
		     !strncmp(con->obj->argv[k], "pktTrace=", 9)) {
              rs->traceType = TRACE_TYPE_PKT;
	      if (!strcmp(con->obj->argv[k], "pktTrace")) 
                cp = con->obj->argv[++k];
	      else
		 cp = con->obj->argv[k]+9;
	      getTraceSize(cp, &n, &m);
              if (n > 0) {
                TRACE_MALLOC(rs->ts, n, TRACE_ROUTER_FCFS, con->obj->name, 
                             con->obj->routerType, m);
              }
            }
            else if (!strncmp(con->obj->argv[k], "pktTraceBeginTime=", 18)) 
	      rs->pktTraceBeginTime = getTime(con->obj->argv[k]+18, 1000)/1000;
            else if (!strncmp(con->obj->argv[k], "pktTraceEndTime=", 16)) 
              rs->pktTraceEndTime = getTime(con->obj->argv[k]+18, 1000)/1000;
	  }
/*  	  for (k=0; simRouterInitFns[k].name != NULL; k++)
	    if (!strcmp(con->obj->routerType, simRouterInitFns[k].name)) {
	      simRouterInitFns[k].fn(rs, con->obj->argc, con->obj->argv);
	      break;
            }
*/
	}

	if (rs->connectCnt > DEFNUM_PORTS)
          Kabort("ERROR: Need to increase DEFNUM_PORTS!\n");
	rs->myAddr[rs->connectCnt] = ns->allHosts[i];
	rs->inPortNets[rs->connectCnt] = ns;
	rs->outPortNets[rs->connectCnt] = ns;
	rs->outQueueCnt[rs->connectCnt] = 0;
	timeInc = -1;
	for (k=0; k<con->obj->argc; k++) {
          if (!strcmp(con->obj->argv[k], "timeInc")) 
	    timeInc = getTime(con->obj->argv[++k], 1);
          else if (!strncmp(con->obj->argv[k], "timeInc=", 8)) 
	    timeInc = getTime(con->obj->argv[k]+8, 1);
	  else if (!strcmp(con->obj->argv[k], "traceIn"))
	    doITrace(con->obj->argv[++k], rs, &iph, timeInc, "in");
	  else if (!strncmp(con->obj->argv[k], "traceIn=", 8)) 
	    doITrace(con->obj->argv[k]+8, rs, &iph, timeInc, "in");
	  else if (!strcmp(con->obj->argv[k], "traceOut"))
	    doITrace(con->obj->argv[++k], rs, &iph, timeInc, "out");
	  else if (!strncmp(con->obj->argv[k], "traceOut=", 9)) 
	    doITrace(con->obj->argv[k]+9, rs, &iph, timeInc, "out");
	  else if (!strcmp(con->obj->argv[k], "traceInOut"))
	    doITrace(con->obj->argv[++k], rs, &iph, timeInc, "inout");
	  else if (!strncmp(con->obj->argv[k], "traceInOut=", 11)) 
	    doITrace(con->obj->argv[k]+11, rs, &iph, timeInc, "inout");
	  else if (!strcmp(con->obj->argv[k], "traceQueue"))
	    doMaxMinTrace(con->obj->argv[++k], rs, &iph, timeInc, "queueCnt");
	  else if (!strncmp(con->obj->argv[k], "traceQueue=", 11)) 
	    doMaxMinTrace(con->obj->argv[k]+11, rs, &iph, timeInc, "queueCnt");
	  else if (!strcmp(con->obj->argv[k], "traceQueueLen"))
	    doMaxMinTrace(con->obj->argv[++k], rs, &iph, timeInc, "queueLen");
	  else if (!strncmp(con->obj->argv[k], "traceQueueLen=", 14)) 
	    doMaxMinTrace(con->obj->argv[k]+14, rs, &iph, timeInc, "queueLen");
          else if (!strncmp(con->obj->argv[k], "pktTracePortOut=", 16)) {
            str2ipHost(&iph2, con->obj->argv[k]+16);
            if (IP_EQUAL(iph, iph2)) {
	      rs->pktTracePortOut[rs->connectCnt] = 1;
            }
          }
          else if (!strncmp(con->obj->argv[k], "pktTracePortIn=", 15)) {
            str2ipHost(&iph2, con->obj->argv[k]+15);
            if (IP_EQUAL(iph, iph2)) {
              rs->pktTracePortIn[rs->connectCnt] = 1;
            }
          }
	}	
/* 	if ((cp = (char *)xsimGetRouterFun(con->obj->routerType)) == NULL) */
/* 	  error1("ERROR: Router type `%s' is not defined!\n",  */
/* 		 con->obj->routerType); */
	addKey(ps, con->obj->name, NULL, &iph, OBJTYPE_FUN, 
	       (char *)rs, rs->connectCnt++, ns->id, con->obj->id);
      }
    }
  }

  /* --- Call router init functions */
  for (n=0,net=netList; net; net=net->next) {
    for (i=0,con=net->connections; i<net->connectCnt; i++,con=con->next) {
      if (con->obj->type == ROUTER_TYPE) {
        rs = con->obj->state;
        if (rs->inFun != NULL)
          continue;
        for (k=0; simRouterInitFns[k].name != NULL; k++)
          if (!strcmp(con->obj->routerType, simRouterInitFns[k].name)) {
            simRouterInitFns[k].fn(rs, con->obj->argc, con->obj->argv);
            break;
          }
      }
    }
  }

  /* --- Now add router tables to routers! */
  for (obj=routerList; obj!=NULL; obj=obj->next) {
    rs = (ROUTER_STATE *) obj->state;
    if (rs == NULL)
      continue;
    re = (RouteEntry *)xMallocZero(sizeof(RouteEntry)*obj->rtableCnt);
    rs->Routes = re;
    rs->numRoutes = obj->rtableCnt;
    for (k=0; k<obj->rtableCnt; k++) {
      re->net = obj->forNet[k];
/*      getIpMask(&re->mask, &re->net); */
      re->hop.type = TYPE_IP;
      re->outPort = -1;
      getIpMask(&re->mask, &obj->useHost[k]);
      IP_AND(iph, re->mask, obj->useHost[k]);
      for (i=0; i<rs->connectCnt; i++) {
	if (IP_EQUAL(rs->outPortNets[i]->net, iph)) {
	  re->outPort = i;
	  if (IP_EQUAL(iph, re->net))
	    re->hop.addr.ip = ipLocalHost;
	  else
	    re->hop.addr.ip = obj->useHost[k];
	  break;
	}
      }
/*      if (re->outPort == -1) 
	Kabort("ERROR: outPort is -1 when creating router tables!\n");
*/
      re++;
    }
  }
  
  if (debug) {
    for (k=0; k<RoutersCnt; k++) {
      rs = Routers[k];
      printf("\nROUTER:  %s\n", rs->name);
      printf("MYADDR:  [%s]  [%s]\n", sim_simaddr2str(&rs->myAddr[0]),
	     sim_simaddr2str(&rs->myAddr[1]));
      printf("INPORT:  %d [%s]  %d [%s]\n", rs->inPortNets[0]->id, 
	     sim_addr2str((void *)&rs->inPortNets[0]->net, TYPE_IP),
	     rs->inPortNets[1]->id,
	     sim_addr2str((void *)&rs->inPortNets[1]->net, TYPE_IP));
      printf("OUTPORT: %d  %d\n", rs->outPortNets[0]->id, 
	     rs->outPortNets[1]->id);
      for (i=0; i<rs->numRoutes; i++) {
	re = rs->Routes+i;
	printf("route %d: net=[%s], outPort=%d, hop=[%s]\n", i, 
	       sim_addr2str((void *)&re->net, TYPE_IP), re->outPort,
	       sim_simaddr2str(&re->hop));
      }
    }
  }

}
