// vim: et:ts=4:sw=4:sts=4:ft=javascript
/*@
Ox.Range <f:Ox.Element> Range Object
    () ->              <f> Range Object
    (options) ->       <f> Range Object
    (options, self) -> <f> Range Object
    options <o> Options object
        arrows          <b>         if true, show arrows
        arrowStep       <n>          step when clicking arrows
        arrowSymbols    <a>           arrow symbols, like ['minus', 'plus']
        max             <n>          maximum value
        min             <n>          minimum value
        orientation     <s>          'horizontal' or 'vertical'
        step            <n>          step between values
        size            <n>          width or height, in px
        thumbSize       <n>          minimum width or height of thumb, in px
        thumbValue      <b>         if true, display value on thumb
        trackGradient   <a>           colors
        trackImages     <s> or <a> one or multiple track background image URLs
        trackStep       <n>          0 (scroll here) or step when clicking track
        value           <n>          initial value
        valueNames      <a>           value names to display on thumb        
    self    <o> shared private variable
    change <!> triggered on change of the range
@*/

Ox.Range = function(options, self) {

    self = self || {};
    var that = Ox.Element({}, self)
            .defaults({
                arrows: false,
                arrowStep: 1,
                arrowSymbols: ['left', 'right'],
                max: 100,
                min: 0,
                orientation: 'horizontal',
                step: 1,
                size: 128, // fixme: shouldn't this be width?
                thumbSize: 16,
                thumbValue: false,
                trackColors: [],
                trackImages: [],
                trackStep: 0,
                value: 0,
                valueNames: null,
            })
            .options($.extend(options, {
                arrowStep: options.arrowStep ?
                    options.arrowStep : options.step,
                trackImages: $.makeArray(options.trackImages || [])
            }))
            .addClass('OxRange')
            .css({
                width: self.options.size + 'px'
            });

    $.extend(self, {
        trackColors: self.options.trackColors.length,
        trackImages: self.options.trackImages.length,
        values: (self.options.max - self.options.min + self.options.step) /
                self.options.step
    });
    setSizes();

    if (self.options.arrows) {
        self.$arrows = [];
        Ox.range(0, 2).forEach(function(i) {
            self.$arrows[i] = Ox.Button({
                overlap: i == 0 ? 'right' : 'left',
                title: self.options.arrowSymbols[i],
                type: 'image'
            })
            .addClass('OxArrow')
            .bindEvent({
                mousedown: function(event, e) {
                    clickArrow(e, i, true);
                },
                mouserepeat: function(event, e) {
                    clickArrow(e, i, false);
                }
            })
            .appendTo(that.$element);
        });
    }

    self.$track = Ox.Element()
        .addClass('OxTrack')
        .css($.extend({
            width: (self.trackSize - 2) + 'px'
        }, self.trackImages == 1 ? {
            background: 'rgb(0, 0, 0)'
        } : {}))
        .bindEvent({
            mousedown: clickTrack,
            drag: dragTrack
        })
        .appendTo(that.$element);

    self.trackColors && setTrackColors();

    if (self.trackImages) {
        self.$trackImages = $('<div>')
            .css({
                width: self.trackSize + 'px',
                marginRight: (-self.trackSize - 1) + 'px'
            })
            .appendTo(self.$track.$element);
        self.options.trackImages.forEach(function(v, i) {
            //Ox.print(self.trackImageWidths[i])
            $('<img>')
                .attr({
                    src: v
                })
                .addClass(i == 0 ? 'OxFirstChild' : '')
                .addClass(i == self.trackImages - 1 ? 'OxLastChild' : '')
                .css({
                    width: self.trackImageWidths[i] + 'px'
                })
                .mousedown(function(e) {
                    e.preventDefault(); // prevent drag
                })
                .appendTo(self.$trackImages);
            //left += self.trackImageWidths[i];
        });
    }

    self.$thumb = Ox.Button({
            id: self.options.id + 'Thumb',
            title: self.options.thumbValue ? (self.options.valueNames ?
                    self.options.valueNames[self.options.value] :
                    self.options.value) : '',
            width: self.thumbSize
        })
        .addClass('OxThumb')
        /*
        .css({
            border: '1px solid rgb(255, 255, 255)',
            background: 'rgba(0, 0, 0, 0)'
        })
        */
        .appendTo(self.$track);

    setThumb();

    function clickArrow(e, i, animate) {
        // fixme: shift doesn't work, see menu scrolling
        setValue(self.options.value + self.options.arrowStep * (i == 0 ? -1 : 1) * (e.shiftKey ? 2 : 1), animate);
    }

    function clickTrack(event, e) {
        // fixme: thumb ends up a bit too far on the right
        var isThumb = $(e.target).hasClass('OxThumb');
        self.drag = {
            left: self.$track.offset().left,
            offset: isThumb ? e.clientX - self.$thumb.offset().left - 8 /*self.thumbSize / 2*/ : 0
        };
        setValue(getVal(e.clientX - self.drag.left - self.drag.offset), !isThumb);
    }

    function dragTrack(event, e) {
        setValue(getVal(e.clientX - self.drag.left - self.drag.offset))
    }

    function getPx(val) {
        var pxPerVal = (self.trackSize - self.thumbSize) /
                    (self.options.max - self.options.min);
        return Math.ceil((val - self.options.min) * pxPerVal);
    }

    /*
    function getTime(oldValue, newValue) {
        return self.animationTime * Math.abs(oldValue - newValue) / (self.options.max - self.options.min);
    }
    */

    function getVal(px) {
        var px = self.trackSize / self.values >= 16 ? px : px - 8,
            valPerPx = (self.options.max - self.options.min) /
                    (self.trackSize - self.thumbSize);
        return Ox.limit(self.options.min +
                Math.floor(px * valPerPx / self.options.step) * self.options.step,
                self.options.min, self.options.max);
    }

    function setSizes() {
        self.trackSize = self.options.size - self.options.arrows * 32;
        self.thumbSize = Math.max(self.trackSize / self.values, self.options.thumbSize);
        self.trackImageWidths = self.trackImages == 1 ? [self.trackSize - 16] :
            Ox.divideInt(self.trackSize - 2, self.trackImages);
        self.trackColorsStart = self.thumbSize / 2 / self.options.size;
        self.trackColorsStep = (self.options.size - self.thumbSize) /
            (self.trackColors - 1) / self.options.size;
        that.css({
            width: self.options.size + 'px'
        });
        self.$track && self.$track.css({
            width: (self.trackSize - 2) + 'px'
        });
        if (self.$thumb) {
            self.$thumb.options({
                width: self.thumbSize
            });
            setThumb();
        }
    }

    function setThumb(animate) {
        self.$thumb.stop().animate({
            marginLeft: (getPx(self.options.value) - 1) + 'px',
            //width: self.thumbSize + 'px'
        }, animate ? 200 : 0, function() {
            if (self.options.thumbValue) {
                self.$thumb.options({
                    title: self.options.valueNames ?
                            self.options.valueNames[self.options.value] :
                            self.options.value
                });
            }
        });
    }

    function setTrackColors() {
        self.$track.css({
            backgroundImage: $.browser.mozilla ?
                ('-moz-linear-gradient(left, ' +
                self.options.trackColors[0] + ' 0%, ' + $.map(self.options.trackColors, function(v, i) {
                    return v + ' ' + ((self.trackColorsStart + self.trackColorsStep * i) * 100) + '%';
                }).join(', ') + ', ' + self.options.trackColors[self.trackColors - 1] + ' 100%)') :
                ('-webkit-gradient(linear, left top, right top, color-stop(0, ' +
                self.options.trackColors[0] + '), ' + $.map(self.options.trackColors, function(v, i) {
                    return 'color-stop(' + (self.trackColorsStart + self.trackColorsStep * i) + ', ' + v + ')';
                }).join(', ') + ', color-stop(1, ' + self.options.trackColors[self.trackColors - 1] + '))')
        });
    }

    function setValue(value, animate) {
        value = Ox.limit(value, self.options.min, self.options.max);
        if (value != self.options.value) {
            //time = getTime(self.options.value, value);
            self.options.value = value;
            setThumb(animate);
            that.triggerEvent('change', {
                value: value
            });
        }
    }

    self.setOption = function(key, value) {
        if (key == 'size') {
            setSizes();
        } else if (key == 'trackColors') {
            setTrackColors();
        } else if (key == 'value') {
            setThumb();
        }
    }

    return that;

};