/**
 * Color For KISSY.
 * @ignore
 * @author yiminghe@gmail.com
 */
KISSY.add("color", function (S, Base) {
    var rgbaRe = /\s*rgba?\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*(?:,\s*([\d\.]+))?\)\s*/,
        hexRe = /\s*#([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)\s*/;

    /**
     * Color for KISSY to normalize HSL, HSV, RGB and HEX.
     * @class KISSY.Color
     */
    var Color = Base.extend({
        /**
         * To hsl string format
         * @return {String}
         */
        toHSL: function () {
            var hsl = this.getHSL();
            return "hsl(" + (Math.round(hsl.h || 0)) + ", " + percentage(hsl.s) + ", " + percentage(hsl.l) + ")";
        },
        /**
         * To hsla string format
         * @return {String}
         */
        'toHSLA': function () {
            var hsl = this.getHSL();
            return "hsla(" + (Math.round(hsl.h || 0)) + ", " + percentage(hsl.s) + ", " +
                percentage(hsl.l) + ", " + this.get('a') + ")";
        },

        /**
         * To rgb string format
         * @return {String}
         */
        toRGB: function () {
            var self = this;
            return "rgb(" + self.get("r") + ", " + self.get("g") + ", " + self.get("b") + ")";
        },
        /**
         * To rgba string format
         * @return {String}
         */
        toRGBA: function () {
            var self = this;
            return "rgba(" + self.get("r") + ", " + self.get("g") +
                ", " + self.get("b") + ", " + self.get("a") + ")";
        },
        /**
         * To hex string format
         * @return {String}
         */
        toHex: function () {
            var self = this;
            return "#" + padding2(Number(self.get("r")).toString(16)) +
                padding2(Number(self.get("g")).toString(16)) + padding2(Number(self.get("b")).toString(16));
        },
        /**
         * Return the color in the rgba format.
         * @return {String}
         */
        toString: function () {
            return this.toRGBA();
        },
        /**
         * Return the color in the hsl format.
         * @return {Object}
         */
        getHSL: function () {
            var self = this,
                r = self.get("r") / 255,
                g = self.get("g") / 255,
                b = self.get("b") / 255,
                max = Math.max(r, g, b),
                min = Math.min(r, g, b),
                delta = max - min,
                h,
                s = 0,
                l = 0.5 * (max + min);

            // min==max means achromatic (hue is undefined)
            if (min != max) {
                s = (l < 0.5) ? delta / (max + min) : delta / (2 - max - min);
                if (r == max) {
                    h = 60 * (g - b) / delta;
                } else if (g == max) {
                    h = 120 + 60 * (b - r) / delta;
                } else {
                    h = 240 + 60 * (r - g) / delta;
                }

                h = (h + 360) % 360;
            }
            return {
                h: h,
                s: s,
                l: l
            };
        },

        /**
         * Return the color in the hsv format.
         * @return {Object}
         */
        getHSV: function () {
            return rgb2hsv({
                r: this.get("r"),
                g: this.get("g"),
                b: this.get("b")
            });
        },

        /**
         * Set value by hsv
         * @param cfg
         * @param cfg.h
         * @param cfg.s
         * @param cfg.v
         */
        setHSV: function (cfg) {
            var self = this,
                current;
            if ("h" in cfg && "s" in cfg && "v" in cfg) {
            } else {
                current = self.getHSV();
                S.each(["h", "s", "v"], function (x) {
                    if (x in cfg) {
                        current[x] = cfg[x];
                    }
                });
                cfg = current;
            }
            self.set(hsv2rgb(cfg));
        },


        /**
         * Set value by hsl
         * @param cfg
         * @param cfg.h
         * @param cfg.s
         * @param cfg.l
         */
        'setHSL': function (cfg) {
            var self = this,
                current;
            if ("h" in cfg && "s" in cfg && "l" in cfg) {
            } else {
                current = self.getHSL();
                S.each(["h", "s", "l"], function (x) {
                    if (x in cfg) {
                        current[x] = cfg[x];
                    }
                });
                cfg = current;
                // S.mix({x:1},{x:undefined})
            }

            self.set(hsl2rgb(cfg));
        }
    }, {
        ATTRS: {
            /**
             * Red.
             * @type {Number}
             * @property r
             */
            /**
             * Red.
             * @cfg {Number} r
             */
            /**
             * @ignore
             */
            r: {
                getter: function (v) {
                    return Math.round(v);
                },
                setter: function (v) {
                    return constrain255(v);
                }
            },
            /**
             * Green.
             * @type {Number}
             * @property g
             */
            /**
             * Green.
             * @cfg {Number} g
             */
            /**
             * @ignore
             */
            g: {
                getter: function (v) {
                    return Math.round(v);
                },
                setter: function (v) {
                    return constrain255(v);
                }
            },
            /**
             * Blue.
             * @type {Number}
             * @property b
             */
            /**
             * Blue.
             * @cfg {Number} b
             */
            /**
             * @ignore
             */
            b: {
                getter: function (v) {
                    return Math.round(v);
                },
                setter: function (v) {
                    return constrain255(v);
                }
            },
            /**
             * Alpha.
             * @type {Number}
             * @property a
             */
            /**
             * Alpha.
             * Defaults to: 1
             * @cfg {Number} a
             */
            /**
             * @ignore
             */
            a: {
                value: 1,
                setter: function (v) {
                    return constrain1(v);
                }
            }
        },


        /**
         * Construct color object from String.
         * @static
         * @param {String} str string format color( '#rrggbb'  '#rgb' or 'rgb(r,g,b)'  'rgba(r,g,b,a)' )
         */
        parse: function (str) {
            var values, r,
                g,
                b,
                a = 1;

            if ((str.length == 4 || str.length == 7) && str.substr(0, 1) === '#') {
                values = str.match(hexRe);
                if (values) {
                    r = parseHex(values[1]);
                    g = parseHex(values[2]);
                    b = parseHex(values[3]);
                    if (str.length == 4) {
                        r = paddingHex(r);
                        g = paddingHex(g);
                        b = paddingHex(b);
                    }
                }
            }
            else {
                values = str.match(rgbaRe);
                if (values) {
                    r = parseInt(values[1]);
                    g = parseInt(values[2]);
                    b = parseInt(values[3]);
                    a = parseFloat(values[4]) || 1;
                }
            }

            return (typeof r == 'undefined') ? undefined : new Color({
                r: r,
                g: g,
                b: b,
                a: a
            });
        },

        /**
         * Construct color object from hsl.
         * @static
         * @param {Object} cfg
         * @param {Number} cfg.h Hue
         * @param {Number} cfg.s Saturation
         * @param {Number} cfg.l lightness
         * @param {Number} cfg.a alpha
         */
        'fromHSL': function (cfg) {
            var rgb = hsl2rgb(cfg);
            rgb.a = cfg.a;
            return new Color(rgb);
        },
        /**
         * Construct color object from hsv.
         * @static
         * @param {Object} cfg
         * @param {Number} cfg.h Hue
         * @param {Number} cfg.s Saturation
         * @param {Number} cfg.v value
         * @param {Number} cfg.a alpha
         */
        fromHSV: function (cfg) {
            var rgb = hsv2rgb(cfg);
            rgb.a = cfg.a;
            return new Color(rgb);
        }
    });

    // #---------------------------- private start

    function to255(v) {
        return v * 255;
    }


    function hsv2rgb(cfg) {
        var h = Math.min(Math.round(cfg.h), 359),
            s = Math.max(0, Math.min(1, cfg.s)),
            v = Math.max(0, Math.min(1, cfg.v)),
            r, g, b,
            i = Math.floor((h / 60) % 6),
            f = (h / 60) - i,
            p = v * (1 - s),
            q = v * (1 - f * s),
            t = v * (1 - (1 - f) * s);

        switch (i) {
            case 0:
                r = v;
                g = t;
                b = p;
                break;
            case 1:
                r = q;
                g = v;
                b = p;
                break;
            case 2:
                r = p;
                g = v;
                b = t;
                break;
            case 3:
                r = p;
                g = q;
                b = v;
                break;
            case 4:
                r = t;
                g = p;
                b = v;
                break;
            case 5:
                r = v;
                g = p;
                b = q;
                break;
        }

        return {r: constrain255(to255(r)), g: constrain255(to255(g)), b: constrain255(to255(b))};
    }

    function rgb2hsv(cfg) {
        var r = cfg.r / 255,
            g = cfg.g / 255,
            b = cfg.b / 255;

        var h, s,
            min = Math.min(Math.min(r, g), b),
            max = Math.max(Math.max(r, g), b),
            delta = max - min,
            hsv;

        switch (max) {
            case min:
                h = 0;
                break;
            case r:
                h = 60 * (g - b) / delta;
                if (g < b) {
                    h += 360;
                }
                break;
            case g:
                h = (60 * (b - r) / delta) + 120;
                break;
            case b:
                h = (60 * (r - g) / delta) + 240;
                break;
        }

        s = (max === 0) ? 0 : 1 - (min / max);

        hsv = {
            h: Math.round(h),
            s: s,
            v: max
        };

        return hsv;
    }

    function hsl2rgb(cfg) {
        var h = Math.min(Math.round(cfg.h), 359),
            s = Math.max(0, Math.min(1, cfg.s)),
            l = Math.max(0, Math.min(1, cfg.l)),
            C, X, m, rgb = [],
            abs = Math.abs,
            floor = Math.floor;

        if (s == 0 || h == null) {
            // achromatic
            rgb = [l, l, l];
        } else {
            // http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSL
            // C is the chroma
            // X is the second largest component
            // m is the lightness adjustment
            h /= 60;
            C = s * (1 - abs(2 * l - 1));
            X = C * (1 - abs(h - 2 * floor(h / 2) - 1));
            m = l - C / 2;
            switch (floor(h)) {
                case 0:
                    rgb = [C, X, 0];
                    break;
                case 1:
                    rgb = [X, C, 0];
                    break;
                case 2:
                    rgb = [0, C, X];
                    break;
                case 3:
                    rgb = [0, X, C];
                    break;
                case 4:
                    rgb = [X, 0, C];
                    break;
                case 5:
                    rgb = [C, 0, X];
                    break;
            }
            rgb = [rgb[0] + m, rgb[1] + m, rgb[2] + m];
        }
        S.each(rgb, function (v, index) {
            rgb[index] = to255(v);
        });
        return {
            r: rgb[0],
            g: rgb[1],
            b: rgb[2]
        };
    }

    function parseHex(v) {
        return parseInt(v, 16);
    }

    function paddingHex(v) {
        return v + v * 16;
    }

    function padding2(v) {
        if (v.length != 2) {
            v = "0" + v;
        }
        return v;
    }

    function percentage(v) {
        return Math.round(v * 100) + "%";
    }

    function constrain255(v) {
        return Math.max(0, Math.min(v, 255));
    }

    function constrain1(v) {
        return Math.max(0, Math.min(v, 1));
    }

    // #---------------------------- private end

    return Color;
}, {
    requires: ['base']
});

/**
 * @ignore
 * Refer:
 *  - 支持 http://en.wikipedia.org/wiki/HSL_and_HSV
 */