dojo.provide("dojox.form.RangeSlider");
dojo.require("dijit.form.HorizontalSlider");
dojo.require("dijit.form.VerticalSlider");
dojo.require("dojox.fx");

dojo.declare(
    "dojox.form._RangeSliderMixin",
    null,
{
    value: [0,100],
    
    postCreate: function(){
        this.inherited(arguments);
        // we sort the values!
        // TODO: re-think, how to set the value
        if(this._isReversed()){
            this.value.sort(function(a, b){
               return b-a; 
            });
        }
        else{
            this.value.sort(function(a, b){
                return a-b;
            });
        }
        // define a custom constructor for a SliderMoverMax that points back to me
        var _self = this;
        var mover = function(){
            dijit.form._SliderMoverMax.apply(this, arguments);
            this.widget = _self;
        };
        dojo.extend(mover, dijit.form._SliderMoverMax.prototype);

        this._movableMax = new dojo.dnd.Moveable(this.sliderHandleMax,{mover: mover});
        dijit.setWaiState(this.focusNodeMax, "valuemin", this.minimum);
        dijit.setWaiState(this.focusNodeMax, "valuemax", this.maximum);
        
        // a dnd for the bar!
        var barMover = function(){
            dijit.form._SliderBarMover.apply(this, arguments);
            this.widget = _self;
        };
        dojo.extend(barMover, dijit.form._SliderBarMover.prototype);
        this._movableBar = new dojo.dnd.Moveable(this.progressBar,{mover: barMover});
    },
    
    destroy: function(){
        this.inherited(arguments);
        this._movableMax.destroy();
        this._movableBar.destroy(); 
    },
    
    _onKeyPress: function(/*Event*/ e){
        if(this.disabled || this.readOnly || e.altKey || e.ctrlKey){ return; }
        var focusedEl = e.currentTarget;
        var minSelected = false;
        var maxSelected = false;
		var signedChange;
        if (focusedEl == this.sliderHandle){
            minSelected = true;
        }
        else if(focusedEl == this.progressBar){
            maxSelected = true;
            minSelected = true;
        }
        else if(focusedEl == this.sliderHandleMax){
            maxSelected = true;
        }
        switch(e.keyCode){
            case dojo.keys.HOME:
                this._setValueAttr(this.minimum, true, maxSelected);
                break;
            case dojo.keys.END:
                this._setValueAttr(this.maximum, true, maxSelected);
                break;
            // this._descending === false: if ascending vertical (min on top)
            // (this._descending || this.isLeftToRight()): if left-to-right horizontal or descending vertical
            case ((this._descending || this.isLeftToRight()) ? dojo.keys.RIGHT_ARROW : dojo.keys.LEFT_ARROW):
            case (this._descending === false ? dojo.keys.DOWN_ARROW : dojo.keys.UP_ARROW):
            case (this._descending === false ? dojo.keys.PAGE_DOWN : dojo.keys.PAGE_UP):
                if (minSelected && maxSelected){
                    signedChange = Array();
                    signedChange[0] = {'change': e.keyCode == dojo.keys.PAGE_UP ? this.pageIncrement : 1, 'useMaxValue': true};
                    signedChange[1] = {'change': e.keyCode == dojo.keys.PAGE_UP ? this.pageIncrement : 1, 'useMaxValue': false};
                    this._bumpValue(signedChange);
                }
                else if (minSelected){
                    this._bumpValue(e.keyCode == dojo.keys.PAGE_UP ? this.pageIncrement : 1, true);
                }
                else if (maxSelected){
                    this._bumpValue(e.keyCode == dojo.keys.PAGE_UP ? this.pageIncrement : 1);
                }
                break;
            case ((this._descending || this.isLeftToRight()) ? dojo.keys.LEFT_ARROW : dojo.keys.RIGHT_ARROW):
            case (this._descending === false ? dojo.keys.UP_ARROW : dojo.keys.DOWN_ARROW):
            case (this._descending === false ? dojo.keys.PAGE_UP : dojo.keys.PAGE_DOWN):
                if (minSelected && maxSelected){
                    signedChange = Array();
                    signedChange[0] = {'change': e.keyCode == dojo.keys.PAGE_DOWN ? -this.pageIncrement : -1, 'useMaxValue': false};
                    signedChange[1] = {'change': e.keyCode == dojo.keys.PAGE_DOWN ? -this.pageIncrement : -1, 'useMaxValue': true};
                    this._bumpValue(signedChange);
                }
                else if (minSelected){
                    this._bumpValue(e.keyCode == dojo.keys.PAGE_DOWN ? -this.pageIncrement : -1);
                }
                else if (maxSelected){
                    this._bumpValue(e.keyCode == dojo.keys.PAGE_DOWN ? -this.pageIncrement : -1, true);
                }
                break;
            default:
                dijit.form._FormValueWidget.prototype._onKeyPress.apply(this, arguments);
                this.inherited(arguments);
                return;
        }
        dojo.stopEvent(e);
    },
    
    _onHandleClickMax: function(e){
        if(this.disabled || this.readOnly){ return; }
        if(!dojo.isIE){
            // make sure you get focus when dragging the handle
            // (but don't do on IE because it causes a flicker on mouse up (due to blur then focus)
            dijit.focus(this.sliderHandleMax);
        }
        dojo.stopEvent(e);
    },
    
    _onClkIncBumper: function(){
        this._setValueAttr(this._descending === false ? this.minimum : this.maximum, true, true);
    },
    
    _bumpValue: function(signedChange, useMaxValue){
				var value;
        // we pass an array to _setValueAttr when signedChange is an array
        if(!dojo.isArray(signedChange)){
            value = this._getBumpValue(signedChange, useMaxValue);
        }
        else{
            value = Array();
            value[0] = this._getBumpValue(signedChange[0]['change'], signedChange[0]['useMaxValue']);
            value[1] = this._getBumpValue(signedChange[1]['change'], signedChange[1]['useMaxValue']);
        }
        this._setValueAttr(value, true, !dojo.isArray(signedChange) && ((signedChange > 0 && !useMaxValue) || (useMaxValue && signedChange < 0)));
    },
    
    _getBumpValue: function(signedChange, useMaxValue){
        var s = dojo.getComputedStyle(this.sliderBarContainer);
        var c = dojo._getContentBox(this.sliderBarContainer, s);
        var count = this.discreteValues;
        if(count <= 1 || count == Infinity){ count = c[this._pixelCount]; }
        count--;
        var myValue = !useMaxValue ? this.value[0] : this.value[1];
        if((this._isReversed() && signedChange < 0) || (signedChange > 0 && !this._isReversed())){
            myValue = !useMaxValue ? this.value[1] : this.value[0];
        }
        var value = (myValue - this.minimum) * count / (this.maximum - this.minimum) + signedChange;
        if(value < 0){ value = 0; }
        if(value > count){ value = count; }
        return value * (this.maximum - this.minimum) / count + this.minimum;
    },
    
    _onBarClick: function(e){
        if(this.disabled || this.readOnly){ return; }
        if(!dojo.isIE){
            // make sure you get focus when dragging the handle
            // (but don't do on IE because it causes a flicker on mouse up (due to blur then focus)
            dijit.focus(this.progressBar);
        }
        dojo.stopEvent(e);  
    },
    
    _onRemainingBarClick: function(e){
        if(this.disabled || this.readOnly){ return; }
        if(!dojo.isIE){
            // make sure you get focus when dragging the handle
            // (but don't do on IE because it causes a flicker on mouse up (due to blur then focus)
            dijit.focus(this.progressBar);
        }
        // now we set the min/max-value of the slider!
        var abspos = dojo.coords(this.sliderBarContainer, true);
        var bar = dojo.coords(this.progressBar, true);
        var relMousePos = e[this._mousePixelCoord] - abspos[this._startingPixelCoord];
        var leftPos = bar[this._startingPixelCount];
        var rightPos = bar[this._startingPixelCount] + bar[this._pixelCount];
        var isMaxVal = this._isReversed() ? relMousePos <= leftPos : relMousePos >= rightPos;
        this._setPixelValue(this._isReversed() ? (abspos[this._pixelCount]-relMousePos) : relMousePos, abspos[this._pixelCount], true, isMaxVal);
        dojo.stopEvent(e);
    },
    
    _setPixelValue: function(/*Number*/ pixelValue, /*Number*/ maxPixels, /*Boolean*/ priorityChange, /*Boolean*/ isMaxVal){
        if(this.disabled || this.readOnly){ return; }
        var myValue = this._getValueByPixelValue(pixelValue, maxPixels);
        this._setValueAttr(myValue, priorityChange, isMaxVal);
    },
    
    _getValueByPixelValue: function(/*Number*/ pixelValue, /*Number*/ maxPixels){
        pixelValue = pixelValue < 0 ? 0 : maxPixels < pixelValue ? maxPixels : pixelValue;
        var count = this.discreteValues;
        if(count <= 1 || count == Infinity){ count = maxPixels; }
        count--;
        var pixelsPerValue = maxPixels / count;
        var wholeIncrements = Math.round(pixelValue / pixelsPerValue);
        return (this.maximum-this.minimum)*wholeIncrements/count + this.minimum;
    },
    
    _setValueAttr: function(/*Array or Number*/ value, /*Boolean, optional*/ priorityChange, /*Boolean, optional*/ isMaxVal){
        // we pass an array, when we move the slider with the bar
        var actValue = this.value;
        if(!dojo.isArray(value)){
            if(isMaxVal){
                if (this._isReversed()){
                    actValue[0] = value;
                }
                else{
                    actValue[1] = value;
                }
            }
            else{
                if(this._isReversed()){
                    actValue[1] = value;
                }
                else{
                    actValue[0] = value;
                }
            }
        }
        else{
            actValue = value;
        }
        // we have to reset this values. don't know the reason for that
        this._lastValueReported = "";
        this.valueNode.value = this.value = value = actValue;
        dijit.setWaiState(this.focusNode, "valuenow", actValue[0]);
        dijit.setWaiState(this.focusNodeMax, "valuenow", actValue[1]);
        if(this._isReversed()){
            this.value.sort(function(a, b){
               return b-a; 
            });
        }
        else{
            this.value.sort(function(a, b){
                return a-b;
            });
        }
        // not calling the _setValueAttr-function of dijit.form.Slider, but the super-super-class (needed for the onchange-event!)
        dijit.form._FormValueWidget.prototype._setValueAttr.apply(this, arguments);
        this._printSliderBar(priorityChange, isMaxVal);
    },
    
    _printSliderBar: function(priorityChange, isMaxVal){
        var percentMin = (this.value[0] - this.minimum) / (this.maximum - this.minimum);
        var percentMax = (this.value[1] - this.minimum) / (this.maximum - this.minimum);
        var percentMinSave = percentMin;
        if(percentMin > percentMax){
            percentMin = percentMax;
            percentMax = percentMinSave;
        }
        var sliderHandleVal = this._isReversed() ? ((1-percentMin)*100) : (percentMin * 100);
        var sliderHandleMaxVal = this._isReversed() ? ((1-percentMax)*100) : (percentMax * 100);
        var progressBarVal = this._isReversed() ? ((1-percentMax)*100) : (percentMin * 100);
        if (priorityChange && this.slideDuration > 0 && this.progressBar.style[this._progressPixelSize]){
            // animate the slider
            var percent = isMaxVal ? percentMax : percentMin;
            var _this = this;
            var props = {};
            var start = parseFloat(this.progressBar.style[this._handleOffsetCoord]);
            var duration = this.slideDuration / 10; // * (percent-start/100);
            if(duration === 0){ return; }
            if(duration < 0){ duration = 0 - duration; }
            var propsHandle = {};
            var propsHandleMax = {};
            var propsBar = {};
            // hui, a lot of animations :-)
            propsHandle[this._handleOffsetCoord] = { start: this.sliderHandle.style[this._handleOffsetCoord], end: sliderHandleVal, units:"%"};
            propsHandleMax[this._handleOffsetCoord] = { start: this.sliderHandleMax.style[this._handleOffsetCoord], end: sliderHandleMaxVal, units:"%"};
            propsBar[this._handleOffsetCoord] = { start: this.progressBar.style[this._handleOffsetCoord], end: progressBarVal, units:"%"};
            propsBar[this._progressPixelSize] = { start: this.progressBar.style[this._progressPixelSize], end: (percentMax - percentMin) * 100, units:"%"};
            var animHandle = dojo.animateProperty({node: this.sliderHandle,duration: duration, properties: propsHandle}); 
            var animHandleMax = dojo.animateProperty({node: this.sliderHandleMax,duration: duration, properties: propsHandleMax}); 
            var animBar = dojo.animateProperty({node: this.progressBar,duration: duration, properties: propsBar});
            var animCombine = dojo.fx.combine([animHandle, animHandleMax, animBar]);
            animCombine.play();
        }
        else{
            this.sliderHandle.style[this._handleOffsetCoord] = sliderHandleVal + "%";
            this.sliderHandleMax.style[this._handleOffsetCoord] = sliderHandleMaxVal + "%";
            this.progressBar.style[this._handleOffsetCoord] = progressBarVal + "%";
            this.progressBar.style[this._progressPixelSize] = ((percentMax - percentMin) * 100) + "%";
        }
    }
});

