module Amp
  module Graphs
    
    ##
    # = AncestorGenerator
    # A generator class that will allow us to only calculate one ancestor
    # of a node at a time, so we don't have to process the full list of
    # ancestors for each node twice. Our old, lazy way was, if you have two
    # nodes A and B, and need to find the common ancestor, we would generate
    # *all* nodes in both A, and B's history. If the two have a very close
    # common ancestor (usually the case when doing a branch merge in a rapid
    # development environment), then this is a huge amount of wasted processing.
    # Generators aren't a familiar construct for most ruby developers, and they
    # work via continuations, which are also typically avoided like the plague.
    # Check out 'lib/support/generator.rb' to see how it works.
    #
    #    A   B
    #    |   |
    #    |   |
    #    |   |
    #    |___| <-- target ancestor
    #        |
    #        | <-- don't need to generate this node, so we take one at a time
    #
    class AncestorGenerator < Generator
      
      def initialize(vertex, depth, parent_func)
        @vertex, @depth_hash, @parent_func = vertex, depth, parent_func
      end
      
      ##
      # Internal method that, given a vertex, a depth-hash, and a way to 
      # find parents, will yield all the ancestors of a node, in order
      # of depth.
      # 
      # @param vertex the vertex ID of a node in the graph
      # @param [Hash] depth_hash associates a node_id to its depth in
      #   the graph
      # @param [Proc] parent_func a function that calcualtes the parents
      #   of a node
      # @yield every single ancestor in a row, from lowest depth to the
      #   highest
      # @yieldparam [Hash] a hash, with :node pointing to the ID, and :depth
      #   giving the depth of the node
      def traverse_ancestors
        h = PriorityQueue.new
        h[@vertex] = @depth_hash[@vertex]
        seen = {}
        until h.empty?
          node, depth = h.delete_min
          unless seen[node]
            seen[node] = true
            yield({:node => node, :depth => depth})
            @parent_func.call(node).each do |parent|
              h[parent] = @depth_hash[parent]
            end
          end
        end
      end
      
      ##
      # Yields each depth in succession from lowest depth to highest depth,
      # with each node in that depth as a hash.
      # 
      # @param vertex the base vertex to start from
      # @param depth a hash assigning each node to its depth from the vertex
      # @param [Proc] parent_func a proc that gives the parents of a node
      # @yield each generation - a set of vertices that are a given depth
      #   from the node
      # @yieldparam depth the depth that this generation is from the head
      #   vertex provided
      # @yieldparam generation the generation, as a hash assigning entries
      #   in that generation to _true_.
      #
      def generator_loop
        sg, s = nil, {}
        traverse_ancestors do |hash|
          g, v = hash[:depth], hash[:node]
          if g != sg
            yield_gen [sg, s] if sg
            sg, s = g, {v => true}
          else
            s[v] = true
          end
        end
        yield_gen [sg, s]
        nil
      end
    end
    
    class AncestorCalculator
      
      ##
      # Returns the closest common ancestor between A and B, given a method
      # that says how to find the parent of a node.
      # 
      # @param a the first node
      # @param b the second node (order doesn't matter)
      # @param parent_func a way to determine the parents of a node. Should
      #   eventually be made to a block, perhaps.
      # @return the node_id of the least-common ancestor.
      def self.ancestors(a, b, parent_func)
        return a if a == b
        to_visit = [a, b]
        depth = {}
        until to_visit.empty?
          vertex = to_visit.last
          parent_list = parent_func.call(vertex)
          if parent_list.empty?
            depth[vertex] = 0
            to_visit.pop
          else
            parent_list.each do |parent|
              return parent if parent == a || parent == b
              to_visit << parent unless depth[parent]
            end
            if to_visit.last == vertex
              depth[vertex] = parent_list.map {|p| depth[p]}.min - 1
              to_visit.pop
            end
          end
        end
        
        x = AncestorGenerator.new(a, depth, parent_func)
        y = AncestorGenerator.new(b, depth, parent_func)
        
        gx = x.next
        gy = y.next
        
        while gx && gy
          if gx[0] == gy[0]
            gx[1].each do |k,v|
              return k if gy[1].include? k
            end
            gx = x.next
            gy = y.next
          elsif gx[0] > gy[0]
            gy = y.next
          else
            gx = x.next
          end
        end
        return nil
      end
    end
  end
end