/*
 *    GeoTools - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) 2002-2006, GeoTools Project Managment Committee (PMC)
 *    (C) 2002, Refractions Reserach Inc.
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation;
 *    version 2.1 of the License.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 */
package org.geotools.graph.path;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import org.geotools.graph.structure.GraphVisitor;
import org.geotools.graph.structure.Graphable;
import org.geotools.graph.structure.Node;
import org.geotools.graph.util.IndexedStack;

public class ExhaustivePathFinder {
  public final static int CONTINUE_PATH = 0;
  public final static int END_PATH_AND_CONTINUE = 1;
  public final static int END_PATH_AND_STOP = 2;
  public final static int KILL_PATH = 3;
  
  private int m_maxitr;
  private int m_maxplen;
  
  public ExhaustivePathFinder() {
    this(Integer.MAX_VALUE, Integer.MAX_VALUE);     
  }
  
  public ExhaustivePathFinder(int maxitr, int maxplen) {
    m_maxitr = maxitr;
    m_maxplen = maxplen;
  }
  
  public Path getPath(Node from, Node to) {
     	final Node dst = to;
     	GraphVisitor visitor = new GraphVisitor() {
     	  public int visit(Graphable component) {
     		if (component.equals(dst)) return(END_PATH_AND_STOP);
     		return(CONTINUE_PATH);
     	  }
     	};
     	List paths = getPaths(from, visitor);
     	if (paths.isEmpty()) return(null);
     	return((Path)paths.get(0));
  }
  
  public List getPaths(Node from, Node to) {
  	final Node dst = to;
    GraphVisitor visitor = new GraphVisitor() {
      public int visit(Graphable component) {
	    if (component.equals(dst)) return(END_PATH_AND_CONTINUE);
	    return(CONTINUE_PATH);
	  }
    };
    return(getPaths(from, visitor));
  }
  
  public List getPaths(Node from, GraphVisitor visitor) {
    ArrayList paths = new ArrayList();
    
    //create a map to maintain iterator state
    HashMap node2related = new HashMap();
    
    //create the stack and place start node on
    IndexedStack stack = new IndexedStack();
    stack.push(from);
    
    int iterations = 0;
 O: while(!stack.isEmpty() && (iterations++ < m_maxitr)) {
      //peek the stack
      Node top = (Node)stack.peek();
      
      switch(visitor.visit(top)) {
        case END_PATH_AND_CONTINUE:
          paths.add(new Path(stack));
          stack.pop();
          continue;
        
        case END_PATH_AND_STOP:
          paths.add(new Path(stack));
          break O;
        
        case KILL_PATH:
          stack.pop();
          continue;
          
        case CONTINUE_PATH:
        	
        
      }
      
      Iterator related = null;
      if ((related = (Iterator)node2related.get(top)) == null) {
        related = top.getRelated();
        node2related.put(top,related); 
      } 
      
      while(stack.size() < m_maxplen && related.hasNext()) {
        Node adj = (Node)related.next();
        if (stack.contains(adj)) continue;
        
        //push adjacent onto stack, and reset iterator
        stack.push(adj);
        node2related.put(adj, adj.getRelated());
      
        continue O;
      }
        
      //all adjacent have been processed or are in stack
      stack.pop();
    }   

    return(paths);
  }
}