jui.defineUI("ui.colorpicker", [ "jquery", "util.base", "util.color" ], function($, _, color) {

    /**
     * @class ui.colorpicker
     * @extends core
     * @alias ColorPicker
     * @requires jquery
     * @requires util.base
     * @requires util.color
     */
    var UI = function() {
        var self, opts, dist;
        var hue_color = [
            { rgb : '#ff0000', start : .0 },
            { rgb : '#ffff00', start : .17 },
            { rgb : '#00ff00', start : .33 },
            { rgb : '#00ffff', start : .50 },
            { rgb : '#0000ff', start : .67 },
            { rgb : '#ff00ff', start : .83 },
            { rgb : '#ff0000', start : 1 }
        ];

        var $root, $hue, $color, $value, $saturation, $drag_pointer, $drag_bar,
            $control, $controlPattern, $controlColor, $hueContainer, $opacity, $opacityContainer,
            $opacityInput, $opacity_drag_bar, $information, $informationTitle1, $informationTitle2,
            $informationTitle3, $informationTitle4, $informationInput1, $informationInput2,
            $informationInput3, $informationInput4;

        function setInputColor(evtType) {
            var rgb = null;

            if (evtType == 'hex') {
                rgb = color.rgb($informationInput1.val());

                $informationInput2.val(rgb.r);
                $informationInput3.val(rgb.g);
                $informationInput4.val(rgb.b);

            } else if (evtType == 'rgb') {
                $informationInput1.val(color.format({
                    r : parseInt($informationInput2.val(), 10),
                    g : parseInt($informationInput3.val(), 10),
                    b : parseInt($informationInput4.val(), 10)
                }, 'hex'));

                rgb = color.rgb($informationInput1.val());

            } else {
                var str = self.getColor('hex');

                $informationInput1.val(str);

                rgb = color.rgb($informationInput1.val());
                $informationInput2.val(rgb.r);
                $informationInput3.val(rgb.g);
                $informationInput4.val(rgb.b);
            }

            // set alpha
            rgb.a = caculateOpacity();

            // set background
            $controlColor.css("background-color", color.format(rgb, 'hex'));
            $opacityInput.val(Math.floor(rgb.a * 100) + "%");

            // emit change
            self.emit("change", [ color.format(rgb, 'hex' ), rgb ]);
        }

        function setMainColor(e) {
            var offset = $color.offset();
            var w = $color.width();
            var h = $color.height();

            var x = e.clientX - offset.left;
            var y = e.clientY - offset.top;

            if (x < 0) x = 0;
            else if (x > w) x = w;

            if (y < 0) y = 0;
            else if (y > h) y = h;

            $drag_pointer.css({
                left: x - 5,
                top: y - 5
            }).data('pos', { x: x, y : y});

            setInputColor();
        }

        function checkHueColor(p) {
            var startColor, endColor;

            for(var i = 0; i < hue_color.length;i++) {
                if (hue_color[i].start >= p) {
                    startColor = hue_color[i-1];
                    endColor = hue_color[i];
                    break;
                }
            }

            if (startColor && endColor) {
                var scale = color.scale().domain(startColor.rgb, endColor.rgb);

                return scale((p - startColor.start)/(endColor.start - startColor.start), 'hex');
            }

            return null;
        }

        function setHueColor(e) {
            var min = $hueContainer.offset().left;
            var max = min + $hueContainer.width();
            var current = pos(e).clientX;

            if (current < min) {
                dist = 0;
            } else if (current > max) {
                dist = 100;
            } else {
                dist = (current - min) / (max - min) * 100;
            }

            var x = ($hue.width() * (dist/100));

            $drag_bar.css({
                left: (x -Math.ceil($drag_bar.width()/2)) + 'px'
            }).data('pos', { x : x});

            var hueColor = checkHueColor(dist/100);
            $color.css("background-color", hueColor);

            setInputColor();
        }

        function setOpacity(e) {
            var min = $opacity.offset().left;
            var max = min + $opacity.width();
            var current = pos(e).clientX;

            if (current < min) {
                dist = 0;
            } else if (current > max) {
                dist = 100;
            } else {
                dist = (current - min) / (max - min) * 100;
            }

            var x = ($opacity.width() * (dist/100));

            $opacity_drag_bar.css({
                left: (x -Math.ceil($opacity_drag_bar.width()/2)) + 'px'
            }).data('pos', { x : x});

            setInputColor();
        }

        function caculateOpacity() {
            var opacityPos = $opacity_drag_bar.data('pos') || { x : 0 };
            var a = Math.round((opacityPos.x / $opacity.width()) * 100) / 100;

            return a;
        }

        function calculateColor() {
            var pos = $drag_pointer.data('pos') || { x : 0, y : 0 };
            var huePos = $drag_bar.data('pos') || { x : 0 };

            var width = $color.width();
            var height = $color.height();

            var h = (huePos.x / $hue.width()) * 360;
            var s = (pos.x / width);
            var v = ((height - pos.y) / height);

            var rgb = color.HSVtoRGB(h, s, v);
            rgb.a = caculateOpacity();

            return rgb;
        }

        function selectDom(tag, attr) {
            var tag = !tag ? "div" : tag,
                $dom = $("<" + tag + " />");

            if(typeof(attr) == "object") {
                $dom.attr(attr);
            }

            return $dom;
        };

        function pos(e) {
            if (_.isTouch) {
                return e.originalEvent.touches[0];
            }

            return e;
        }

        function checkNumberKey(e) {
            var code = e.which,
                isExcept = false;

            if(code == 37 || code == 39 || code == 8 || code == 46 || code == 9)
                isExcept = true;

            if(!isExcept && (code < 48 || code > 57))
                return false;

            return true;
        }

        function setRGBtoHexColor(e) {
            var r = $informationInput2.val(),
                g = $informationInput3.val(),
                b = $informationInput4.val();

            if(r == "" || g == "" || b == "") return;

            if(parseInt(r) > 255) $informationInput2.val(255);
            else $informationInput2.val(parseInt(r));

            if(parseInt(g) > 255) $informationInput3.val(255);
            else $informationInput3.val(parseInt(g));

            if(parseInt(b) > 255) $informationInput4.val(255);
            else $informationInput4.val(parseInt(b));

            initColor(color.format({
                r: parseInt($informationInput2.val()),
                g: parseInt($informationInput3.val()),
                b: parseInt($informationInput4.val())
            }, "hex"), "rgb");
        }

        function initColor(newColor, evtType) {
            var c = newColor || self.options.color,
                rgb = color.rgb(c);

            $color.css("background-color", c);

            var hsv = color.RGBtoHSV(rgb.r, rgb.g, rgb.b),
                x = $color.width() * hsv.s,
                y = $color.height() * (1-hsv.v);

            $drag_pointer.css({
                left : x - 5,
                top : y - 5
            }).data('pos', { x  : x, y : y });

            var hueX = $hue.width() * (hsv.h / 360);

            $drag_bar.css({
                left : hueX - 7.5
            }).data('pos', { x : hueX });

            var opacityX = $opacity.width() * (rgb.a || 0);

            $opacity_drag_bar.css({
                left : opacityX - 7.5
            }).data('pos', { x : opacityX });

            setInputColor(evtType);
        }

        function initEvent() {
            self.addEvent($color, 'mousedown', function(e) {
                $color.data('isDown', true);
                setMainColor(e);
            });

            self.addEvent($color, 'mouseup', function(e) {
                $color.data('isDown', false);
            });

            self.addEvent($drag_bar, 'mousedown', function(e) {
                e.preventDefault();
                $hue.data('isDown', true);
            });

            self.addEvent($opacity_drag_bar, 'mousedown', function(e) {
                e.preventDefault();
                $opacity.data('isDown', true);
            });

            self.addEvent($hueContainer, 'mousedown', function(e) {
                $hue.data('isDown', true);
                setHueColor(e);
            });

            self.addEvent($opacityContainer, 'mousedown', function(e) {
                $opacity.data('isDown', true);
                setOpacity(e);
            });

            self.addEvent($informationInput1, 'keydown', function(e) {
                if(e.which < 65 || e.which > 70) {
                    return checkNumberKey(e);
                }
            });
            self.addEvent($informationInput1, 'keyup', function(e) {
                var code = $(this).val();

                if(code.charAt(0) == '#' && code.length == 7) {
                    initColor(code, 'hex');
                }
            });

            self.addEvent($informationInput2, 'keydown', checkNumberKey);
            self.addEvent($informationInput2, 'keyup', setRGBtoHexColor);
            self.addEvent($informationInput3, 'keydown', checkNumberKey);
            self.addEvent($informationInput3, 'keyup', setRGBtoHexColor);
            self.addEvent($informationInput4, 'keydown', checkNumberKey);
            self.addEvent($informationInput4, 'keyup', setRGBtoHexColor);

            self.addEvent(document, 'mouseup', function (e) {
                $color.data('isDown', false);
                $hue.data('isDown', false);
                $opacity.data('isDown', false);
            });

            self.addEvent(document, 'mousemove', function (e) {
                if ($color.data('isDown')) {
                    setMainColor(e);
                }

                if ($hue.data('isDown')) {
                    setHueColor(e);
                }

                if ($opacity.data('isDown')) {
                    setOpacity(e);
                }
            });
        }

        this.init = function() {
            self = this, opts = this.options;

            $root = $(this.root);
            $color = selectDom('div', { 'class': 'color' });
            $drag_pointer = selectDom('div', { 'class': 'drag-pointer' });
            $value = selectDom('div', { 'class': 'value' });
            $saturation = selectDom('div', { 'class': 'saturation' });

            $control = selectDom('div', { 'class': 'control' });
            $controlPattern = selectDom('div', { 'class': 'empty' });
            $controlColor = selectDom('div', { 'class': 'color' });
            $hue = selectDom('div', { 'class': 'hue' });
            $hueContainer = selectDom('div', { 'class': 'container' });
            $drag_bar = selectDom('div', { 'class': 'drag-bar' });
            $opacity = selectDom('div', { 'class': 'opacity' });
            $opacityContainer = selectDom('div', { 'class': 'container' });
            $opacityInput = selectDom('input', { 'class': 'input', 'type': 'text', 'disabled': true });
            $opacity_drag_bar = selectDom('div', { 'class': 'drag-bar2' });

            $information = selectDom('div', { 'class': 'information' });
            $informationTitle1 = selectDom('div', { 'class': 'title' }).html("HEX");
            $informationTitle2 = selectDom('div', { 'class': 'title' }).html("R");
            $informationTitle3 = selectDom('div', { 'class': 'title' }).html("G");
            $informationTitle4 = selectDom('div', { 'class': 'title' }).html("B");
            $informationInput1 = selectDom('input', { 'class': 'input', 'type': 'text', 'maxlength': 7 });
            $informationInput2 = selectDom('input', { 'class': 'input', 'type': 'text', 'maxlength': 3  });
            $informationInput3 = selectDom('input', { 'class': 'input', 'type': 'text', 'maxlength': 3  });
            $informationInput4 = selectDom('input', { 'class': 'input', 'type': 'text', 'maxlength': 3  });

            $value.html($drag_pointer);
            $saturation.html($value);
            $color.html($saturation);

            $hueContainer.html($drag_bar);
            $hue.html($hueContainer);

            $opacityContainer.html($opacity_drag_bar);
            $opacity.html($opacityContainer);

            $control.append($hue);
            $control.append($opacity);
            $control.append($opacityInput);
            $control.append($controlPattern);
            $control.append($controlColor);

            $information.append($informationInput1);
            $information.append($informationInput2);
            $information.append($informationInput3);
            $information.append($informationInput4);
            $information.append($informationTitle1);
            $information.append($informationTitle2);
            $information.append($informationTitle3);
            $information.append($informationTitle4);

            $root.html($color);
            $root.append($control);
            $root.append($information);

            initEvent();
            initColor();
        }

        this.setColor = function(value) {
            if(typeof(value) == "object") {
                if(!value.r || !value.g || !value.b)
                    return;

                initColor(color.format(value, "hex"));
            } else if(typeof(value) == "string") {
                if(value.charAt(0) != "#")
                    return;

                initColor(value);
            }
        }

        this.getColor = function(type) {
            var rgb = calculateColor();

            if (type) {
                if (type == 'hex') {
                    if (rgb.a < 1) {
                        type = 'rgb';
                    }
                }
                return color.format(rgb, type);
            }

            return rgb;
        }
    }

    UI.setup = function() {
        return {
            type : 'full',
            color : '#FF0000'
        }
    }

    return UI;
});