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

    /**
     * @class ui.slider
     * @extends core
     * @alias Slider
     * @requires jquery
     * @requires util.base
     */
    var UI = function() {
        var self, isVertical, preFromValue, preToValue;
        var $root, $track, $handle, $toHandle, $tooltipTrack, $progress;
        var $tooltip, $tooltipMessage, $tooltip2, $tooltipMessage2;

        function min() {
            return self.options.min;
        }

        function max() {
            return self.options.max;
        }

        function step() {
            return self.options.step;
        }

        function type() {
            return self.options.type;
        }

        function isDouble() {
            return type() == 'double';
        }

        function isSingle() {
            return type() == 'single';
        }

        function isShowProgress() {

            return $root.data('progress') == false ? false : self.options.progress;
        }

        function isShowTooltip() {
            return $root.data('tooltip') == false ? false : self.options.tooltip;
        }

        function getTooltip(type) {
            return (type == 'from') ? $tooltip : $tooltip2;
        }

        function getTooltipMessage(type) {
            return (type == 'from') ? $tooltipMessage : $tooltipMessage2;
        }

        function getHandle(type) {
            if (type == 'to') {
                return $toHandle;
            }

            return $handle;
        }

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

            return e;
        }

        function getStyleValue($node, key) {
            return $node[0].style[key];
        }

        function setProgressBar() {
            if (isSingle()) {
                if (isVertical) {
                    $progress.height(getStyleValue($handle, 'bottom')).css({ bottom : 0 });
                } else {
                    $progress.width(getStyleValue($handle, 'left'));
                }
            } else {
                if (isVertical) {
                    var toDist = parseFloat(getStyleValue($toHandle, 'bottom').replace('%', ''));
                    var fromDist = parseFloat(getStyleValue($handle, 'bottom').replace('%', ''));

                    $progress.height((toDist - fromDist) + '%').css({
                        bottom : fromDist + '%'
                    });
                } else {
                    var toDist = parseFloat(getStyleValue($toHandle, 'left').replace('%', ''));
                    var fromDist = parseFloat(getStyleValue($handle, 'left').replace('%', ''));

                    $progress.width((toDist - fromDist) + '%').css({
                        left : fromDist + '%'
                    });
                }
            }
        }

        function checkMaxFromTo(dist, type) {
            if (isDouble()) {
                if (type == 'from') {
                    if (isVertical) {
                        var toDist = parseFloat(getStyleValue($toHandle, 'bottom').replace('%', ''));
                        if (dist >=  toDist) {
                            dist = toDist;
                        }
                    } else {
                        var toDist = parseFloat(getStyleValue($toHandle, 'left').replace('%', ''));
                        if (dist >=  toDist) {
                            dist = toDist;
                        }
                    }
                } else if (type == 'to') {
                    if (isVertical) {
                        var fromDist = parseFloat(getStyleValue($handle, 'bottom').replace('%', ''));
                        if (dist <=  fromDist) {
                            dist = fromDist;
                        }
                    } else {
                        var fromDist = parseFloat(getStyleValue($handle, 'left').replace('%', ''));
                        if (dist <=  fromDist) {
                            dist = fromDist;
                        }
                    }
                }
            }

            return dist;
        }

        function setViewStatus(dist, type) {
            var value = getValue(dist/100);

            if (value < min()) value = min();
            if (value > max()) value = max();

            dist = (value - min()) / (max() - min()) * 100;
            dist = checkMaxFromTo(dist, type);

            // redefine value
            value = getValue(dist/100);

            var percent = dist + '%';
            var $handle = getHandle(type)

            if (isVertical) {
                $handle.css({ bottom : percent });
            } else {
                $handle.css({ left : percent });
            }

            setProgressBar();

            if (isShowTooltip()) {
                var $tooltip = getTooltip(type);
                var $tooltipMessage = getTooltipMessage(type);

                if (_.typeCheck("function", self.options.format)) {
                    value = self.options.format.call(self, value);
                }

                $tooltipMessage.html(value);

                if (isVertical) {
                    $tooltip.css({
                        bottom : $track.height() * (dist / 100),
                        'margin-bottom' : -1 * ($tooltip.height()/2)
                    });

                } else {
                    $tooltip.css({
                        left : percent,
                        "margin-left" : -1 * ($tooltip.width()/2)
                    });

                    var xPos = $track.width() * ( dist/100);
                    var lastPos =  xPos + $tooltip.width()/2;
                    var firstPos =  xPos - $tooltip.width()/2;

                    if (lastPos >= $track.width() ) {
                        $tooltip.css({
                            left : $track.width() - $tooltip.width() + $handle.width()/2,
                            'margin-left' : 0
                        }).addClass('last');
                    } else if (firstPos <= 0 ) {
                        $tooltip.css({
                            'left' : -$handle.width()/2,
                            'margin-left' : 0
                        }).addClass('first');
                    } else {
                        $tooltip.removeClass('first last');
                    }
                }

                $tooltip.show();
            }

            if (type == 'from') {
                if (preFromValue != value) {
                    self.emit("change", [ { type: type, from: value, to: self.getToValue() } ]);
                    preFromValue = value;
                }
            } else if (type == 'to') {
                if (preToValue != value) {
                    self.emit("change", [ { type: type, from: self.getFromValue(), to: value } ]);
                    preToValue = value;
                }
            }

        }

        function setHandlePosition(e, type) {
            var min, max, current;
            var dist = undefined;

            if (self.options.orient == 'vertical') {
                min = $track.offset().top - $("body").scrollTop();
                max = min + $track.height();
                current = pos(e).clientY;

                if (current <= min) {
                    dist = 100;
                } else if (current >= max) {
                    dist = 0;
                } else {
                    dist = (max - current) / (max - min) * 100;
                }
            } else {
                min = $track.offset().left;
                max = min + $track.width();
                current = pos(e).clientX;

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

            setViewStatus(dist, type);
        }

        function getValue(dist) {
            if (typeof dist == 'undefined') {

                if (isVertical) {
                    dist = parseFloat($handle.css('bottom'))/$track.height();
                } else {
                    dist = parseFloat($handle.css('left'))/$track.width();
                }
            }

            var minValue = min();
            var maxValue = max();

            var value = (minValue + (maxValue - minValue) * dist);

            var stepValue = step();
            var temp = math.remain(value, stepValue);

            value = math.minus(value, temp);

            if (temp > math.div(stepValue, 2)) {
                value = math.plus(value, stepValue);
            }

            return value;
        }

        function initElement() {
            $root.addClass(self.options.orient);

            $track = $("<div class='track' />");
            $tooltipTrack = $("<div class='tooltip-track' />");
            $progress = $("<div class='progress' />");

            if (!isShowProgress()) {
                $progress.hide();
            }

            $handle = $("<div class='handle from' />");
            $track.html($progress);
            $track.append($handle);

            if (isDouble()) {
                $toHandle = $("<div class='handle to' />");
                $track.append($toHandle);
            }

            var tooltip_orient = isVertical ? 'right': 'top';

            $tooltip = $('<div class="tooltip '+tooltip_orient+'"><div class="message" /></div>').hide();
            $tooltipMessage = $tooltip.find(".message");

            $tooltip2 = $('<div class="tooltip '+tooltip_orient+'"><div class="message" /></div>').hide();
            $tooltipMessage2 = $tooltip2.find(".message");

            $tooltipTrack.html($tooltip);
            $tooltipTrack.append($tooltip2);

            $root.html($track);
            $root.append($tooltipTrack);

            if (isShowTooltip()) {
                $root.addClass('has-tooltip');
            }
        }

        function initEvent() {
            self.addEvent($handle, 'mousedown', function(e) {
                $handle.data('select', true);
                $("body").addClass("slider-cursor");
            });

            if (isDouble()) {
                self.addEvent($toHandle, 'mousedown', function(e) {
                    $toHandle.data('select', true);
                    $("body").addClass("slider-cursor");
                });
            }

            self.addEvent($track, 'mousedown', function(e) {
                $("body").addClass("slider-cursor");

                if (self.options.type == 'single') {
                    $handle.data('select', true);
                    setHandlePosition(e, 'from');
                } else {
                    //TODO: if type is double, check position
                }
            });

            self.addEvent('body', 'mouseup', function(e) {
                $handle.data('select', false);
                if (self.options.type == 'double') {
                    $toHandle.data('select', false);
                }

                $("body").removeClass("slider-cursor");
            });

            self.addEvent('body', 'mousemove', function(e) {
                if ($handle.data('select')) {
                    setHandlePosition(e, 'from');
                } else if (self.options.type == 'double' && $toHandle.data('select')) {
                    setHandlePosition(e, 'to');
                }
            });
        }

        this.init = function() {
            self = this;
            $root = $(this.root);

            isVertical = (this.options.orient == 'vertical');
            initElement();
            initEvent();

            this.setFromValue();
            this.setToValue();
        }

        /**
         * @method setFromValue
         * set FromHandle's value
         *
         * @param {Number}
         */
        this.setFromValue = function(value) {
            var from = value || $root.data("from") || this.options.from,
                dist = (from - min()) / (max() - min()) * 100;

            setViewStatus(dist, "from");
        }

        /**
         * @method setToValue
         * set ToHandle's value
         *
         * @param {Number}
         */
        this.setToValue = function(value) {
            if (isDouble()) {
                var to = value || $root.data("to") || this.options.to,
                    dist = (to - min()) / (max() - min()) * 100;

                setViewStatus(dist, "to");
            }
        }

        /**
         * @method getFromValue
         * get FromHandle's value
         *
         * @return {Number} value
         */
        this.getFromValue = function() {
            return getValue();
        }

        /**
         * @method getToValue
         * get ToHandle's value
         *
         * @return {Number} value
         */
        this.getToValue = function () {
            var dist;

            if(isDouble()) {
                if (isVertical) {
                    dist = parseFloat($toHandle.css("bottom")) / $track.height();
                } else {
                    dist = parseFloat($toHandle.css("left")) / $track.width();
                }

                return getValue(dist);
            }

            return getValue();
        }
    }

    UI.setup = function() {
        return {
            type : "single", // or double
            orient : "horizontal", // or vertical,
            min : 0,
            max : 10,
            step : 1,
            from : 0,
            to : 10,
            tooltip : true,
            format : null,
            progress : true

        }
    }

    /**
     * @event change
     * Event that occurs when dragging on a slider
     *
     * @param {Object} data Data of current from
     * @param {jQueryEvent} e The event object
     */

    return UI;
});