function destino_EventDispatcher(target) {
    this.listeners = {};
    target.eventDispatcher = this;
    target.addEventListener = this.add;
    target.removeEventListener = this.remove;
    target.dispatchEvent = this.dispatchEvent;
};
destino_EventDispatcher.prototype = {
    listeners: null,
    add: function (eventType, handler) {
        if (this.eventDispatcher.listeners[eventType] == null)
            this.eventDispatcher.listeners[eventType] = [];

        this.eventDispatcher.listeners[eventType].push(handler);
    },
    remove: function (eventType, handler) {
        if (this.eventDispatcher.listeners[eventType]) {
            var targetIndex = this.eventDispatcher.listeners.indexOf(handler);
            if (targetIndex != -1) {
                this.eventDispatcher.listeners.splice(targetIndex, 1);
            }
        }
    },
    dispatchEvent: function (event) {
    //todo make scope safe
        var listenerList = this.listeners[event.type];
        if (listenerList != null) {
            for (var i = 0; i < listenerList.length; i += 1) {
                listenerList[i](event);
            }
        }
    }
};

function Gallery(id, delay, width, height) {
    new destino_EventDispatcher(this);
    this.items = [];
    this.idlist = [];

    if (id != null) {
        this.delay = delay || 4000;
        this.create(id, width, height);
        this.start();
    }
    
};
Gallery.prototype = {
    lastVisible: undefined,
    items: [],
    index: 0,

    intervalId: 0,
    self: this,
    target: null,

    listIndex: 0,
    idlist: null,

    contentWidth: 0,
    contentHeight: 0,

    delay: 4000,

    animator: function (from, to, doneCallback) {
        $(to).show();
        $(from).hide();

        doneCallback();
    },
    create: function (id, width, height) {
        var tag = $(id)[0];
        if (tag.nodeName !== "UL") {
            tag = $("ul", id)[0];
        }
        this.target = tag;

        var itms = [];

        this.idlist = [];

        for (var i = 0; i < tag.childNodes.length; i++) {
            var child = tag.childNodes[i];
            if (child.nodeName === "LI") {
                $(child).css({ position: "absolute", left: 0, top: 0 });
                itms.push(child);

                this.idlist.push(this.idlist.length);
            }
        }

        this.contentWidth = width || $(itms[0]).width();
        this.contentHeight = height || $(itms[0]).height();

        $(tag).css({ position: "relative", overflow: "hidden", listStyle: "none", width: this.contentWidth, height: this.contentHeight });

        this.lastVisible = itms[0];
        for (var i = 1; i < itms.length; i++) {
            this.target.removeChild(itms[i]);
        }

        this.items = itms;
    },
    start: function () {
        this.stop();
        this.intervalId = setTimeout(function (that) { return function () { that.next() }; } (this), this.delay);
        this.eventDispatcher.dispatchEvent({ type: "galleryStarted", data: this.index });
    },
    stop: function () {
        clearInterval(this.intervalId);
        this.eventDispatcher.dispatchEvent({ type: "galleryStopped", data: this.index });
    },
    next: function () {
        this.changeImage(this.idlist[(this.listIndex + 1) % this.idlist.length]);
    },
    previous: function () {
        this.changeImage(this.idlist[this.listIndex == 0 ? this.idlist.length - 1 : this.listIndex - 1]);
    },
    changeImage: function (newIndex) {
        if (newIndex === this.index) {
            return;
        }

        var animationDirection;
        var dirOffset = 1;
        var itemCount = this.items.length;

        while (animationDirection === undefined && dirOffset < 25) {
            if (smartMOD(this.index - dirOffset, itemCount) === newIndex) {
                animationDirection = "backward";
            } else if (smartMOD(this.index + dirOffset, itemCount) === newIndex) {
                animationDirection = "forward";
            }
            dirOffset++;
        }

        this.index = newIndex;
        this.listIndex = this.idlist.indexOf(this.index);

        var from = this.lastVisible;
        var to = this.items[this.index];

        this.target.appendChild(to);

        this.lastVisible = this.items[this.index];
        this.animator(from, to, animationDirection, function (that) {
            return function () { that.target.removeChild(from); };
        } (this));

        this.start();
        this.eventDispatcher.dispatchEvent({ type: "imageChanged", data: this.index });
    },
    randomize: function () {
        var nls = null;
        var like = true;

        while (like) {
            var nls = this.idlist.concat();
            like = false;

            nls.sort(function (a, b) {
                return Math.ceil((Math.random() * 3) - 2);
            });

            for (var i = 0; i < nls.length; i++) {
                if (nls[i] == this.idlist[i]) {
                    like = true
                    break;
                }
            }
        }

        this.idlist = nls;
    }
};

function DotNavigation(gallery, buttonContainer, normalStyle, selectedStyle) {
    this.create(gallery, buttonContainer, normalStyle, selectedStyle);
};
DotNavigation.prototype = {
    dots: [],
    lastActive: null,
    normalStyle: "dotStandard",
    selectedStyle: "dotHover",
    dotContainer: null,
    target: null,

    create: function (gallery, buttonContainer, normalStyle, selectedStyle) {
        this.dotContainer = buttonContainer;
        this.target = gallery;

        this.normalStyle = normalStyle;
        this.selectedStyle = selectedStyle;

        var childList = $("li", buttonContainer);

        for (var i = 0; i < childList.length; i++) {

            if (gallery.index === i) {
                $(childList[i]).addClass(this.selectedStyle);
                this.lastActive = childList[i];
            } else {
                $(childList[i]).addClass(this.normalStyle);
            }

            $(childList[i]).click(function (id, that) {
                return function () { that.target.changeImage(id); };
            } (i, this));
        };

        this.dots = childList;

        gallery.addEventListener("imageChanged", function (that) { return function (e) { that.change(e.data); } } (this));

    },
    change: function (index) {
        if (this.lastActive != null) {
            $(this.lastActive).removeClass(this.selectedStyle).addClass(this.normalStyle);
        }

        this.lastActive = this.dots[index];

        $(this.lastActive).removeClass(this.normalStyle).addClass(this.selectedStyle);
    }
};

var GalleryEffects = {
    fade: function (from, to, direction, doneCallback) {
        $(from).stop();
        $(to).stop();

        $(to).show();
        $(to).css({ opacity: 0 });
        $(to).animate({ opacity: 1 }, { duration: 600, complete: function () {
            doneCallback();
            }
        });
    },
    slide: function (from, to, direction, doneCallback) {
        $(from).stop();
        $(to).stop();

        var fromDistination = direction === "backward" ? $(to).width() : -$(to).width();

        $(to).css({ left: -fromDistination });
        $(to).animate({ left: 0 }, { duration: 600 });
        $(from).animate({ left: fromDistination }, { duration: 600, complete: function () {
            doneCallback();
            }
        });
    }
};
function smartMOD(value, mod) {
    if (value < 0) {
        return mod + value;
    }
    return value % mod;
}

if(!Array.indexOf){
    Array.prototype.indexOf = function(obj){
        for(var i=0; i<this.length; i++){
            if(this[i]==obj){
                return i;
            }
        }
        return -1;
    }
}
