# = ANSICode
#
# Module which makes it very easy to use ANSI codes.
# These are especially nice for beautifying shell output.
#
#   include ANSICode
#
#   p red, "Hello", blue, "World"
#   => "\e[31mHello\e[34mWorld"
#
#   p red { "Hello" } + blue { "World" }
#   => "\e[31mHello\e[0m\e[34mWorld\e[0m"
#
# == Supported ANSI Comands
#
# 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
#
# == Authors
#
# * Florian Frank
# * Thomas Sawyer
#
# == Speical Thanks
#
# Special thanks to Florian Frank. ANSICode is a partial adaptation
# of ANSIColor, Copyright (c) 2002 Florian Frank, LGPL.
#
# == Todo
#
# * Need to add rest of ANSI codes. Include modes?
# * Re-evaluate how color/yielding methods are defined.
# * Maybe up, down, right, left should have yielding methods too?
#
# == Copying
#
# Copyright (c) 2004 Florian Frank, Thomas Sawyer


# = ANSICode
#
# Module which makes it very easy to use ANSI codes.
# These are esspecially nice for beautifying shell output.
#
#   include ANSICode
#
#   p red, "Hello", blue, "World"
#   => "\e[31mHello\e[34mWorld"
#
#   p red { "Hello" } + blue { "World" }
#   => "\e[31mHello\e[0m\e[34mWorld\e[0m"
#
# == Supported ANSI Comands
#
# 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 ANSICode

  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/

  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

  module_function

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

end

