try {
    if ( typeof(MochiKit.Base) == 'undefined' ||
         typeof(MochiKit.DOM)  == 'undefined' ||
         typeof(MochiKit.Iter) == 'undefined' ||
         typeof(MochiKit.Visual) == 'undefined' ) {
         throw "MochiKit.SlideShow depends on MochiKit.Base, MochiKit.DOM, MochiKit.Iter and MochiKit.Visual!";
    }
} catch(e) { throw e; }

if (typeof(MochiKit.SlideShow) == "undefined") {
    MochiKit.SlideShow = {};
}

MochiKit.SlideShow.NAME = 'MochiKit.SlideShow';
MochiKit.SlideShow.VERSION = '0.9';

MochiKit.SlideShow.__repr__ = function () {
    return '[' + this.NAME + ' ' + this.VERSION + ']';
};

MochiKit.SlideShow.toString = function () {
    return this.__repr__();
};

MochiKit.SlideShow.EXPORT = [
    "SlideShow"
];

MochiKit.SlideShow.EXPORT_OK = [
    "SlideShow"
];

/*
MochiKit.SlideShow.SlideQueue = function(totalSlides) {
    if ( typeof totalSlides == "undefined" || isNaN(totalSlides) )
        totalSlides = 4;
    for ( var i = 0; i < totalSlides; i++ )
        this.slides.push(new MochiKit.SlideShow.Slide());
};

MochiKit.SlideShow.Slide = function() {

}

MochiKit.SlideShow.Slide.prototype = {
    __init__: function() {

    },
    setURI: function(URI) {

    },
    setContent: function(content) {

    }
};
*/

MochiKit.SlideShow.SlideShow = function(element, options) {
    this.__init__(element, options);
}

