require 'puppet'
require 'puppet/parser/ast/branch'
require 'puppet/parser/collector'

# An object that collects stored objects from the central cache and returns
# them to the current host, yo.
class Puppet::Parser::AST
class CollExpr < AST::Branch
    attr_accessor :test1, :test2, :oper, :form, :type, :parens

    # We return an object that does a late-binding evaluation.
    def evaluate(scope)
        # Make sure our contained expressions have all the info they need.
        [@test1, @test2].each do |t|
            if t.is_a?(self.class)
                t.form ||= self.form
                t.type ||= self.type
            end
        end

        # The code is only used for virtual lookups
        str1, code1 = @test1.safeevaluate scope
        str2, code2 = @test2.safeevaluate scope

        # First build up the virtual code.
        # If we're a conjunction operator, then we're calling code.  I did
        # some speed comparisons, and it's at least twice as fast doing these
        # case statements as doing an eval here.
        code = proc do |resource|
            case @oper
            when "and": code1.call(resource) and code2.call(resource)
            when "or": code1.call(resource) or code2.call(resource)
            when "==": resource[str1] == str2
            when "!=": resource[str1] != str2
            end
        end

        # Now build up the rails conditions code
        if self.parens and self.form == :exported
            Puppet.warning "Parentheses are ignored in Rails searches"
        end

        case @oper
        when "and", "or":
            if form == :exported
                raise Puppet::ParseError, "Puppet does not currently support collecting exported resources with more than one condition"
            end
            oper = @oper.upcase
        when "==": oper = "="
        else
            oper = @oper
        end

        if oper == "=" or oper == "!="
            # Add the rails association info where necessary
            if str1 == "title"
                str = "title #{oper} '#{str2}'"
            else
                str = "param_values.value #{oper} '#{str2}' and " +
                    "param_names.name = '#{str1}'"
            end
        else
            str = "(%s) %s (%s)" % [str1, oper, str2]
        end
        
        return str, code
    end

    def initialize(hash = {})
        super

        unless %w{== != and or}.include?(@oper)
            raise ArgumentError, "Invalid operator %s" % @oper
        end
    end
end
end
