

#  Copyright (c) 2005 Gavin E. Crooks
#
#  This software is distributed under the MIT Open Source License.
#  <http://www.opensource.org/licenses/mit-license.html>
#
#  Permission is hereby granted, free of charge, to any person obtaining a 
#  copy of this software and associated documentation files (the "Software"),
#  to deal in the Software without restriction, including without limitation
#  the rights to use, copy, modify, merge, publish, distribute, sublicense,
#  and/or sell copies of the Software, and to permit persons to whom the
#  Software is furnished to do so, subject to the following conditions:
#
#  The above copyright notice and this permission notice shall be included
#  in all copies or substantial portions of the Software.
#
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
#  THE SOFTWARE.

""" Color specifications using CSS2 (Cascading Style Sheet) syntax."""

class Color:    
    """ Color specifications using CSS2 (Cascading Style Sheet) syntax.
    
    http://www.w3.org/TR/REC-CSS2/syndata.html#color-units

    Usage:
    
    red = Color(255,0,0)
    red = Color(1., 0., 0.)
    red = Color.by_name("red")
    red = Color.from_rgb(1.,0.,0.)
    red = Color.from_rgb(255,0,0)
    red = Color.from_hsl(0.,1., 0.5)
    
    red = Color.from_string("red") 
    red = Color.from_string("RED") 
    red = Color.from_string("#F00") 
    red = Color.from_string("#FF0000") 
    red = Color.from_string("rgb(255, 0, 0)")
    red = Color.from_string("rgb(100%, 0%, 0%)")
    red = Color.from_string("hsl(0, 100%, 50%)")

    """
    def __init__(self, red, green, blue) :
        
        if ( type(red) is not type(green) ) or (type(red) is not type(blue)):
            raise TypeError("Mixed floats and integers?")

        if type(red) is type(1) : red = float(red)/255.
        if type(green) is type(1) : green = float(green)/255.                  
        if type(blue) is type(1) : blue = float(blue)/255.

        self.red = max(0., min(float(red), 1.0))
        self.green = max(0., min(float(green), 1.0))
        self.blue = max(0., min(float(blue), 1.0))

    #@staticmethod
    def names():
        "Return a list of standard color names."
        return _std_colors.keys()
    names = staticmethod(names)

    #@classmethod
    def from_rgb(cls, r, g, b):
        return cls(r,g,b)
    from_rgb = classmethod(from_rgb)
 
    #@classmethod
    def from_hsl(cls, hue_angle, saturation, lightness ):
        def hue_to_rgb( v1, v2, vH) :
            if vH < 0.0 : vH += 1.0
            if vH > 1.0 : vH -= 1.0
            if vH*6.0 < 1.0 : return (v1 + (v2 - v1) * 6.0 * vH)
            if vH*2.0 < 1.0 : return v2
            if vH*3.0 < 2.0 : return (v1 + (v2 - v1) * ((2.0/3.0) - vH) * 6.0)
            return v1      
    
        hue =  (((hue_angle % 360.) + 360.) % 360.)/360.
        
        if not (saturation >= 0.0 and saturation <=1.0) :
            raise ValueError("Out-of-range saturation %f"% saturation)
        if not (lightness >= 0.0 and lightness <=1.0) :
            raise ValueError("Out-of-range lightness %f"% lightness)
                
        if saturation == 0 :
            # greyscale
            return cls.from_rgb( lightness, lightness, lightness)
    
        if lightness < 0.5 :
            v2 = lightness * (1.0+ saturation)
        else :
            v2 = (lightness + saturation) - (saturation* lightness)       

        v1 = 2.0 * lightness - v2
        r = hue_to_rgb( v1, v2, hue + (1./3.) )
        g = hue_to_rgb( v1, v2, hue )
        b = hue_to_rgb( v1, v2, hue - (1./3.) )

        return cls(r,g,b)
    from_hsl = classmethod(from_hsl)  
 
    
    #@staticmethod
    def by_name(string):
        s = string.strip().lower().replace(' ', '')
        
        try:
            return _std_colors[s]
        except KeyError:
            raise ValueError("Unknown color name: %s"%  s) 
    by_name = staticmethod(by_name)
       
    #@classmethod
    def from_string(cls, string):
        def to_frac(string) :
            # string can be "255" or "100%"
            if string[-1]=='%': 
                return float(string[0:-1])/100.
            else:
                return float(string)/255.
                
        s = string.strip().lower().replace(' ', '').replace('_', '')
        
        if s in _std_colors : # "red"
            return _std_colors[s]

        if s[0] == "#" :    # "#fef"
            if len(s) == 4 :
                r = int(s[1]+s[1],16)
                g = int(s[2]+s[2],16)
                b = int(s[3]+s[3],16)
                return cls(r,g,b)
            elif len(s) ==7 :   # "#ff00aa"
                r = int(s[1:3],16)
                g = int(s[3:5],16)
                b = int(s[5:7],16)
                return cls(r,g,b)
            else :
                raise ValueError("Cannot parse string: %s" % s)

        if s[0:4] == 'rgb(' and s[-1] == ')' :
            rgb = s[4:-1].split(",")
            if len(rgb) != 3 :
                raise ValueError("Cannot parse string a: %s" % s)
            return cls( to_frac(rgb[0]), to_frac(rgb[1]), to_frac(rgb[2]))

        if s[0:4] == 'hsl(' and s[-1] == ')' :
            hsl = s[4:-1].split(",")
            if len(hsl) != 3 :
                raise ValueError("Cannot parse string a: %s" % s)
            return cls.from_hsl( int(hsl[0]), to_frac(hsl[1]), to_frac(hsl[2]))
        
        raise ValueError("Cannot parse string: %s" % s)
    from_string = classmethod(from_string)

    def __eq__(self, other) :
        req = int(0.5+255.*self.red) == int(0.5+255.*other.red)
        beq = int(0.5+255.*self.blue) == int(0.5+255.*other.blue)
        geq = int(0.5+255.*self.green) == int(0.5+255.*other.green)
        
        return req and beq and geq

    def __repr__(self):
        return "Color(%f,%f,%f)" % (self.red, self.green, self.blue)
        
    