dojo.declare("dijit.form._SliderMoverMax",
    dijit.form._SliderMover,
{   
    onMouseMove: function(e){
        var widget = this.widget;
        var abspos = widget._abspos;
        if(!abspos){
            abspos = widget._abspos = dojo.coords(widget.sliderBarContainer, true);
            widget._setPixelValue_ = dojo.hitch(widget, "_setPixelValue");
            widget._isReversed_ = widget._isReversed();
        }
        var pixelValue = e[widget._mousePixelCoord] - abspos[widget._startingPixelCoord];
        widget._setPixelValue_(widget._isReversed_ ? (abspos[widget._pixelCount]-pixelValue) : pixelValue, abspos[widget._pixelCount], false, true);
    },
    
    destroy: function(e){
        dojo.dnd.Mover.prototype.destroy.apply(this, arguments);
        var widget = this.widget;
        widget._abspos = null;
        widget._setValueAttr(widget.value, true);
    }
});

dojo.declare("dijit.form._SliderBarMover",
    dojo.dnd.Mover,
{
    onMouseMove: function(e){
        var widget = this.widget;
        if(widget.disabled || widget.readOnly){ return; }
        var abspos = widget._abspos;
        var bar = widget._bar;
        var mouseOffset = widget._mouseOffset;
        if(!abspos){
            abspos = widget._abspos = dojo.coords(widget.sliderBarContainer, true);
            widget._setPixelValue_ = dojo.hitch(widget, "_setPixelValue");
            widget._getValueByPixelValue_ = dojo.hitch(widget, "_getValueByPixelValue");
            widget._isReversed_ = widget._isReversed();
        }
        if(!bar){
            bar = widget._bar = dojo.coords(widget.progressBar, true);
        }
        if(!mouseOffset){
            mouseOffset = widget._mouseOffset = e[widget._mousePixelCoord] - abspos[widget._startingPixelCoord] - bar[widget._startingPixelCount];
        }
        var pixelValueMin = e[widget._mousePixelCoord] - abspos[widget._startingPixelCoord] - mouseOffset;
        var pixelValueMax = e[widget._mousePixelCoord] - abspos[widget._startingPixelCoord] - mouseOffset + bar[widget._pixelCount];
        // we don't narrow the slider when it reaches the bumper!
        // maybe there is a simpler way
        var pixelValues = [pixelValueMin, pixelValueMax];
        pixelValues.sort(function(a, b){
            return a-b;
        });
        if(pixelValues[0] <= 0){
            pixelValues[0] = 0;
            pixelValues[1] = bar[widget._pixelCount];
        }
        if(pixelValues[1] >= abspos[widget._pixelCount]){
            pixelValues[1] = abspos[widget._pixelCount];
            pixelValues[0] = abspos[widget._pixelCount] - bar[widget._pixelCount];
        }
        // getting the real values by pixel
        var myValues = [widget._getValueByPixelValue(widget._isReversed_ ? (abspos[widget._pixelCount] - pixelValues[0]) : pixelValues[0], abspos[widget._pixelCount]), 
                        widget._getValueByPixelValue(widget._isReversed_ ? (abspos[widget._pixelCount] - pixelValues[1]) : pixelValues[1], abspos[widget._pixelCount])];
        // and setting the value of the widget
        widget._setValueAttr(myValues, false, false);
    },
    
    destroy: function(e){
        dojo.dnd.Mover.prototype.destroy.apply(this, arguments);
        var widget = this.widget;
        widget._abspos = null;
        widget._bar = null;
        widget._mouseOffset = null;
        widget._setValueAttr(widget.value, true);
    }
});

dojo.declare(
    "dojox.form.HorizontalRangeSlider",
    [dijit.form.HorizontalSlider, dojox.form._RangeSliderMixin],
    {
        templatePath: dojo.moduleUrl('dojox.form','resources/HorizontalRangeSlider.html')
    }
);

dojo.declare(
    "dojox.form.VerticalRangeSlider",
    [dijit.form.VerticalSlider, dojox.form._RangeSliderMixin],
    {
        // summary
        // A form widget that allows one to select a range with two vertically draggable images
        templatePath: dojo.moduleUrl('dojox.form','resources/VerticalRangeSlider.html')
    }
);
