/*
 * Graph representation of network.
 */

#include <linux/kernel.h>
#include <linux/slab.h>
#include <asm/string.h>

#include "linklist.h"
#include "graph.h"
#include "pqueue.h"
#include "spf.h"

/* -----------------------------------------------------------
 * Node functions
 * -----------------------------------------------------------
 */

struct node *
node_new(__u32 id) 
{
  struct node *r;
  
  /* Make new router node */
  r = kmalloc (sizeof (struct node), GFP_ATOMIC);
  memset(r, 0, sizeof (struct node));

  r->id = id;

  /* Initialize link list */
  r->links = list_new();

  return r;
}


void node_free(struct node *n)
{
  struct listnode *ln;
#ifndef DSR_DEBUG_NO_REQ_TIMER
  /* delete the timer */
  del_timer(&(n->state.timer));
#endif    
  /* Free all link objects */
  for (ln = listhead(n->links); ln; nextnode (ln)) {
    struct link *l = getdata(ln);

    link_free (l);
  }

  list_delete (n->links);

  /* Free node object */
  kfree(n);
}


void node_print(struct node *n)
{
  struct listnode *ln;
  
  printk(KERN_INFO "node id: %u.%u.%u.%u # links: %d\n",
         NIPQUAD(n->id), 
	 listcount(n->links));

  for (ln = listhead(n->links); ln; nextnode(ln)) {
    struct link *link = getdata(ln);

    link_print(link);
    printk(KERN_INFO "\n");
  } 

}

void node_printsimple (struct node *n)
{
  printk(KERN_INFO "[%u.%u.%u.%u]", NIPQUAD(n->id));
}

/* -----------------------------------------------------------
 * Link functions
 * -----------------------------------------------------------
 */

/* Make a new link object that connects two nodes and add link to graph */
struct link *link_new(struct node *from, struct node *to, unsigned int metric)
{
  struct link *l;

  /* Make new directed link */
  l = kmalloc (sizeof (struct link), GFP_ATOMIC);
  memset(l, 0, sizeof (struct link));

  l->metric = metric;

  l->to = to;

  /* Connect "from" node and "to" node */
  listnode_add(from->links, l);
  
  return l;
}

void link_free(struct link *l)
{
  /* Free link object */
  kfree(l);
}

/* Get the link that is pointing from src to dst */
struct link *link_get_node(struct node *src, struct node * dst)
{
  struct listnode *ln;

  for (ln = listhead(src->links); ln; nextnode(ln)) {
    struct link *l = getdata(ln);

    if (l->to == dst) {
      return l;
    }
  }
  return NULL;
}


/* Get the link that is pointing to a node with the given link_id */
struct link *link_get_addr(struct node *n, __u32 id)
{
  struct listnode *ln;

  for (ln = listhead(n->links); ln; nextnode(ln)) {
    struct link *l = getdata(ln);

    if (l->to->id == id) {
      return l;
    }
  }
  return NULL;
}


void link_print(struct link *l)
{
  printk(KERN_INFO " link metric: %d to: %u.%u.%u.%u ", l->metric, NIPQUAD(l->to->id));
}


/* -----------------------------------------------------------
 * Graph functions
 * -----------------------------------------------------------
 */

struct graph *graph_new() {
  struct graph *g;
  
  g = kmalloc (sizeof (struct graph), GFP_ATOMIC);
  memset(g, 0, sizeof (struct graph));

  g->nodes = list_new();
  g->links = list_new();

  /* priority queue for SPF algorithm */
  g->pq = pqueue_new();

  g->lock = SPIN_LOCK_UNLOCKED;
  g->refresh = 0;
//  g->lock = RW_LOCK_UNLOCKED;
  return g;
}

void graph_free(struct graph *g) 
{  
  struct listnode *ln;

  /* free all node objects in graph */
  for (ln = listhead(g->nodes); ln; nextnode(ln)) {
    struct node *n = getdata(ln);

    node_free(n);
  }

#ifdef NOMORE
  /* free all link objects in graph */
  for (ln = listhead(g->links); ln; nextnode(ln)) {
    struct link *l = getdata(ln);

    link_free(l);
  }
#endif

  /* free node and link lists */
  list_delete(g->nodes); 
  list_delete(g->links);
  
  /* Free priority queue */
  pqueue_free(g->pq);
  
  kfree(g);
}

#if 0
void graph_print(struct graph *g) {
  struct listnode *ln;

  printk(KERN_INFO "Graph #nodes: %d #links: %d\n", 
	 listcount(g->nodes), listcount(g->links));

  for (ln = listhead(g->nodes); ln; nextnode(ln)) {
    struct node *node = getdata(ln);

    node_print(node);
  }
}
#endif

void graph_add_node(struct graph *g, struct node *n) 
{
  struct node *n2; 

  /* First check whether this node is already in graph to prevent duplicates */
  n2 = node_get(g, n->id);
  if (n2) {
//      read_lock_irqsave(@(g->lock), g->lock_flags);
      printk(KERN_INFO "  graph_add_node: node %u.%u.%u.%u already in graph\n", NIPQUAD(n->id));
      return;
//      read_unlock_irqrestore(@(g->lock), g->lock_flags);
  }

  /* Add node to graph */
//  write_lock_irqsave(@(g->lock), g->lock_flags);
  listnode_add(g->nodes, n);
//  write_unlock_irqrestore(@(g->lock), g->lock_flags);
}

void graph_add_node_nocheck(struct graph *g, struct node *n) 
{
    /* Add node to graph */
//    write_lock_irqsave(@(g->lock), g->lock_flags);
    listnode_add(g->nodes, n);
//    write_unlock_irqrestore(@(g->lock), g->lock_flags);
}

#if 0
void graph_add_link(struct graph *g, struct link *l)
{

#ifdef NOTYET
  /* First check whether link is already in graph to prevent duplicates */
  struct listnode *ln;

  for (ln = listhead(g->links); ln; nextnode(ln)) {
    struct link *l2 = getdata(ln);
    
    if (l->to. == NODE_NETWORK
	&& n->id == id) {
      return (struct network_node *)n;
    }
  }
#endif /* NOTYET */

  /* Add link to graph */
  listnode_add(g->links, l);
}
#endif


/* Lookup the given node matching the id
   in the graph otherwise return NULL */
struct node * node_get(struct graph *g, __u32 id)
{
/* TODO: bad implementation. we should use something like a hashtable here 
 * since this function is called quite often */
  struct listnode *ln;

//  read_lock_irqsave(@(g->lock), g->lock_flags);
  for (ln = listhead(g->nodes); ln; nextnode(ln)) {
    struct node *n = getdata(ln);
    
    if (n->id == id) {
//	read_unlock_irqrestore(@(g->lock), g->lock_flags);
	return n;
    }
  }

  /* node not found in graph */
//  read_unlock_irqrestore(@(g->lock), g->lock_flags);
  return NULL;
}
