# $Id: parsefancy.rb,v 1.4 2005/06/29 20:57:12 vlietstra Exp $

# parsefancy module.
# een funksie wat van die buitekant af gebruik moet word, parsefancy(string)
# 
# Hierdie funksie wat 'n string as input en return 'n string wat geformat
# is volgens ANSI controls codes volgens sekere van ons eie, meer leesbare 
# control 'codes'. Kyk na testparsefancy.rb in die base dir vir hans-on
# examples
#  
# Alle control codes is enclosed in [x] (die delimiters op die oomblik
# Control strings is geseperate deur spaces
# Spesiale control string is die vir kleure, waar fg:bg
# met fg die foreground color, en bg die background color
# gebruik word. Dit is moontlik om :bg te specify, wat net die
# background stel, of fg: wat net die foreground stel
# 'n spesiale case word gehandle waar jy net kan se fg vir foreground color
# ook
#
# [] reset ook
# i.e. "[red:green]Rooi op groen[]Gewone text[:swart]swart agtergrond
# [underscore red:green]Rooi op groen, underscore
# etc.

$delimiter = ['<','>']

$codesmap = {
         # Foreground colors
         'black' => '30',
         'red' => '31',
         'green' => '32',
         'yellow' => '33',
         'blue' => '34',
         'magenta' => '35',
         'cyan' => '36',
         'white' => '37',
         # Background colors
         'BGblack' => '40',
         'BGred' => '41',
         'BGgreen' => '42',
         'BGyellow' => '43',
         'BGblue' => '44',
         'BGmagenta' => '45',
         'BGcyan' => '46',
         'BGwhite' => '47',
         # General codes
         'reset' => '0',
         'bright' => '1',
         'dim' => '2',
         'underscore' => '4',
         'blink' => '5',
         'reverse' => '7',
         'hidden' => '8',
         }

def parse_codes(str)
    codestring = ""
    if str == ''
        codelist = [$codesmap['reset']]
    else
        codelist = []
        
        # get all the space seperated codes
        codes = str.split(' ')
        for code in codes
            # If the code has a : in it, it means it is a
            # color. fg:bg (either can be empty)
            if code.index(':') != nil
                colors = code.split(':')
                key = '%s' % colors[0]
                #foreground may be empty, then we skip it
                if key != ""
                    codelist.push($codesmap[key])
                end
                key = 'BG%s' % colors[1]
                #backghround may be empty, then we skip it
                if key != "BG"
                    codelist.push($codesmap[key])
                end
            else
                codelist.push($codesmap[code])
            end
        end
    end
    # append the codes with spaces in between
    codelist.each { |c| codestring += "%s;" % c }
    # remove the last space
    codestring = codestring[0,codestring.length-1]
    return ("%c" % (27)) + "[%sm" % (codestring)
end

def color(str)
    newstr = ""
    i = 0
    while i < str.length
        c = "%c" % (str[i])
        
        if c == $delimiter[0]
           # if this is the last character, raise an exception
            if i == str.length-1
                raise TypeError, "Open delimiter at end of string."
            end
            
            # if the next character is an open bracket, add it
            if ( ("%c" % (str[i+1])) == $delimiter[0])
                newstr += $delimiter[0]
                i+=1
            else
                # it's some other character, we need to find the first
                # close delimiter.
                k=i+1
                while k < str.length
                    # if at any stage we find another open delimiter
                    # inside the previous delimiter, we raise an
                    # exception
                    if ( ("%c" % str[k] == $delimiter[0]) )
                        raise TypeError, "Open delimiter before close delimiter after open delimiter."+"\n#{str}"
                    end
                    if ( ("%c" % str[k] == $delimiter[1]) )
                        break
                    end
                    k+=1
                end
                # if we reached the end of the string, it means
                # there is no close delimiter, raise an exception
                if k == str.length
                    raise TypeError, "No matching close delimiter"
                end
                
                # commandstr will be all the characters we
                # found inside the brackets
                commandstr = str[i+1...k]
                
                newstr += parse_codes(commandstr)
                
                # update our string index to skip over all the characters
                # we have just assigned to commandstr
                i+=commandstr.length+2 # it's +2 to skip over the [ and ]
            end
                
        else
            newstr += c
            i+=1
        end
        
        
    end
        
    return newstr + ("%c[0m" % (27))
end