module Vedeu

  # This module is the direct interface between Vedeu and your
  # terminal/ console, via Ruby's IO core library.
  #
  module Terminal

    include Vedeu::Terminal::Mode
    extend self

    # Opens a terminal screen in either `raw` or `cooked` mode. On
    # exit, attempts to restore the screen. See
    # {Vedeu::Terminal#restore_screen}.
    #
    # @raise [Vedeu::Error::RequiresBlock] The required block was not
    #   given.
    # @return [Array]
    def open
      fail Vedeu::Error::RequiresBlock unless block_given?

      if raw_mode? || fake_mode?
        console.raw    { initialize_screen(mode) { yield } }

      else
        console.cooked { initialize_screen(mode) { yield } }

      end
    ensure
      restore_screen
    end

    # Takes input from the user via the keyboard. Accepts special keys
    # like the F-Keys etc, by capturing the entire sequence.
    #
    # @return [String]
    def input
      Vedeu.log(type: :input, message: 'Waiting for user input...')

      if raw_mode? || fake_mode?
        Vedeu::Editor::Capture.read(console)

      else
        console.gets.chomp

      end
    end
    alias_method :read, :input

    # Prints the streams to the screen and returns the streams.
    #
    # @param streams [String|Array]
    # @return [Array]
    def output(*streams)
      streams.each { |stream| console.print(stream) }
    end
    alias_method :write, :output

    # When the terminal emit the 'SIGWINCH' signal, Vedeu can
    # intercept this and attempt to redraw the current interface with
    # varying degrees of success. Can also be used to simulate a
    # terminal resize.
    #
    # @example
    #   Vedeu.resize
    #
    # @return [TrueClass]
    def resize
      Vedeu.trigger(:_clear_)

      Vedeu.trigger(:_refresh_)

      true
    end

    # @param mode [Symbol]
    # @return [void]
    def initialize_screen(mode)
      Vedeu.log(type: :info, message: "Terminal entering '#{mode}' mode")

      output(Vedeu::EscapeSequences::Esc.string('screen_init'))

      yield if block_given?
    end

    # Clears the entire terminal space.
    #
    # @example
    #   Vedeu.clear
    #
    # @return [String]
    def clear
      output(Vedeu::EscapeSequences::Esc.string('clear'))
    end

    # Attempts to tidy up the screen just before the application
    # terminates. The cursor is shown, colours are reset to terminal
    # defaults, the terminal is told to reset, and finally we clear
    # the last line ready for the prompt.
    #
    # @return [String]
    def restore_screen
      output(Vedeu::EscapeSequences::Esc.string('screen_exit'))
    end

    # Sets the cursor to be visible unless in raw mode, whereby it
    # will be left hidden.
    #
    # @return [String]
    def set_cursor_mode
      output(Vedeu::EscapeSequences::Esc.string('show_cursor')) unless raw_mode?
    end

    # Returns a coordinate tuple of the format [y, x], where `y` is
    # the row/line and `x` is the column/character.
    #
    # @return [Array]
    def centre
      [(height / 2), (width / 2)]
    end

    # Returns the `y` (row/line) component of the coordinate tuple
    # provided by {Vedeu::Terminal.centre}
    #
    # @return [Fixnum]
    def centre_y
      centre[0]
    end

    # Returns the `x` (column/character) component of the coodinate
    # tuple provided by {Vedeu::Terminal.centre}
    #
    # @return [Fixnum]
    def centre_x
      centre[-1]
    end

    # Returns 1. This 1 is either the top-most or left-most coordinate
    # of the terminal.
    #
    # @return [Fixnum]
    def origin
      1
    end
    alias_method :x, :origin
    alias_method :y, :origin
    alias_method :tx, :origin
    alias_method :ty, :origin

    # Returns the total width (number of columns/characters) of the
    # current terminal.
    #
    # @example
    #   Vedeu.width # => provides the width via the Vedeu API.
    #
    # @return [Fixnum]
    def width
      return Vedeu::Configuration.drb_width if Vedeu::Configuration.drb?
      return Vedeu::Configuration.width     if Vedeu::Configuration.width

      size[-1]
    end
    alias_method :xn, :width
    alias_method :txn, :width

    # Returns the total height (number of rows/lines) of the current
    # terminal.
    #
    # @example
    #   Vedeu.height # => provides the height via the Vedeu API.
    #
    # @return [Fixnum]
    def height
      return Vedeu::Configuration.drb_height if Vedeu::Configuration.drb?
      return Vedeu::Configuration.height     if Vedeu::Configuration.height

      size.first
    end
    alias_method :yn, :height
    alias_method :tyn, :height

    # Returns a tuple containing the height and width of the current
    # terminal.
    #
    # @note
    #   If the terminal is a odd number of characters in height or
    #   width, then 1 is deducted from the dimension to make it even.
    #   For example; the actual terminal is height: 37, width: 145,
    #   then the reported size will be 36, 144 respectively.
    #
    #   This is done to make it easier for client applications to
    #   divide the terminal space up when defining interfaces or
    #   views, leading to more consistent rendering.
    #
    #   If the client application is using the
    #   {Vedeu::Geometry::Grid#rows} or
    #   {Vedeu::Geometry::Grid#columns} helpers, the dimensions are
    #   made more consistent using this approach.
    #
    # @return [Array]
    def size
      h, w = console.winsize

      h = (h.even? ? h : h - 1)
      w = (w.even? ? w : w - 1)

      [h, w]
    end

    # Provides our gateway into the wonderful rainbow-filled world of
    # IO.
    #
    # @return [File]
    def console
      IO.console
    end

  end # Terminal

end # Vedeu