MochiKit.SlideShow.SlideShow.prototype = {
    images: {},
    __class__: MochiKit.SlideShow.SlideShow,

    options: function(element) {

    },

    destroy: function(element) {

    },

    __init__: function(element, /* optional */options) {
        var d = MochiKit.DOM;
        var b = MochiKit.Base;

        this.debug   = d.getElement("debugPanel");
        this.element = d.getElement(element);
        this.options = MochiKit.Base.update({
            element:    element,
            tag:        'div', // Use DIV tags for the image elements
            speed:      6000,
            frames:     4,
            fade:       true,
            duration:   3.0,
            blend:      false,
            banner:     "",
            slideClass: "slide-frame",
            pauseOnRewind: true,
            pauseOnForward: true,
            status: false,
            positionVert:  "center",
            positionHoriz: "center"
        }, options);

        this.currentIter = 0;
        this.prevFrame   = 0;
        this.lookAhead   = 0;
        try {
            if ( options.images.length < 1 ) {
                throw "MochiKit.SlideShow requires an image array!";
            }
        } catch(e) {
            throw "MochiKit.SlideShow requires a valid image array!";
        }

        if ( this.options.status ) {
            this.imageCycle = 1;
            this.options.statusEl =
                document.getElementById(this.options.status);
        }
        return self;
    },

    __createFrames: function() {
        var d = MochiKit.DOM;
        var b = MochiKit.Base;
        this._cachedImages = [];
        this._scratchPad = d.DIV({
            "style": {
                "position": "absolute",
                "left":     "-5000px"
            }});
        document.body.appendChild(this._scratchPad);

        if ( this.options.banner == "center" ) {
            this.bannerDiv = d.DIV({
                "style": {
                    "position": "absolute",
                    "z-index":  "10",
                    "left":     "-5000px"
            }});
        } else {
            this.bannerDiv = d.DIV();
        }
        this.element.appendChild(this.bannerDiv);

        this.frameCycle = 0;
        this.options._frames = [];
        for ( var i = 0; i < this.options.frames; i++ ) {
            var div = MochiKit.DOM.DIV({
                "style": {
                    display: "none",
                    height: "1500px",
                    position: "absolute",
                    "z-index": "5"
                },
                "class": this.options.slideClass
            });
            d.makePositioned(div);
            this.options._frames.push(div);
            this.element.appendChild(div);
        }
    },

    start: function() {
        MochiKit.DOM.addLoadEvent(
            MochiKit.Base.bind(this.startSlideShow, this));
    },

    setSpeed: function(speed) {
        if ( isNaN(speed) )
            return false;
        if ( speed < 1000 )
            return false;
        this.options.speed = speed;
    },

    toggleFade: function(fade) {
        this.options.fade = fade ? true : false;
    },

    togglePlayback: function() {
        if ( this.isPlaying ) {
            this.pauseSlideShow();
        } else {
            this.resumeSlideShow();
        }
    },

    pause: function() {
        if ( this.cycleTimer ) {
            clearTimeout(this.cycleTimer);
            this.cycleTimer = null;
        }
        this.isPlaying = false;
    },

    resume: function() {
        this.isPlaying  = true;
        if ( this.cycleTimer ) {
            clearTimeout(this.cycleTimer);
            this.cycleTimer = null;
        }
        /* Hide whatever image we have currently */
        if ( this.options.fade ) {
            /* Fade requires to wait until the fade is finished */
            MochiKit.Visual.fade(this.options._frames[this.frameCycle], {
                duration: this.options.duration,
                afterFinish: MochiKit.Base.bind(
                    function() {
                        this.cycleFrames();
                    }, this)
            });
        } else {
            MochiKit.Style.hideElement(this.options._frames[this.frameCycle]);
            /* Lets get started! */
            this.cycleFrames();
        }
    },

    rewind: function(count) {
        /* Immediately hide the current frame being displayed */
        MochiKit.Style.hideElement(this.options._frames[this.frameCycle]);
        /* Clear the play timer */
        if ( this.cycleTimer )
            clearTimeout(this.cycleTimer);

        if ( typeof(count) == "undefined" || isNaN(count) || count <= 0 )
            count = 1;

        this.imageCycle  -= (count + 1);
        if ( this.imageCycle < 0 )
            this.imageCycle = this.options.images.length - 1;
        this.currentIter = this.imageCycle;
        /*
         We have to reset all frames, this sets frameCycle to 0 and
         we start over.
        */
        this.reloadFrames();

        if ( this.options.pauseOnRewind )
            this.isPlaying = false;
        this.singleShotNoFade = true;
        this.cycleFrames();
    },

    forward: function(count) {
        /* Immediately hide the current frame being displayed */
        MochiKit.Style.hideElement(this.options._frames[this.frameCycle]);
        /* Clear the play timer */
        if ( this.cycleTimer )
            clearTimeout(this.cycleTimer);

        if ( typeof(count) == "undefined" || isNaN(count) || count <= 0 )
            count = 1;

        /* We have to reset all frames, this sets frameCycle to 0 and
           we start over.
        this.reloadFrames();
        */
        this.loadNextFrame();
        if ( this.options.pauseOnForward )
            this.isPlaying = false;
        this.singleShotNoFade = true;
        this.cycleFrames();
    },

    startSlideShow: function() {
        this.__createFrames();
        this.isPlaying = true;
        this.preloadFrames();

        if ( this.options.fade ) {
            this.updateBanner();
            MochiKit.Visual.appear(this.options._frames[this.frameCycle], {
                duration: this.options.duration
            });
        } else {
            this.updateBanner();
            MochiKit.Style.hideElement(this.options._frames[this.frameCycle]);
        }
        if ( this.cycleTimer )
            clearTimeout(cyleTimer);
        this.cycleTimer = setTimeout(
            MochiKit.Base.bind(this.cycleFrames, this),
            parseInt(this.options.speed) + 2000);
    },

/* Slideshow Queue Functions */
    getNextFrame: function() {
        /*this.status(this.dumpStatus());*/
        return 1;
    },

    addFrame: function(frameData, insertPoint) {
        if ( typeof insertPoint == "undefined" || isNaN(insertPoint) ) {
            insertPoint = this.options._frames.length - 1;
            for ( var i = 0; i < insertPoint; i++ ) { 
                /* Cycle upwards (it is a queue) */
                this.options._frames[i].style.background =
                    this.options._frames[i + 1].style.background;
                this.options._frames[i].innerHTML =
                    this.options._frames[i + 1].innerHTML;
            }
        }

        this.loadImage(frameData[0]);
        MochiKit.Style.setStyle(this.options._frames[insertPoint], {
            background:
                "url(" + frameData[0] + ") no-repeat " +
                    this.options.positionHoriz + " " + this.options.positionVert
        });
        if ( frameData[2] || frameData[3] ) {
            this.options._frames[insertPoint].innerHTML =
                "<div class=\"slideshow-banner\">" +
                "<h2>" + frameData[2] + "</h2>" +
                "<p>" + frameData[3] + "</p>" +
                "</div>";
        } else {
            this.options._frames[insertPoint].innerHTML = "";
        }
    },

    updateBanner: function() {
        var c = this.options._frames[0];
        if ( c.innerHTML == "" ) {
            return;
        }
        this.bannerDiv.innerHTML = c.innerHTML;

        var width =
            window.innerWidth ||
            document.documentElement.clientWidth ||
            document.body.clientWidth;
        if ( this.options.banner == "center" ) {
            var w = this.bannerDiv.offsetWidth;

            var widthToSet = 500;
            if ( w > 800 )
                widthToSet = 800;
            if ( this.options.fade )
                MochiKit.Style.hideElement(this.bannerDiv);
            MochiKit.Style.setStyle(this.bannerDiv, {
                "width": widthToSet + "px",
                "left":  parseInt(parseInt(width - widthToSet) / 2) + "px"
            });
        } else {
            MochiKit.Style.setStyle(this.bannerDiv, {
                "width": width + "px" });
        }
        /*
        MochiKit.Style.setStyle(this.options._frames[0], {
            "top": "auto"
        });
        */
        if ( this.options.fade ) {
            MochiKit.Visual.appear(this.bannerDiv, {
                duration: this.options.duration,
                queue: {
                    scope:    'slideshow-banner',
                    position: 'break'
                }
            });
        }
        c.innerHTML = "";
    },

    hideBanner: function() {
        if ( this.options.fade ) {
            MochiKit.Visual.fade(this.bannerDiv, {
                duration: this.options.duration,
                queue: {
                    scope:    'slideshow-banner',
                    position: 'break'
                }
            });
        }
        if ( this.options.banner == "center" ) {
            MochiKit.Style.setStyle(this.bannerDiv,
                { "left":  "-5000px" });
            MochiKit.Style.showElement(this.bannerDiv);
        }
        this.bannerDiv.innerHTML = "";
    },

    cycleFrames: function() {
        if ( this.singleShotNoFade ) {
            if ( !this.options.fade ) {
                this.singleShotNoFade = false; 
            }
            this.options.fade = false;
        }
        if ( this.cycleTimer )
            clearTimeout(this.cycleTimer);

        /* Temporary fixed top position, so the image won't move
         * when the banner fades out
        var currentPos = getElementPosition(this.options._frames[0]);
        setElementPosition(this.options._frames[0], { y: currentPos.y });
        */

        /* Here be dragons, be careful */
        if ( this.options.fade ) {
            MochiKit.Visual.fade(this.options._frames[0], {
                duration: this.options.duration,
                queue: {
                    scope:    'slideshow',
                    position: 'break'
                },
                afterFinish: MochiKit.Base.bind(
                    function() {
                        this.hideBanner();
                        this.setupNextFrame();
                        this.updateBanner();
                        MochiKit.Visual.appear(this.options._frames[0], {
                            queue: {
                                scope:    'slideshow',
                                position: 'start'
                            },
                            duration: this.options.duration,
                            afterFinish: MochiKit.Base.bind(
                            function() {
                                if ( this.isPlaying ) {
                                    this.cycleTimer = setTimeout(
                                        MochiKit.Base.bind(
                                            this.cycleFrames, this),
                                        this.options.speed);
                                    }
                            } , this)
                        } )
                    }, this)
            });
        } else {
            this.hideBanner();
            MochiKit.Style.hideElement(this.options._frames[0]);
            this.setupNextFrame();
            this.updateBanner();
            MochiKit.Style.showElement(this.options._frames[0]);
            if ( this.isPlaying ) {
                this.cycleTimer = setTimeout(
                    MochiKit.Base.bind(this.cycleFrames, this), this.options.speed);
            }
        }
        if ( this.options.statusEl ) {
            if ( ++this.imageCycle > this.options.images.length )
                this.imageCycle = 1;
            this.options.statusEl.innerHTML = this.imageCycle;
        }
        if ( this.singleShotNoFade ) {
            this.options.fade     = true;
            this.singleShotNoFade = false;
        }
    },

    setupNextFrame: function() {
        if ( this.isPlaying ) {
            this.loadNextFrame();
        }
    },

    loadNextFrame: function() {
        this.addFrame(
            this.options.images[this.currentIter++ % this.options.images.length]
        );
    },

    reloadFrames: function() {
        var currentImage = this.currentIter;
        this.frameCycle  = 0;
        for ( var i = 0; i < this.options.frames; i++ ) {
            var nextImageToLoad =
                (this.currentIter + i) % this.options.images.length;
            this.addFrame(this.options.images[nextImageToLoad], i);
        }
    },

    loadImage: function(imgUrl) {
        var image = MochiKit.DOM.IMG();
        image.src = imgUrl;
        if ( this._cachedImages[imgUrl] ) {
            return;
        }
        connect(image, 'onload',
            MochiKit.Base.bind(
                function(url) {
                    this._cachedImages[url] = 1;
                }, this, imgUrl)
        );
        if ( this._scratchPad.firstChild )
            this._scratchPad.removeChild(this._scratchPad.firstChild);
        this._scratchPad.appendChild(image);
    },

    preloadFrames: function() {
        this.currentIter = 0;
        for ( var i = 0; i < this.options.frames; i++ ) {
            var imgIndex = ( this.currentIter++ % this.options.images.length );
            this.addFrame(this.options.images[imgIndex], i);
        }
    },


    onStart: function(element) {

    },

    onEnd: function(element) {

    },

    onChange: function(element) {

    },
    status: function(msg) {
        return;
        document.getElementById("status").innerHTML =
            this.currentIter + ", " + this.frameCycle + ": " + msg +
                "<br/> "/* + this.dumpStatus() + "<br/>"*/ +
                document.getElementById("status").innerHTML;
    },
    dumpStatus: function() {
        var str = "<pre><code>";
        for ( var i = 0; i < this.options._frames.length; i++ ) {
            if ( i == this.frameCycle )
                str += "<b>";
            var tmp = this.options._frames[i].innerHTML;
            tmp = tmp.replace(/</g, "&lt;");
            tmp = tmp.replace(/>/g, "&gt;");
            str += tmp;
            if ( i == this.frameCycle )
                str += "</b>";
            str += "<br/>";
        }
        str += "</code></pre>";
        return str;
    }
};


