'use strict';

/*@
Ox.Progressbar <f> Progress Bar
    ([options[, self]]) -> <o:Ox.Element> Progress Bar
        cancel <!> cancelled
        complete <!> completed
        pause <!> paused
        restart <!> restart
        resume <!> resumed
    options <o|{}> Options object
        cancelled <b|false> If true, progress bar is cancelled
        paused <b|false> If true, progress bar is paused
        progress <n|0> Progress, float between 0 and 1, or -1 for indeterminate
        showCancelButton <b|false> If true, show cancel button
        showPauseButton <b|false> If true, show pause button
        showPercent <b|false> If true, show progress in percent
        showRestartButton <b|false> If true, show restart button
        showTime <b|false> If true, show remaining time
        showTooltips <b|false> If true, buttons have tooltips
        width <n|256> Width in px
    self <o|{}> Shared private variable
@*/

Ox.Progressbar = function(options, self) {

    self = self || {};
    var that = Ox.Element({}, self)
        .defaults({
            cancelled: false,
            paused: false,
            progress: 0,
            showCancelButton: false,
            showPauseButton: false,
            showPercent: false,
            showRestartButton: false,
            showTime: false,
            showTooltips: false,
            width: 256
        })
        .options(options || {})
        .update({
            cancelled: toggleCancelled,
            paused: togglePaused,
            progress: function() {
                self.options.progress = Ox.limit(self.options.progress, 0, 1);
                !self.options.paused && !self.options.cancelled && setProgress(true);
            }
        })
        .addClass('OxProgressbar')
        .css({width: self.options.width - 2 + 'px'});

    self.indeterminate = self.options.progress == -1;

    self.trackWidth = self.options.width
        - self.options.showPercent * 36
        - self.options.showTime * 60
        - self.options.showPauseButton * 16
        - self.options.showCancelButton * 16;

    self.$track = $('<div>')
        .addClass('OxTrack')
        .css({
            width: self.trackWidth - 2 + 'px'
        })
        .appendTo(that);

    self.$progress = $('<div>')
        .addClass('OxProgress')
        .appendTo(self.$track);

    if (self.options.showPercent) {
        self.$percent = $('<div>')
            .addClass('OxText')
            .css({width: '36px'})
            .appendTo(that);
    }

    if (self.options.showTime) {
        self.$time = $('<div>')
            .addClass('OxText')
            .css({width: '60px'})
            .appendTo(that);
    }

    if (self.options.showPauseButton) {
        self.$pauseButton = Ox.Button({
                style: 'symbol',
                tooltip: self.options.showTooltips ? ['Pause', 'Resume'] : '',
                type: 'image',
                value: !self.options.paused ? 'pause' : 'redo',
                values: ['pause', 'redo']
            })
            .bindEvent({
                click: togglePaused
            })
            .appendTo(that);
    }

    if (self.options.showCancelButton) {
        self.$cancelButton = Ox.Button(Ox.extend({
                style: 'symbol',
                type: 'image'
            }, self.options.showRestartButton ? {
                tooltip: self.options.showTooltips ? ['Cancel', 'Restart'] : '',
                value: 'close',
                values: ['close', 'redo']
            } : {
                title: 'close',
                tooltip: self.options.showTooltips ? 'Cancel' : ''
            }))
            .bindEvent({
                click: toggleCancelled
            })
            .appendTo(that);
    }

    setProgress();

    !self.options.paused && resume();

    function cancel() {
        self.options.cancelled = true;
        if (self.options.paused) {
            self.options.paused = false;
            self.$pauseButton && self.$pauseButton.toggle();
        }
        stop();
        that.triggerEvent('cancel');
    }

    function complete() {
        self.complete = true;
        stop();
        self.paused = false;
        that.triggerEvent('complete');
    }

    function pause() {
        self.pauseTime = +new Date();
        self.$progress.removeClass('OxAnimate');
        ($.browser.mozilla || $.browser.opera) && clearInterval(self.interval);
        self.$time && self.$time.html(
            self.options.cancelled ? 'Cancelled' : 'Paused'
        );
    }

    function restart() {
        self.options.cancelled = false;
        if (!self.indeterminate) {
            self.options.progress = 0;
        }
        delete self.startTime;
        self.$pauseButton && self.$pauseButton.options({disabled: false});
        setProgress();
        resume();
        that.triggerEvent('restart');
    }

    function resume() {
        self.startTime = !self.startTime
            ? +new Date()
            : self.startTime + +new Date() - self.pauseTime;
        self.$progress.addClass('OxAnimate');
        if ($.browser.mozilla || $.browser.opera) {
            self.offset = 0;
            self.interval = setInterval(function() {
                self.$progress.css({backgroundPosition: --self.offset + 'px 0, 0 0'})
            }, 1000 / 32);
        }
        self.$time && self.$time.html(
            self.options.progress ? Ox.formatDuration(that.status().remaining) : 'unknown'
        );
    }

    function setProgress(animate) {
        self.$percent && self.$percent.html(
            Math.floor(self.options.progress * 100) + '%'
        );
        self.$time && self.$time.html(
            self.options.progress ? Ox.formatDuration(that.status().remaining) : 'unknown'
        );
        self.$progress.stop().animate({
            width: Math.round(14 + Math.abs(self.options.progress) * (self.trackWidth - 16)) + 'px'
        }, animate ? 250 : 0, function() {
            self.options.progress == 1 && complete();
        });
    }

    function stop() {
        pause();
        self.$time && self.$time.html(
            self.options.cancelled ? 'Cancelled' : 'Complete'
        );
        if (self.$pauseButton && (self.options.cancelled || self.complete)) {
            self.$pauseButton.options({disabled: true});
        }
        if (self.$cancelButton && (self.complete || !self.options.showRestartButton)) {
            self.$cancelButton.options({disabled: true});
        }
    }

    function toggleCancelled(e) {
        if (e) {
            self.options.cancelled = !self.options.cancelled;
        } else if (self.$cancelButton) {
            self.$cancelButton.toggle();
        }
        self.options.cancelled ? cancel() : restart();
        that.triggerEvent(self.options.cancelled ? 'cancel' : 'restart');
    }

    function togglePaused(e) {
        if (e) {
            self.options.paused = !self.options.paused;
        } else if (self.$pauseButton) {
            self.$pauseButton.toggle();
        }
        self.options.paused ? pause() : resume();
        that.triggerEvent(self.options.paused ? 'pause' : 'resume');
    }

    /*@
    that.status <f> Returns time elapsed / remaining
        () -> <o> status
    @*/
    that.status = function() {
        var elapsed = +new Date() - self.startTime,
            remaining = elapsed / self.options.progress * (1 - self.options.progress);
        return {
            elapsed: Math.floor(elapsed / 1000),
            remaining: self.options.progress
                ? Math.ceil(remaining / 1000)
                : Infinity
        };
    };

    return that;

};
