class FormatLine
  EOL    = :eol
  Alpha  = /[a-z]/
  Alpha2 = /[a-z0-9_]/
  Other  = Object

  SimpleFormats     = {}
  SimpleFormats[:b] = %w[<b> </b>]
  SimpleFormats[:i] = %w[<i> </i>]
  SimpleFormats[:t] = %w[<tt> </tt>]
  SimpleFormats[:s] = %w[<strike> </strike>]

  def initialize
    @buffer, @vname, @fname, @param, @substr = "", "", "", "", ""
  end

  def peek
    @enum.peek
  rescue StopIteration
    EOL
  end

  def grab
    @enum.next
  rescue StopIteration
    EOL
  end

  def skip
    @enum.next
    @enum.peek
  rescue StopIteration
    EOL
  end

  def keep(initial = "")
    @buffer << initial
    @buffer << @enum.next
  rescue StopIteration
    EOL
  end

  def emit(str = "")
    @buffer << str
  end

  def funcall(name, param)
    fobj = ::Livetext::Functions.new
    ::Livetext::Functions.param = param
# STDERR.puts "Calling '#{name}' with '#{param}'"
    fobj.send(name)
  end

  def vsub
    @buffer << Livetext::Vars[@vname]
    @vname = ""
  end

  def fcall
#   puts "Calling funcall: #@name(#@param)"
    @buffer << funcall(@fname, @param)
    @fname, @param = "", ""
  end

  def bold
    d0, d1 = SimpleFormats[:b]
    @buffer << "#{d0}#@substr#{d1}"
    @substr = ""
  end

  def ttype
    d0, d1 = SimpleFormats[:t]
    @buffer << "#{d0}#@substr#{d1}"
    @substr = ""
  end

  def italics
    d0, d1 = SimpleFormats[:i]
    @buffer << "#{d0}#@substr#{d1}"
    @substr = ""
  end

  def strike
    d0, d1 = SimpleFormats[:s]
    @buffer << "#{d0}#@substr#{d1}"
    @substr = ""
  end

  def parse(line)
    @enum = line.chomp.each_char
    @buffer = ""
    @substr = ""
    @fname  = ""
    @vname  = ""
    @param  = ""
    
    loop do   # starting state
      char = peek
  #   puts "char = #{char.inspect}"
      case char
        when "\\"
          char = skip
          case char
            when "$", "*", "_", "`", "~"
              emit(char)
              skip
            when " "
              emit("\\ ")
              skip
            when EOL
              emit("\\")
              break
            when Other
              emit("\\")   # logic??
          end
        when EOL
          break
        when "$"        # var or func or $
          case skip
            when EOL
              emit("$")
              break
            when Alpha
              loop { ch = peek; break if ch == EOL; @vname << grab; break unless Alpha2 === peek } 
              vsub
            when "$" 
              case skip
                when EOL
                  emit("$$")
                  break
                when Alpha
                  loop { ch = peek; break if ch == EOL; @fname << grab; break unless Alpha2 === peek }
                  case peek
                    when " "   # no param - just call
                      @param = nil
                      fcall    # no param? Hmm
                    when "["   # long param
                      skip
                      loop do 
                        if peek == "\\"
                          skip
                          @param << grab
                        end
                        break if ["]", EOL].include?(peek)
                        @param << grab
                      end
                      skip
                      fcall
                    when ":"   # param (single token or to-eol)
                      case skip
                        when ":"   # param to eol
                          skip
                          loop { break if peek == EOL; @param << grab }
                        when Other # grab until space or eol
                          loop { @param << grab; break if [" ", EOL].include?(peek) }
                          fcall
                      end
                    when Other # no param - just call
                      fcall
                  end
                when Other
                  emit "$$"
              end
            when Other
              emit "$"
          end
        when "*"
          case skip
            when EOL
              emit "*"
            when " "
              emit "*"
            when "["
              skip
              loop do
                if peek == "\\"
                  skip
                  @substr << grab
                  next
                end
                break if ["]", EOL].include?(peek)
                @substr << grab
              end
              skip
              bold
            when Other
              loop { @substr << grab; break if [" ", EOL].include?(peek) }
              bold
          end
        when "_"
          case skip
            when EOL
              emit "_"
            when " "
              emit "_"
            when "["
              skip
              loop do
                if peek == "\\"
                  skip
                  @substr << grab
                  next
                end
                break if ["]", EOL].include?(peek)
                @substr << grab
              end
              skip
              italics
            when "_"   # doubled...
              skip
              loop do
                if peek == "\\"
                  skip
                  @substr << grab
                  next
                end
                break if [".", ",", ")", EOL].include?(peek)
                @substr << grab
              end
              italics
            when Other
              loop { @substr << grab; break if [" ", EOL].include?(peek) }
              italics
          end
        when "`"
          case skip
            when EOL
              emit "`"
            when " "
              emit "`"
            when "["
              skip
              loop do
                if peek == "\\"
                  skip
                  @substr << grab
                  next
                end
                break if ["]", EOL].include?(peek)
                @substr << grab
              end
              skip
              ttype
            when "`"   # doubled...
              skip
              loop { break if [".", ",", ")", EOL].include?(peek); @substr << grab }   # ";" ?? FIXME
              ttype
            when Other
              loop { @substr << grab; break if [" ", EOL].include?(peek) }
              ttype
          end
        when "~"
          case skip
            when EOL
              emit "~"
            when " "
              emit "~"
            when "["
              skip
              loop do
                if peek == "\\"
                  skip
                  @substr << grab
                  next
                end
                break if ["]", EOL].include?(peek)
                @substr << grab
              end
              skip
              strike
            when "~"   # doubled...
              skip
              loop { break if [".", ",", ")", EOL].include?(peek); @substr << grab }   # ";" ?? FIXME
              strike
            when Other
              loop { @substr << grab; break if [" ", EOL].include?(peek) }
              strike
          end
        when Other
          keep
      end
    end

    @buffer
  end
end