/*
 * Shortest-Path First (SPF) algorithm 
 */
#include <linux/kernel.h>

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


/* -----------------------------------------------------------
 * Shortest path algorithm
 * -----------------------------------------------------------
 */

void spf_init(struct graph *g, struct node *s)
{
  struct listnode *ln;

  /* Initialize weights and predecessors */
  for (ln = listhead(g->nodes); ln; nextnode(ln)) {
    struct node *n = getdata(ln);
        
    n->distance = INT_MAX; /* infinity */
    n->pred = NULL;
  }

  /* Adjust distance from "s" to itself */
  s->distance = 0;

  /* Initialize set of visited nodes to empty */
  //g->s = list_new();

  /* (Re-)initialize priority queue. Each time the SPF is run, pqueue
     must be initialized. */
  pqueue_init(g->pq);

  /* Add all nodes to visit */
  for (ln = listhead(g->nodes); ln; nextnode(ln)) {
    struct node *n = getdata(ln);

    pqueue_insert(g->pq, n);
  } 
}

void spf_term ()
{
  /* cleanup */
}


void spf_relax(struct graph *g, struct node *u, 
	       struct node *v, int distance)
{
  /* Relax only if the distances are indeed not infinity */
  if (u->distance < INT_MAX &&
      distance < INT_MAX &&
      v->distance > u->distance + distance) {
    v->distance = u->distance + distance;
    
    /* Correct? XXX */
    pqueue_remove(g->pq, v);
    pqueue_insert(g->pq, v);
    
    v->pred = u;
  }
}

/* Calulate Shortest-Path First algorithm from source "s" */
void spf_calculate(struct graph *g, struct node *s)
{
  /* Initialize */
  spf_init(g, s);

  while (!pqueue_isempty(g->pq)) {
    struct node *u;
    struct listnode *ll;


    u = pqueue_remove_first(g->pq);
    if(!u) {
	printk(KERN_INFO "!!! unable to calculate route!\n)");
	break;
    }


    /* Add "u" to visited set */
    //listnode_add(g->s, u); /* needed? */

    for (ll = listhead(u->links); ll; nextnode(ll)) {
      struct link *l = getdata(ll);
      struct node *v = l->to;

      spf_relax(g, u, v, l->metric);
    }
  }
}

/* Output the shortest path from a node to the source by following the
   predecessors links */
void spf_printpath (struct graph *g, struct node *endnode) {
  struct node *n;

  printk(KERN_INFO " spf_printpath: ");

  n = endnode;
  while (n) {
    //node_print(n);
    node_printsimple(n);
    //printk(KERN_INFO "distance from source: %d\n", n->distance);
    printk(KERN_INFO "(%d) ", n->distance);
    n = n->pred;
  }
  printk(KERN_INFO "\n");
}

/* Print all paths computed */
void spf_printpaths (struct graph *g) {
  struct listnode *ln;
 
  printk(KERN_INFO "--- spf_printpaths:\n");
 
  for (ln = listhead(g->nodes); ln; nextnode(ln)) {
    struct node *n = getdata(ln);
    spf_printpath(g, n);
  }
}
