# Ansi::Code Copyright (c) 2009 Thomas Sawyer, LGPL
#
# This library is a partial adaptation of ANSIColor by Florian Frank.
#
# ANSIColor Copyright (c) 2002 Florian Frank, LGPL
#
# TODO: Need to add rest of ANSI codes. Include modes?
# TODO: Re-evaluate how color/yielding methods are defined.
# TODO: Maybe up, down, right, left should have yielding methods too?

#
module ANSI

  # Currently Windows is not supported.
  SUPPORTED = !(RUBY_PLATFORM =~ /win/)

  # = ANSI Codes
  #
  # Ansi::Code module makes it very easy to use ANSI codes.
  # These are esspecially nice for beautifying shell output.
  #
  #   include Ansi::Code
  #
  #   p red, "Hello", blue, "World"
  #   => "\e[31mHello\e[34mWorld"
  #
  #   p red { "Hello" } + blue { "World" }
  #   => "\e[31mHello\e[0m\e[34mWorld\e[0m"
  #
  # == Supported ANSI Commands
  #
  # The following is a list of supported codes.
  #
  #     save
  #     restore
  #     clear_screen
  #     cls             # synonym for :clear_screen
  #     clear_line
  #     clr             # synonym for :clear_line
  #     move
  #     up
  #     down
  #     left
  #     right
  #     display
  #
  #     clear
  #     reset           # synonym for :clear
  #     bold
  #     dark
  #     italic          # not widely implemented
  #     underline
  #     underscore      # synonym for :underline
  #     blink
  #     rapid_blink     # not widely implemented
  #     negative        # no reverse because of String#reverse
  #     concealed
  #     strikethrough   # not widely implemented
  #
  #     black
  #     red
  #     green
  #     yellow
  #     blue
  #     magenta
  #     cyan
  #     white
  #
  #     on_black
  #     on_red
  #     on_green
  #     on_yellow
  #     on_blue
  #     on_magenta
  #     on_cyan
  #     on_white
  #
  module Code

    extend self

    # Save current cursor positon.
    def save
      "\e[s"
    end

    # Restore saved cursor positon.
    def restore
      "\e[u"
    end

    # Clear the screen and move cursor to home.
    def clear_screen
      "\e[2J"
    end
    alias_method :cls, :clear_screen

    # Clear to the end of the current line.
    def clear_line
      "\e[K"
    end
    alias_method :clr, :clear_line

    #--
    #def position
    #  "\e[#;#R"
    #end
    #++

    # Move curose to line and column.
    def move( line, column=0 )
      "\e[#{line.to_i};#{column.to_i}H"
    end

    # Move cursor up a specificed number of spaces.
    def up( spaces=1 )
      "\e[#{spaces.to_i}A"
    end

    # Move cursor down a specificed number of spaces.
    def down( spaces=1 )
      "\e[#{spaces.to_i}B"
    end

    # Move cursor left a specificed number of spaces.
    def left( spaces=1 )
      "\e[#{spaces.to_i}D"
    end

    # Move cursor right a specificed number of spaces.
    def right( spaces=1 )
      "\e[#{spaces.to_i}C"
    end

    # Like +move+ but returns to original positon
    # after yielding block or adding string argument.
    def display( line, column=0, string=nil ) #:yield:
      result = "\e[s"
      result << "\e[#{line.to_i};#{column.to_i}H"
      if block_given?
        result << yield
        result << "\e[u"
      elsif string
        result << string
        result << "\e[u"
      elsif respond_to?(:to_str)
        result << self
        result << "\e[u"
      end
      return result
    end

    # Define color codes.
    def self.define_ansicolor_method(name,code)
      class_eval <<-HERE
        def #{name.to_s}(string = nil)
            result = "\e[#{code}m"
            if block_given?
                result << yield
                result << "\e[0m"
            elsif string
                result << string
                result << "\e[0m"
            elsif respond_to?(:to_str)
                result << self
                result << "\e[0m"
            end
            return result
        end
      HERE
    end

    @@colors = [
      [ :clear        ,   0 ],
      [ :reset        ,   0 ],     # synonym for :clear
      [ :bold         ,   1 ],
      [ :dark         ,   2 ],
      [ :italic       ,   3 ],     # not widely implemented
      [ :underline    ,   4 ],
      [ :underscore   ,   4 ],     # synonym for :underline
      [ :blink        ,   5 ],
      [ :rapid_blink  ,   6 ],     # not widely implemented
      [ :negative     ,   7 ],     # no reverse because of String#reverse
      [ :concealed    ,   8 ],
      [ :strikethrough,   9 ],     # not widely implemented
      [ :black        ,  30 ],
      [ :red          ,  31 ],
      [ :green        ,  32 ],
      [ :yellow       ,  33 ],
      [ :blue         ,  34 ],
      [ :magenta      ,  35 ],
      [ :cyan         ,  36 ],
      [ :white        ,  37 ],
      [ :on_black     ,  40 ],
      [ :on_red       ,  41 ],
      [ :on_green     ,  42 ],
      [ :on_yellow    ,  43 ],
      [ :on_blue      ,  44 ],
      [ :on_magenta   ,  45 ],
      [ :on_cyan      ,  46 ],
      [ :on_white     ,  47 ],
    ]

    @@colors.each do |c, v|
      define_ansicolor_method(c, v)
    end

    ColoredRegexp = /\e\[([34][0-7]|[0-9])m/

    module_function

    #
    def uncolored(string = nil)
      if block_given?
        yield.gsub(ColoredRegexp, '')
      elsif string
        string.gsub(ColoredRegexp, '')
      elsif respond_to?(:to_str)
        gsub(ColoredRegexp, '')
      else
        ''
      end
    end

    #
    def colors
      @@colors.map { |c| c[0] }
    end

  end

end