
var MarinoCarousel = function(viewport, panels, options) {
    var viewport, panels, tray, slots = [], options, index = 0, busy = false, executor, pos_offset = 0, rotations = 0, tray_offsets = [], selector_buttons = [], tray_panels = [], tray_panel_content = [];
    var init = function() {
        options = set_default(options, {});
        options.width = set_default(options.width, 0);
        options.height = set_default(options.height, 0);
        options.direction = set_default(options.direction, 'horizontal');
        options.duration = set_default(options.duration, 1);
        options.interval = set_default(options.interval, 1);
        options.autostart = set_default(options.autostart, true);
        options.rotations = set_default(options.rotations, 0);
        options.initial = set_default(options.initial, 0);

        /* AS: check to make sure initial isn't greater than num of panels */ 
        if (options.initial <= panels.size()) {
            index = options.initial;
        } else {
            index = 0;
        }

        if (options.width == 0)
            options.width = viewport.getWidth();
        if (options.height == 0)
            options.height = viewport.getHeight();
        if (options.direction != 'horizontal' && options.direction != 'vertical')
            throw 'direction must be horizontal or vertical (default is horizontal)';

        // calc tray dimensions and store panel deltas and offsets
        var biggest_height = 0;
        var biggest_width = 0;
        var total_height = 0;
        var total_width = 0;
        var tray_size = 0;
        var idx = 0;
        tray_offsets[idx] = 0;
        if (options.reload_panel_content) {
            tray_panels[idx] = [];
            tray_panel_content[idx] = [];
        }
        for (var i = 0; i < panels.size(); i++) {
            var pw = panels[i].getWidth();
            var ph = panels[i].getHeight();

            if (ph > biggest_height)
                biggest_height = ph;
            if (pw > biggest_width)
                biggest_width = pw;

            // if tray length is going to be bigger than the viewport, start a new tray
            if (options.direction == 'vertical') {
                if (tray_size + ph > options.height) {
                    tray_offsets[idx + 1] = tray_offsets[idx] + tray_size;
                    idx++;
                    if (options.reload_panel_content) {
                        tray_panels[idx] = [];
                        tray_panel_content[idx] = []
                    }
                    tray_size = 0;
                }
                tray_size += ph;
            } else {
                if (tray_size + pw > options.width) {
                    tray_offsets[idx + 1] = tray_offsets[idx] + tray_size;
                    idx++;
                    if (options.reload_panel_content) {
                        tray_panels[idx] = [];
                        tray_panel_content[idx] = [];
                    }
                    tray_size = 0;
                }
                tray_size += pw;
            }
            if (options.reload_panel_content) {
                tray_panels[idx].push(i);
                tray_panel_content[idx].push(panels[i].innerHTML);
            }
            total_height += ph;
            total_width += pw;
        }
        if (options.direction == 'vertical') {
            if (tray_size < options.height && panels.size() > 1) {
                tray_offsets[tray_offsets.size() - 1] -= (options.height - tray_size)
            }
        } else {
            if (tray_size < options.width && panels.size() > 1) {
                tray_offsets[tray_offsets.size() - 1] -= (options.width - tray_size)
            }
        }

        // create tray
        var w = 0;
        var h = 0;
        if (options.direction == 'vertical') {
            w = biggest_width;
            h = total_height;
        } else {
            w = total_width;
            h = biggest_height;
        }
        var t = 0, l = 0;
        if (options.direction == 'horizontal') {
            l = (options.width * index) * -1;
        } else {
            t = (options.height * index) * -1;
        }
        log('t: ' + t);
        log('l: ' + l);
        log('display:block;width:' + w + 'px;height:' + h + 'px;top:' + t + ';left:' + l + 'px;');
        tray = new Element('div', { 'class': 'tray', 'style': 'display:block;position:relative;width:' + w + 'px;height:' + h + 'px;top:' + t + ';left:' + l + 'px;' });
        //                                                 display:block;position:relative;width:942px;  height:840px;  top:-210px;left:0px;
        viewport.insert(tray);
        viewport.style.overflow = 'hidden';
        viewport.style.position = 'relative';
        viewport.style.width = options.width;
        viewport.style.height = options.height;

        tray.style.top = t + 'px';
        tray.style.left = l + 'px';
        pos_offset = tray_offsets[index];

        // add panels to tray
        for (var i = 0; i < panels.size(); i++) {
            tray.insert(new Element('div', { 'style': 'float:left;display:block' }).insert(panels[i]));
        }

        if (options.previous_button && tray_offsets.size() > 1) {
            log('adding previous button functionality');
            options.previous_button.each(function(button) {
                button.observe('click', previous);
                button.removeClassName('inactive');
                button.addClassName('active');
            });
        }
        if (options.next_button && tray_offsets.size() > 1) {
            log('adding next button functionality');
            options.next_button.each(function(button) {
                button.observe('click', next);
                button.removeClassName('inactive');
                button.addClassName('active');
            });
        }
        if (options.selector) {
            log('adding jump_to functionality');
            var btn_idx = 0;
            var ul = new Element('ul', { 'class': 'c_selector' });
            for (var i = 0; i < tray_offsets.size(); i++) {
                var css = 'c_btn ';
                if (i == index) css += 'active ';
                else css += 'inactive ';
                if (i == 0) css += 'first ';
                if (i == tray_offsets.size() - 1) css += 'last ';
                if (tray_offsets.size() == 1) css += 'single ';
                selector_buttons[i] = new Element('li', { 'class': css });
                selector_buttons[i].btn_idx = btn_idx;
                selector_buttons[i].observe('click', function(e) {
                    var el = Event.element(e);
                    if (typeof (e) != 'undefined' && typeof (e.stop) != 'undefined') {
                        e.stop();
                    }
                    jump_to(el.btn_idx);
                } .bind(this));
                ul.insert(selector_buttons[i]);
                btn_idx++;
            }
            options.selector.insert(ul);
        }
        if (options.autostart)
            start();
    }

    var start = function(e) {
        if (typeof (e) != 'undefined' && typeof (e.stop) != 'undefined') {
            e.stop();
        }
        if (!executor || !executor.timer) {
            log('start');
            executor = new PeriodicalExecuter(auto_next, options.interval + options.duration);
        }
    }
    var stop = function(e) {
        if (typeof (e) != 'undefined' && typeof (e.stop) != 'undefined') {
            e.stop();
        }
        if (executor && executor.timer) {
            log('stop');
            executor.stop();
        }
    }

    var auto_next = function() {
        log('auto_next');
        move_to((index + 1) == tray_offsets.size() ? 0 : (index + 1));
    }

    var next = function(e) {
        log('next');
        if (typeof (e) != 'undefined' && typeof (e.stop) != 'undefined') {
            e.stop();
        }
        jump_to((index + 1) == tray_offsets.size() ? 0 : (index + 1));
    }

    var previous = function(e) {
        log('previous');
        if (typeof (e) != 'undefined' && typeof (e.stop) != 'undefined') {
            e.stop();
        }
        jump_to((index == 0) ? tray_offsets.size() - 1 : index - 1);
    }

    var jump_to = function(new_index) {
        log('jump_to');
        stop(); // stops any periodical executors
        move_to(new_index);
    }

    var move_to = function(new_index) {
        if (busy || new_index == index || new_index < 0 || new_index >= tray_offsets.size() || typeof (new_index) == "undefined") {
            return;
        }
        var delta = tray_offsets[new_index] - pos_offset;
        var xdelta = 0;
        var ydelta = 0;
        switch (options.direction) {
            case 'vertical':
                ydelta = delta * -1;
                break;
            case 'right':
                xdelta = delta;
                break;
            case 'horizontal':
                xdelta = delta * -1;
                break;
        }


        if (options.selector) {
            selector_buttons[index].removeClassName('active');
            selector_buttons[index].addClassName('inactive');
            selector_buttons[new_index].removeClassName('inactive');
            selector_buttons[new_index].addClassName('active');
        }

        busy = true;
        index = new_index;
        if (options.reload_panel_content) {
            reload_panels_in_tray(new_index);
        }
        new Effect.Move(tray, { x: xdelta, y: ydelta, mode: 'relative', afterFinish: finishMovement, duration: options.duration });
    }

    var finishMovement = function() {
        pos_offset = tray_offsets[index];
        busy = false;
    }

    var reload_panels_in_tray = function(idx) {
        for (var i = 0; i < tray_panels[idx].size(); i++) {
            panels[tray_panels[idx][i]].update(tray_panel_content[idx][i]);
        }
    }

    var id = function() {
        return viewport.id;
    }

    var log = function(s) {
        if (typeof (console) != "undefined" && typeof (console.log) != "undefined")
            console.log('MarinoCarousel:' + id() + ':' + s);
    }

    var set_default = function(obj, default_value) {
        if (typeof (obj) == 'undefined') {
            return default_value;
        }
        return obj;
    }

    init();
    return {
        start: start,
        stop: stop,
        next: next,
        previous: previous,
        jump_to: jump_to,
        id: id
    };
}


