module Puppet; end

module Puppet::Parser
  # The ParserFactory makes selection of parser possible.
  # Currently, it is possible to switch between two different parsers:
  # * classic_parser, the parser in 3.1
  # * eparser, the Expression Based Parser
  #
  class ParserFactory
    # Produces a parser instance for the given environment
    def self.parser(environment)
      if Puppet.future_parser?
        evaluating_parser(environment)
      else
        classic_parser(environment)
      end
    end

    # Creates an instance of the classic parser.
    #
    def self.classic_parser(environment)
      # avoid expensive require if already loaded
      require 'puppet/parser' unless defined? Puppet::Parser::Parser
      Puppet::Parser::Parser.new(environment)
    end

    # Creates an instance of an E4ParserAdapter that adapts an
    # EvaluatingParser to the 3x way of parsing.
    #
    def self.evaluating_parser(file_watcher)
      # Since RGen is optional, test that it is installed
      assert_rgen_installed()
      unless defined?(Puppet::Pops::Parser::E4ParserAdapter)
        require 'puppet/parser/e4_parser_adapter'
        require 'puppet/pops/parser/code_merger'
      end
      E4ParserAdapter.new(file_watcher)
    end

    # Asserts that RGen >= 0.6.6 is installed by checking that certain behavior is available.
    # Note that this assert is expensive as it also requires puppet/pops (if not already loaded).
    #
    def self.assert_rgen_installed
      @@asserted ||= false
      return if @@asserted
      @@asserted = true
      begin
        require 'rgen/metamodel_builder'
      rescue LoadError
        raise Puppet::DevError.new("The gem 'rgen' version >= 0.7.0 is required when using the setting '--parser future'. Please install 'rgen'.")
      end
      # Since RGen is optional, there is nothing specifying its version.
      # It is not installed in any controlled way, so not possible to use gems to check (it may be installed some other way).
      # Instead check that "eContainer, and eContainingFeature" has been installed.
      require 'puppet/pops'
      begin
        litstring = Puppet::Pops::Model::LiteralString.new();
        container = Puppet::Pops::Model::ArithmeticExpression.new();
        container.left_expr = litstring
        raise "no eContainer" if litstring.eContainer() != container
        raise "no eContainingFeature" if litstring.eContainingFeature() != :left_expr
      rescue => e
        # TODO: RGen can raise exceptions for other reasons!
        raise Puppet::DevError.new("The gem 'rgen' version >= 0.7.0 is required when using '--parser future'. An older version is installed, please update.")
      end
    end

    def self.code_merger
      if Puppet.future_parser?
        Puppet::Pops::Parser::CodeMerger.new
      else
        Puppet::Parser::CodeMerger.new
      end
    end
  end
end
