require 'vedeu/configuration/configuration'
require 'vedeu/output/esc'
require 'vedeu/support/log'

module Vedeu

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

    extend self

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

      if raw_mode?
        Vedeu.log(type: :info, message: "Terminal entering 'raw' mode")

        console.raw    { initialize_screen { yield } }

      else
        Vedeu.log(type: :info, message: "Terminal entering 'cooked' mode")

        console.cooked { initialize_screen { 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
      keys_or_cmd = if raw_mode?
                      keys = console.getch

                      if keys.ord == 27
                        keys << console.read_nonblock(3) rescue nil
                        keys << console.read_nonblock(2) rescue nil
                      end
                      keys

                    else
                      console.gets.chomp

                    end

      Vedeu.trigger(:tick, Time.now.to_f)

      keys_or_cmd
    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 do |stream|
        # Write the stream to the log file.
        # Vedeu.log(Esc.escape(stream))

        console.print(stream)

        # Vedeu::Console.write(stream)
      end

      streams
    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.
    #
    # @return [TrueClass]
    def resize
      Vedeu.trigger(:_clear_)

      Vedeu.trigger(:_refresh_)

      true
    end

    # @param block [Proc]
    # @return [void]
    def initialize_screen(&block)
      output(Esc.string('screen_init'))

      yield
    end

    # Clears the entire terminal space.
    #
    # @return [String]
    def clear
      output(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(Esc.string('screen_exit'), Esc.string('clear_last_line'))
    end

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

    # Returns a boolean indicating whether the terminal is currently in `cooked`
    # mode.
    #
    # @return [Boolean]
    def cooked_mode?
      mode == :cooked
    end

    # Sets the terminal in to `cooked` mode.
    #
    # @return [Symbol]
    def cooked_mode!
      Vedeu.log(type: :info, message: "Terminal switching to 'cooked' mode")

      @mode = :cooked
    end

    # Returns a boolean indicating whether the terminal is currently in `raw`
    # mode.
    #
    # @return [Boolean]
    def raw_mode?
      mode == :raw
    end

    # Sets the terminal in to `raw` mode.
    #
    # @return [Symbol]
    def raw_mode!
      Vedeu.log(type: :info, message: "Terminal switching to 'raw' mode")

      @mode = :raw
    end

    # Toggles the terminal's mode between `cooked` and `raw`, depending on its
    # current mode.
    #
    # @return [Symbol]
    def switch_mode!
      return cooked_mode! if raw_mode?

      raw_mode!
    end

    # Returns the mode of the terminal, either `:raw` or `:cooked`
    #
    # @return [Symbol]
    def mode
      @mode ||= Vedeu::Configuration.terminal_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.first
    end

    # Returns the `x` (column/character) component of the coodinate tuple
    # provided by {Vedeu::Terminal.centre}
    #
    # @return [Fixnum]
    def centre_x
      centre.last
    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
      if Vedeu::Configuration.drb?
        Vedeu::Configuration.drb_width

      else
        size.last

      end
    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
      if Vedeu::Configuration.drb?
        Vedeu::Configuration.drb_height

      else
        size.first

      end
    end
    alias_method :yn, :height
    alias_method :tyn, :height

    # Returns a tuple containing the height and width of the current terminal.
    #
    # @return [Array]
    def size
      console.winsize
    end

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

    # @return [VirtualTerminal]
    def virtual
      @virtual ||= Vedeu::VirtualTerminal.new(height, width)
    end

  end # Terminal

end # Vedeu