#include <protocol/protocol.h>
#include <elib/list.h>
#include <elib/table.h>

/* this code does not support the notion
   of many IP hosts being serviced by a router
   connected through a single vc, but this would
   be the right place to put such support*/

typedef struct ipvc {
  table vcs;
  layer (*new_vc_function)();
  void *arg;
  layer l;
  int timeout;
} *ipvc;

typedef struct vc {
  unsigned int ip;
  unsigned int idle;
  layer l;
  ipvc i;
  list q;
  int connected;
} *vc;

static int compare (vc a,unsigned int b)
{
  return(a->ip==b);
}

static int dump_thing(pbuffer b,layer l)
{
  int len=buffer_length(b);
  pbuffer d=allocate_downwards_buffer(l,len);

  buffer_copy(d,0,b,0,len);
  write_down(l,d,0);
  dereference_buffer(d);
  dereference_buffer(b);
  return(0);
}

static int shutdown_vc(vc c,int send_down)
{
  table_remove(c->i->vcs,c->ip);
  if (send_down) write_down(c->l,0,PROTOCOL_STACK_SHUTDOWN);
  /*repeated kmem_free?*/
  /*  deallocate_memory(c);*/
  return(0);
}

static void up(layer l, pbuffer b, int f)
{
  vc v=(vc)(l->state); 

  if (f & PROTOCOL_STACK_SHUTDOWN) {
    printf ("ipvc shutting down %d.%d.%d.%d\n",v->ip>>24,(v->ip>>16)&255,
	    (v->ip>>8)&255,v->ip&255);
    shutdown_vc(v,0);
  } else {
    if (f & PROTOCOL_CONNECTED) foreach(&v->q,dump_thing,v->l);
    if (v->idle) v->idle=1;
    write_up(v->i->l,b,f);
  }
}

static void timeout_each_vc(vc v,int timeout)
{
  if (v->idle){
    if (v->idle++ > timeout){
      printf ("ipvc timeout %d.%d.%d.%d\n",v->ip>>24,(v->ip>>16)&255,
	      (v->ip>>8)&255,v->ip&255);
      shutdown_vc(v,1);
    }
  }
}

int ip_vc_timer(layer l)
{
  ipvc i=(ipvc)l->state;
  iterate_table_entries(i->vcs,timeout_each_vc,i->timeout);
}

layer add_ip_vc(layer l,unsigned int a,int permanent)
{
  ipvc i=(ipvc)(l->state); 
  vc new=(vc)allocate_memory(sizeof(struct vc));
  new->ip=a;
  new->i=i;
  new->q=0;
  new->idle=permanent?0:1;
  new->connected=0;
  table_insert(i->vcs,new,a);
  return(new->l=create_layer(new,up,up,0));
}

static void down(layer l,pbuffer b,int f)
{
  pbuffer d;
  ipvc i=(ipvc)(l->state);
  unsigned int dest,source;
  int len;
  vc v;
  layer vcl;

  if (b){
    buffer_read(b,16,&dest,4);
    len=buffer_length(b);
    if (v=table_find(i->vcs,dest)){
      d=allocate_downwards_buffer(v->l,len);
      buffer_copy(d,0,b,0,len);
      write_down(v->l,d,f);
      if (v->idle) v->idle=1;
      dereference_buffer(d);
    } else {
      buffer_read(b,12,&source,4);
      (*i->new_vc_function)(vcl=add_ip_vc(l,dest,0),dest,source,i->arg);
      v=(vc)(vcl->state);
      v->q=insert(b,v->q);
      reference_buffer(b);
    }
  } if (f&PROTOCOL_STACK_SHUTDOWN){
    iterate_table_entries(i->vcs,shutdown_vc,1);
    deallocate_memory(i);
  }
}

     

layer create_ip_vc(layer (*new_vc_function)(),void *arg,int timeout)
{
  ipvc i=(ipvc)allocate_memory(sizeof(struct ipvc));
  i->vcs=create_table(compare,key_from_int);
  i->new_vc_function=new_vc_function;
  i->arg=arg;
  i->timeout=timeout;
  return(i->l=create_layer(i,down,0,0));
}

