class Pry

  # A class to manage the loading of files through the REPL loop.
  # This is an interesting trick as it processes your file as if it
  # was user input in an interactive session. As a result, all Pry
  # commands are available, and they are executed non-interactively. Furthermore
  # the session becomes interactive when the repl loop processes a
  # 'make-interactive' command in the file. The session also becomes
  # interactive when an exception is encountered, enabling you to fix
  # the error before returning to non-interactive processing with the
  # 'make-non-interactive' command.

  class REPLFileLoader
    def initialize(file_name)
      full_name = File.expand_path(file_name)
      raise RuntimeError, "No such file: #{full_name}" if !File.exists?(full_name)

      define_additional_commands
      @content = File.read(full_name)
    end

    # Switch to interactive mode, i.e take input from the user
    # and use the regular print and exception handlers.
    # @param [Pry] _pry_ the Pry instance to make interactive.
    def interactive_mode(_pry_)
      _pry_.config.input = Pry.config.input
      _pry_.config.print = Pry.config.print
      _pry_.config.exception_handler = Pry.config.exception_handler
      Pry::REPL.new(_pry_).start
    end

    # Switch to non-interactive mode. Essentially
    # this means there is no result output
    # and that the session becomes interactive when an exception is encountered.
    # @param [Pry] _pry_ the Pry instance to make non-interactive.
    def non_interactive_mode(_pry_, content)
      _pry_.print = proc {}
      _pry_.exception_handler = proc do |o, e, _p_|
        _p_.run_command "cat --ex"
        o.puts "...exception encountered, going interactive!"
        interactive_mode(_pry_)
      end

      content.lines.each do |line|
        break unless _pry_.eval line, :generated => true
      end

      unless _pry_.eval_string.empty?
        _pry_.output.puts "#{_pry_.eval_string}...exception encountered, going interactive!"
        interactive_mode(_pry_)
      end
    end

    # Define a few extra commands useful for flipping back & forth
    # between interactive/non-interactive modes
    def define_additional_commands
      s = self

      Pry::Commands.command "make-interactive", "Make the session interactive" do
        s.interactive_mode(_pry_)
      end

      Pry::Commands.command "load-file", "Load another file through the repl" do |file_name|
        s.non_interactive_mode(_pry_, File.read(File.expand_path(file_name)))
      end
    end

    # Actually load the file through the REPL by setting file content
    # as the REPL input stream.
    def load
      non_interactive_mode(Pry.new, @content)
    end
  end
end
