# coding: utf-8

#--
# terminal.rb
#
#  Originally created by James Edward Gray II on 2006-06-14 as
#  system_extensions.rb.
#  Copyright 2006 Gray Productions. All rights reserved.
#
#  This is Free Software.  See LICENSE and COPYING for details.

require "highline/compatibility"

class HighLine
  # Basic Terminal class which HighLine will direct
  # input and output to.
  # The specialized Terminals all decend from this HighLine::Terminal class
  class Terminal
    # Probe for and return a suitable Terminal instance
    # @param input [IO] input stream
    # @param output [IO] output stream
    def self.get_terminal(input, output)
      # First of all, probe for io/console
      begin
        require "io/console"
        require "highline/terminal/io_console"
        terminal = HighLine::Terminal::IOConsole.new(input, output)
      rescue LoadError
        require "highline/terminal/unix_stty"
        terminal = HighLine::Terminal::UnixStty.new(input, output)
      end

      terminal.initialize_system_extensions
      terminal
    end

    # @return [IO] input stream
    attr_reader :input

    # @return [IO] output stream
    attr_reader :output

    # Creates a terminal instance based on given input and output streams.
    # @param input [IO] input stream
    # @param output [IO] output stream
    def initialize(input, output)
      @input  = input
      @output = output
    end

    # An initialization callback.
    # It is called by {.get_terminal}.
    def initialize_system_extensions; end

    # @return [Array<Integer, Integer>] two value terminal
    #   size like [columns, lines]
    def terminal_size
      [80, 24]
    end

    # Enter Raw No Echo mode.
    def raw_no_echo_mode; end

    # Yieds a block to be executed in Raw No Echo mode and
    # then restore the terminal state.
    def raw_no_echo_mode_exec
      raw_no_echo_mode
      yield
    ensure
      restore_mode
    end

    # Restore terminal to its default mode
    def restore_mode; end

    # Get one character from the terminal
    # @return [String] one character
    def get_character; end # rubocop:disable Naming/AccessorMethodName

    # Get one line from the terminal and format accordling.
    # Use readline if question has readline mode set.
    # @param question [HighLine::Question]
    # @param highline [HighLine]
    def get_line(question, highline)
      raw_answer =
        if question.readline
          get_line_with_readline(question, highline)
        else
          get_line_default(highline)
        end

      question.format_answer(raw_answer)
    end

    # Get one line using #readline_read
    # @param (see #get_line)
    def get_line_with_readline(question, highline)
      require "reline" # load only if needed

      raw_answer = readline_read(question, highline)

      if !raw_answer && highline.track_eof?
        raise EOFError, "The input stream is exhausted."
      end

      raw_answer || ""
    end

    # Use readline to read one line
    # @param question [HighLine::Question] question from where to get
    #   autocomplete candidate strings
    def readline_read(question, highline)
      # prep auto-completion
      unless question.selection.empty?
        Reline.completion_proc = lambda do |str|
          question.selection.grep(/\A#{Regexp.escape(str)}/)
        end
      end

      # TODO: Check if this is still needed after Reline
      # work-around ugly readline() warnings
      old_verbose = $VERBOSE
      $VERBOSE    = nil

      raw_answer  = run_preserving_stty do
        prompt = highline.render_and_ident_statement(question)
        Reline.readline(prompt, true)
      end

      $VERBOSE = old_verbose

      raw_answer
    end

    # Get one line from terminal using default #gets method.
    # @param highline (see #get_line)
    def get_line_default(highline)
      raise EOFError, "The input stream is exhausted." if highline.track_eof? &&
                                                          highline.input.eof?
      highline.input.gets
    end

    # @!group Enviroment queries

    # Running on JRuby?
    def jruby?
      defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
    end

    # Running on Rubinius?
    def rubinius?
      defined?(RUBY_ENGINE) && RUBY_ENGINE == "rbx"
    end

    # Running on Windows?
    def windows?
      defined?(RUBY_PLATFORM) && (RUBY_PLATFORM =~ /mswin|mingw|cygwin/)
    end

    # @!endgroup

    # Returns the class name as String. Useful for debuggin.
    # @return [String] class name. Ex: "HighLine::Terminal::IOConsole"
    def character_mode
      self.class.name
    end

    private

    # Yield a block using stty shell commands to preserve the terminal state.
    def run_preserving_stty
      save_stty
      yield
    ensure
      restore_stty
    end

    # Saves terminal state using shell stty command.
    def save_stty
      @stty_save = begin
                     `stty -g`.chomp if input.tty?
                   rescue StandardError
                     nil
                   end
    end

    # Restores terminal state using shell stty command.
    def restore_stty
      system("stty", @stty_save) if @stty_save
    end
  end
end
