/**
 * Roar - Notifications
 *
 * Inspired by Growl
 *
 * @version     1.0.1
 *
 * @license     MIT-style license
 * @author      Harald Kirschner <mail [at] digitarald.de>
 * @copyright   Author
 */

var Roar = new Class({

    Implements: [Options, Events, Chain],

    options: {
        duration: 4000,
        position: 'upperLeft',
        container: null,
        bodyFx: null,
        itemFx: null,
        margin: {x: 10, y: 10},
        offset: 5,
        className: 'roar',
        onShow: $empty,
        onHide: $empty,
        onRender: $empty,
        style: 'notice'
    },

    initialize: function(options) {
        this.setOptions(options);
        this.items = [];
        this.container = $(this.options.container) || document;
    },

    alert: function(title, message, options) {
        var params = Array.link(arguments, {title: String.type, message: String.type, options: Object.type});
        var items = [new Element('h3', {'html': $pick(params.title, '')})];
        if (params.message) items.push(new Element('p', {'html': params.message}));
        return this.inject(items, params.options);
    },

    inject: function(elements, options) {
        if (!this.body) this.render();
        options = options || {};

        var offset = [-this.options.offset, 0];
        var last = this.items.getLast();
        if (last) {
            offset[0] = last.retrieve('roar:offset');
            offset[1] = offset[0] + last.offsetHeight + this.options.offset;
        }
        var to = {'opacity': 1};
        to[this.align.y] = offset;

        var item = new Element('div', {
            'class': this.options.className,
            'opacity': 0
        }).adopt(
            new Element('div', {
                'class': this.options.className+'-bg',
                'opacity': 0.7
            }),
            elements
        );

        item.setStyle(this.align.x, 0).store('roar:offset', offset[1]).set('morph', $merge({
            unit: 'px',
            link: 'cancel',
            onStart: Chain.prototype.clearChain,
            transition: Fx.Transitions.Back.easeOut
        }, this.options.itemFx));

        var remove = this.remove.create({
            bind: this,
            arguments: [item],
            delay: 10
        });
        this.items.push(item.addEvent('click', remove));

        if (this.options.duration) {
            var over = false;
            var trigger = (function() {
                trigger = null;
                if (!over) remove();
            }).delay(this.options.duration);
            item.addEvents({
                mouseover: function() {
                    over = true;
                },
                mouseout: function() {
                    over = false;
                    if (!trigger) remove();
                }
            });
        }
        item.inject(this.body).morph(to);
        return this.fireEvent('onShow', [item, this.items.length]);
    },

    remove: function(item) {
        var index = this.items.indexOf(item);
        if (index == -1) return this;
        this.items.splice(index, 1);
        item.removeEvents();
        var to = {opacity: 0};
        to[this.align.y] = item.getStyle(this.align.y).toInt() - item.offsetHeight - this.options.offset;
        item.morph(to).get('morph').chain(item.destroy.bind(item));
        return this.fireEvent('onHide', [item, this.items.length]).callChain(item);
    },

    empty: function() {
        while (this.items.length) this.remove(this.items[0]);
        return this;
    },

    render: function() {
        this.position = this.options.position;
        if ($type(this.position) == 'string') {
            var position = {x: 'center', y: 'center'};
            this.align = {x: 'left', y: 'top'};
            if ((/left|west/i).test(this.position)) position.x = 'left';
            else if ((/right|east/i).test(this.position)) this.align.x = position.x = 'right';
            if ((/upper|top|north/i).test(this.position)) position.y = 'top';
            else if ((/bottom|lower|south/i).test(this.position)) this.align.y = position.y = 'bottom';
            this.position = position;
        }
        this.body = new Element('div', {'class': this.options.className+'-body'}).inject(document.body);
        if (Browser.Engine.trident4) this.body.addClass(this.options.className+'-body-ugly');
        this.moveTo = this.body.setStyles.bind(this.body);
//        this.reposition();
        if (this.options.bodyFx) {
            var morph = new Fx.Morph(this.body, $merge({
                unit: 'px',
                chain: 'cancel',
                transition: Fx.Transitions.Circ.easeOut
            }, this.options.bodyFx));
            this.moveTo = morph.start.bind(morph);
        }
//        var repos = this.reposition.bind(this);
//        window.addEvents({
//            scroll: repos,
//            resize: repos
//        });
        this.fireEvent('onRender', this.body);
    },

    reposition: function() {
        var max = document.getCoordinates(), scroll = document.getScroll(), margin = this.options.margin;
        max.left += scroll.x;
        max.right += scroll.x;
        max.top += scroll.y;
        max.bottom += scroll.y;
        var rel = ($type(this.container) == 'element') ? this.container.getCoordinates() : max;
        this.moveTo({
            left: (this.position.x == 'right')
                ? (Math.min(rel.right, max.right) - margin.x)
                : (Math.max(rel.left, max.left) + margin.x),
            top: (this.position.y == 'bottom')
                ? (Math.min(rel.bottom, max.bottom) - margin.y)
                : (Math.max(rel.top, max.top) + margin.y)
        });
    },
    
    setClassName: function(className) {
        this.options.className = className;
    }

});
