package network;

import java.util.Vector;
import java.util.Enumeration;

import general.Set;

public class ShortestRoute {
  private Set switches;
  private int nSwitch;
  private Set links;
  private int nLink;

  // data structures used to calculate shortest pathes
  Vector	switchVector;	// Vector representation of switches
  Link[][]	edge;		// matrix of links
  Vector[]	adjs;		// adjacent switches
  double[][]	dist;		// distance matrix
  int[][]	predecessor;	// predecessor matrix

  /** create an instance of ShortestRoute */
  public ShortestRoute(Set Switches, Set Links) {
    this.switches = Switches;
    this.nSwitch = switches.size();
    this.switchVector = switches.getVector();
    this.links = Links;
    this.nLink = links.size();
  }

  /** return distance between two switches, should be called after
   *  apply() is called, so that dist[][] is uptodate.
   */
  public double getDistance(Switch swtA, Switch swtB) {
    int i = switchVector.indexOf(swtA);
    int j = switchVector.indexOf(swtB);
    if ( i == j ) 
      return 0;
    if ( i < j )
      return dist[i][j];
    else
      return dist[j][i];
  }

  /** apply the shortest path route on the network. */
  public void apply() {
    // clear all references of the links
    Link link;
    for( Enumeration e = links.elements(); e.hasMoreElements();) {
      link = (Link)e.nextElement();
      link.refPath.removeAllElements();
      link.refParam.removeAllElements();
    }

    predecessor = new int[nSwitch][nSwitch];
    dist = new double[nSwitch][nSwitch];
    int i, j, index = 0;
    for (i = 0; i < nSwitch; i++)
      for (j = 0; j < nSwitch; j++)
	dist[i][j] = Double.MAX_VALUE;
    
    double min;
    constructAdjacentAndEdge(links);
    for (int src = 0; src < nSwitch; src++) {
      // set the dist[][] and predecessor[][] accordingly
      singleSourceShortestPathes(src);
    }
    for ( i = 0; i < nSwitch - 1; i++)
      for ( j = i + 1; j < nSwitch; j++ )
	setRefPaths(i, j);
  } // end of getAllPairsShortestPathes()

  /**
   * fills "adjs" and "edge" based on set of links: curLink,
   * to be used by Dijkstra's algorithm.
   */
  private void constructAdjacentAndEdge(Set curLinks) {
    int i, l, r;
    Link tmpLink;

    adjs = new Vector[nSwitch];
    edge = new Link[nSwitch][nSwitch];
    for (i = 0; i < nSwitch; i++)
      adjs[i] = new Vector();
    for (Enumeration e = curLinks.elements(); e.hasMoreElements(); ) {
      tmpLink = (Link)e.nextElement();
      l = switchVector.lastIndexOf( tmpLink.getL() );
      r = switchVector.lastIndexOf( tmpLink.getR() );
      
      // every link corresponds to two edges
      edge[l][r] = tmpLink;
      edge[r][l] = tmpLink;
      adjs[l].addElement(new Integer(r));
      adjs[r].addElement(new Integer(l));
    }
  }

  /**
   * get single source shortest pathes.
   * edge[][], dist[][], predecessor[][] should be initialized.
   */
  public void singleSourceShortestPathes(int src) {
    int n = switches.size();
    boolean[] selected = new boolean[n];
    initializeSingleSource(src, selected);
    dist[src][src] = 0;
    double min;
    int i, j, index = 0;
    for (int count = 0; count < n; count++) {
      // extract min dist from the src
      min = Double.MAX_VALUE;
      for (i = 0; i < n; i++) {
	if (selected[i] == false && dist[src][i] < min) {
	  min = dist[src][i];
	  index = i;
	}
      }
      selected[index] = true;
      for (Enumeration e = adjs[index].elements(); e.hasMoreElements();) {
	j = ((Integer)e.nextElement()).intValue();
	// do relax on all switches adjacent to the selected one
	if (edge[index][j] != null &&
	    dist[src][j] > dist[src][index] + edge[index][j].length) {
	  dist[src][j] = dist[src][index] + edge[index][j].length;
	  predecessor[src][j] = index;
	}
      }
    }
  }

  /** mark predecessor from Switch s to all others as -1, and 
   *  all switches as not selected.
   */
  private boolean initializeSingleSource(int s, boolean[] selected) {
    for (int i = 0; i < nSwitch; i++) {
      predecessor[s][i] = -1;
      selected[i] = false;
    }
    return true;
  }

  /**
   * set reference path on links on the path from switch i to switch j
   * every reference is a switch pair.
   */
  private void setRefPaths(int i, int j) {
    SwitchPair sp1, sp2; 
    if ( i == j ) return;
    // a path from sp.l to sp.r will pass the link from left to right
    sp1 = new SwitchPair( (Switch)switchVector.elementAt(i),
			 (Switch)switchVector.elementAt(j) );
    sp2 = new SwitchPair( (Switch)switchVector.elementAt(j),
			 (Switch)switchVector.elementAt(i) );
    int src = i;
    int dst = j;
    int pred;
    Link tmpLink;
    while ( (pred = predecessor[src][dst]) != -1 ) {
      tmpLink = edge[pred][dst];
      if (tmpLink.getR().equals(switchVector.elementAt(dst)))
	tmpLink.refPath.addElement(sp1);
      else
	tmpLink.refPath.addElement(sp2);
      tmpLink.refParam.addElement(new Float(1.0));
      dst = pred;
    }
  } // end of setRefPaths()


} // end of class