_std_colors = dict(
    aliceblue = Color(240,248,255),     #f0f8ff
    antiquewhite = Color(250,235,215),     #faebd7
    aqua = Color(0,255,255),     #00ffff
    aquamarine = Color(127,255,212),     #7fffd4
    azure = Color(240,255,255),     #f0ffff
    beige = Color(245,245,220),     #f5f5dc
    bisque = Color(255,228,196),     #ffe4c4
    black = Color(0,0,0),     #000000
    blanchedalmond = Color(255,235,205),     #ffebcd
    blue = Color(0,0,255),     #0000ff
    blueviolet = Color(138,43,226),     #8a2be2
    brown = Color(165,42,42),     #a52a2a
    burlywood = Color(222,184,135),     #deb887
    cadetblue = Color(95,158,160),     #5f9ea0
    chartreuse = Color(127,255,0),     #7fff00
    chocolate = Color(210,105,30),     #d2691e
    coral = Color(255,127,80),     #ff7f50
    cornflowerblue = Color(100,149,237),     #6495ed
    cornsilk = Color(255,248,220),     #fff8dc
    crimson = Color(220,20,60),     #dc143c
    cyan = Color(0,255,255),     #00ffff
    darkblue = Color(0,0,139),     #00008b
    darkcyan = Color(0,139,139),     #008b8b
    darkgoldenrod = Color(184,134,11),     #b8860b
    darkgray = Color(169,169,169),     #a9a9a9
    darkgreen = Color(0,100,0),     #006400
    darkgrey = Color(169,169,169),     #a9a9a9
    darkkhaki = Color(189,183,107),     #bdb76b
    darkmagenta = Color(139,0,139),     #8b008b
    darkolivegreen = Color(85,107,47),     #556b2f
    darkorange = Color(255,140,0),     #ff8c00
    darkorchid = Color(153,50,204),     #9932cc
    darkred = Color(139,0,0),     #8b0000
    darksalmon = Color(233,150,122),     #e9967a
    darkseagreen = Color(143,188,143),     #8fbc8f
    darkslateblue = Color(72,61,139),     #483d8b
    darkslategray = Color(47,79,79),     #2f4f4f
    darkslategrey = Color(47,79,79),     #2f4f4f
    darkturquoise = Color(0,206,209),     #00ced1
    darkviolet = Color(148,0,211),     #9400d3
    deeppink = Color(255,20,147),     #ff1493
    deepskyblue = Color(0,191,255),     #00bfff
    dimgray = Color(105,105,105),     #696969
    dimgrey = Color(105,105,105),     #696969    
    dodgerblue = Color(30,144,255),     #1e90ff
    firebrick = Color(178,34,34),     #b22222
    floralwhite = Color(255,250,240),     #fffaf0
    forestgreen = Color(34,139,34),     #228b22
    fuchsia = Color(255,0,255),     #ff00ff
    gainsboro = Color(220,220,220),     #dcdcdc
    ghostwhite = Color(248,248,255),     #f8f8ff
    gold = Color(255,215,0),     #ffd700
    goldenrod = Color(218,165,32),     #daa520
    gray = Color(128,128,128),     #808080
    green = Color(0,128,0),     #008000
    greenyellow = Color(173,255,47),     #adff2f
    grey = Color(128,128,128),     #808080
    honeydew = Color(240,255,240),     #f0fff0
    hotpink = Color(255,105,180),     #ff69b4
    indianred = Color(205,92,92),     #cd5c5c
    indigo = Color(75,0,130),     #4b0082
    ivory = Color(255,255,240),     #fffff0
    khaki = Color(240,230,140),     #f0e68c
    lavender = Color(230,230,250),     #e6e6fa
    lavenderblush = Color(255,240,245),     #fff0f5
    lawngreen = Color(124,252,0),     #7cfc00
    lemonchiffon = Color(255,250,205),     #fffacd
    lightblue = Color(173,216,230),     #add8e6
    lightcoral = Color(240,128,128),     #f08080
    lightcyan = Color(224,255,255),     #e0ffff
    lightgoldenrodyellow = Color(250,250,210),     #fafad2
    lightgray = Color(211,211,211),     #d3d3d3
    lightgreen = Color(144,238,144),     #90ee90
    lightgrey = Color(211,211,211),     #d3d3d3
    lightpink = Color(255,182,193),     #ffb6c1
    lightsalmon = Color(255,160,122),     #ffa07a
    lightseagreen = Color(32,178,170),     #20b2aa
    lightskyblue = Color(135,206,250),     #87cefa
    lightslategray = Color(119,136,153),     #778899
    lightslategrey = Color(119,136,153),     #778899
    lightsteelblue = Color(176,196,222),     #b0c4de
    lightyellow = Color(255,255,224),     #ffffe0
    lime = Color(0,255,0),     #00ff00
    limegreen = Color(50,205,50),     #32cd32
    linen = Color(250,240,230),     #faf0e6
    magenta = Color(255,0,255),     #ff00ff
    maroon = Color(128,0,0),     #800000
    mediumaquamarine = Color(102,205,170),     #66cdaa
    mediumblue = Color(0,0,205),     #0000cd
    mediumorchid = Color(186,85,211),     #ba55d3
    mediumpurple = Color(147,112,219),     #9370db
    mediumseagreen = Color(60,179,113),     #3cb371
    mediumslateblue = Color(123,104,238),     #7b68ee
    mediumspringgreen = Color(0,250,154),     #00fa9a
    mediumturquoise = Color(72,209,204),     #48d1cc
    mediumvioletred = Color(199,21,133),     #c71585
    midnightblue = Color(25,25,112),     #191970
    mintcream = Color(245,255,250),     #f5fffa
    mistyrose = Color(255,228,225),     #ffe4e1
    moccasin = Color(255,228,181),     #ffe4b5
    navajowhite = Color(255,222,173),     #ffdead
    navy = Color(0,0,128),     #000080
    oldlace = Color(253,245,230),     #fdf5e6
    olive = Color(128,128,0),     #808000
    olivedrab = Color(107,142,35),     #6b8e23
    orange = Color(255,165,0),     #ffa500
    orangered = Color(255,69,0),     #ff4500
    orchid = Color(218,112,214),     #da70d6
    palegoldenrod = Color(238,232,170),     #eee8aa
    palegreen = Color(152,251,152),     #98fb98
    paleturquoise = Color(175,238,238),     #afeeee
    palevioletred = Color(219,112,147),     #db7093
    papayawhip = Color(255,239,213),     #ffefd5
    peachpuff = Color(255,218,185),     #ffdab9
    peru = Color(205,133,63),     #cd853f
    pink = Color(255,192,203),     #ffc0cb
    plum = Color(221,160,221),     #dda0dd
    powderblue = Color(176,224,230),     #b0e0e6
    purple = Color(128,0,128),     #800080
    red = Color(255,0,0),     #ff0000
    rosybrown = Color(188,143,143),     #bc8f8f
    royalblue = Color(65,105,225),     #4169e1
    saddlebrown = Color(139,69,19),     #8b4513
    salmon = Color(250,128,114),     #fa8072
    sandybrown = Color(244,164,96),     #f4a460
    seagreen = Color(46,139,87),     #2e8b57
    seashell = Color(255,245,238),     #fff5ee
    sienna = Color(160,82,45),     #a0522d
    silver = Color(192,192,192),     #c0c0c0
    skyblue = Color(135,206,235),     #87ceeb
    slateblue = Color(106,90,205),     #6a5acd
    slategray = Color(112,128,144),     #708090
    slategrey = Color(112,128,144),     #708090
    snow = Color(255,250,250),     #fffafa
    springgreen = Color(0,255,127),     #00ff7f
    steelblue = Color(70,130,180),     #4682b4
    tan = Color(210,180,140),     #d2b48c
    teal = Color(0,128,128),     #008080
    thistle = Color(216,191,216),     #d8bfd8
    tomato = Color(255,99,71),     #ff6347
    turquoise = Color(64,224,208),     #40e0d0
    violet = Color(238,130,238),     #ee82ee
    wheat = Color(245,222,179),     #f5deb3
    white = Color(255,255,255),     #ffffff
    whitesmoke = Color(245,245,245),     #f5f5f5
    yellow = Color(255,255,0),     #ffff00
    yellowgreen = Color(154,205,50)     #9acd32
    )
    
    
