/*
 * Copyright (c) 2003-2011, Franz-Josef Elmer, All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 * 
 * - Redistributions of source code must retain the above copyright notice, 
 *   this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright notice, 
 *   this list of conditions and the following disclaimer in the documentation 
 *   and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */
package classycle.dependency;

import java.util.HashSet;
import java.util.Set;

import classycle.graph.NameAttributes;
import classycle.graph.Vertex;
import classycle.graph.VertexCondition;
import classycle.util.StringPattern;

/**
 * @author  Franz-Josef Elmer
 */
public class DependencyPathsRenderer
{
  private static final String INDENT = "  ";
  private final Vertex[] _graph;
  private final VertexCondition _startSetCondition;
  private final VertexCondition _finalSetCondition;
  private final Set<Vertex> _vertices = new HashSet<Vertex>();
  
  public DependencyPathsRenderer(Vertex[] graph, 
                                 StringPattern startSetPattern, 
                                 StringPattern finalSetPattern)
  {
    this(graph, new PatternVertexCondition(startSetPattern), 
                new PatternVertexCondition(finalSetPattern));
  }
  
  public DependencyPathsRenderer(Vertex[] graph, 
                                 VertexCondition startSetCondition,
                                 VertexCondition finalSetCondition)
  {
    _graph = graph;
    _startSetCondition = startSetCondition;
    _finalSetCondition = finalSetCondition;
    for (int i = 0; i < graph.length; i++)
    {
      _vertices.add(graph[i]);
    }
  }
  
  public String renderGraph(final String lineStart)
  {
    final StringBuffer buffer = new StringBuffer();
    DependencyPathRenderer renderer = new DependencyPathRenderer()
      {
        String _start = '\n' + lineStart;
        private int _indentation;
    
        public void increaseIndentation()
        {
          _indentation++;
        }
    
        public void add(String nodeName)
        {
          buffer.append(_start);
          for (int i = 0; i < _indentation; i++)
          {
            buffer.append(INDENT);
          }
          if (_indentation > 0)
          {
            buffer.append("-> ");
          }
          buffer.append(nodeName);
        }
        
        public void decreaseIndentation()
        {
          _indentation--;
        }
    
      };
    renderGraph(renderer);
    
    return new String(buffer);
  }
  
  public void renderGraph(DependencyPathRenderer renderer)
  {
    Set<Vertex> visitedVertices = new HashSet<Vertex>();
    for (int i = 0; i < _graph.length; i++)
    {
      Vertex vertex = _graph[i];
      if (_startSetCondition.isFulfilled(vertex))
      {
        renderer.add(getNameOf(vertex));
        renderPaths(renderer, vertex, visitedVertices);
      }
    }
  }

  private void renderPaths(DependencyPathRenderer renderer, Vertex vertex, 
                           Set<Vertex> visitedVertices)
  {
    visitedVertices.add(vertex);
    renderer.increaseIndentation();
    for (int i = 0, n = vertex.getNumberOfOutgoingArcs(); i < n; i++)
    {
      Vertex headVertex = vertex.getHeadVertex(i);
      if (_vertices.contains(headVertex)
          && !_startSetCondition.isFulfilled(headVertex))
      {
        renderer.add(getNameOf(headVertex));
        if (!_finalSetCondition.isFulfilled(headVertex) 
            && !visitedVertices.contains(headVertex))
        {
          renderPaths(renderer, headVertex, visitedVertices);
        }
      }
    }
    renderer.decreaseIndentation();
  }

  private String getNameOf(Vertex vertex)
  {
    return ((NameAttributes) vertex.getAttributes()).getName();
  }
}
