/*
 *    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.traverse.standard;

import java.util.Iterator;

import org.geotools.graph.structure.Graph;
import org.geotools.graph.structure.Graphable;
import org.geotools.graph.traverse.GraphTraversal;
import org.geotools.graph.traverse.basic.SourceGraphIterator;
import org.geotools.graph.util.FIFOQueue;
import org.geotools.graph.util.Queue;

/**
 * Iterates over the nodes of a graph in a <B>Breadth First Search</B> pattern 
 * starting from a specified node. The following illustrates the iteration 
 * order. <BR>
 * <BR>
 * <IMG src="doc-files/bfs.gif"/><BR>
 * <BR>
 * The iteration operates by maintaning a node queue of <B>active</B> nodes.  
 * An <B>active</B> node is a node that will returned at a later stage of the i
 * teration. The node queue for a Breadth First iteration is implemented as a 
 * <B>First In First Out</B> queue.
 * A node is placed in the the node queue if it has not been visited, and 
 * it is adjacent to a a node that has been visited. The node queue intially 
 * contains only the source node of the traversal.
 * 
 * @author Justin Deoliveira, Refractions Research Inc, jdeolive@refractions.net
 *
 * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/extension/graph/src/main/java/org/geotools/graph/traverse/standard/BreadthFirstIterator.java $
 */
public class BreadthFirstIterator extends SourceGraphIterator {

  /** Contains all nodes to be returned **/
  private Queue m_active;
  
  /**
   * Sets the source of the traversal and places it in the node queue. The first 
   * call to this method will result in the internal node queue being built. 
   * Subsequent calls to the method clear the node queue and reset the iteration. 
   * 
   * @see SourceGraphIterator#setSource(Graphable)
   */
  public void setSource(Graphable source) {
    super.setSource(source);
    
    //set source of traversal, creating queue if necessary
    if (m_active == null) m_active = buildQueue(getGraph());
    else if (m_active.isEmpty()) m_active.clear();
     
    m_active.enq(getSource());
  }

  /**
   * Does nothing. 
   * 
   * @see org.geotools.graph.traverse.GraphIterator#init(Graph)
   */
  public void init(Graph graph, GraphTraversal traversal) { 
    //do nothing
  }
  
  /**
   * Returns the next node from the node queue that has not yet been visited. It
   * is possible for the node queue to contain duplicate entries. To prevent
   * the iteration returning the same node multiple times, the visited flag is
   * checked on nodes coming out of the queue. If the flag is set, the node 
   * is ignored, not returned, and the next node in the queue is returned. This
   * is however tranparent to the caller.
   * 
   * @see org.geotools.graph.traverse.GraphIterator#next()
   */
  public Graphable next(GraphTraversal traversal) {
    while(!m_active.isEmpty()) {
      Graphable next = (Graphable)m_active.deq();
      if (!traversal.isVisited(next)) return(next);  
    }
    return(null);
  }

  /**
   * Looks for nodes adjacent to the current node to place into the node queue. 
   * An adjacent node is only placed into the node queue if its visited flag
   * is unset.
   * 
   * @see org.geotools.graph.traverse.GraphIterator#cont(Graphable)
   */
  public void cont(Graphable current, GraphTraversal traversal) {
    for (Iterator itr = current.getRelated(); itr.hasNext();) {
      Graphable related = (Graphable)itr.next();
      if (!traversal.isVisited(related)) {
        m_active.enq(related);  
      }      
    }
  }

  /**
   * Kills the current branch by not looking for any adjacent nodes to place
   * into the node queue.
   * 
   * @see org.geotools.graph.traverse.GraphIterator#killBranch(Graphable)
   */
  public void killBranch(Graphable current, GraphTraversal traversal) {
    //do not look for any adjacent nodes to place into the active queue
  }
  
  /**
   * Builds the node queue for the iteration.
   * 
   * @param graph The graph being iterated over.
   *
   * @return A First In First Out queue.
   */
  protected Queue buildQueue(Graph graph) {
    return(new FIFOQueue(graph.getNodes().size()));
  }
  
  /**
   * Returns the node queue.
   * 
   * @return The node queue.
   */
  protected Queue getQueue() {
    return(m_active);  
  }
  
}
