/**
 * @author Peter Swan
 */
var Slideshow = Class.create({
    image_src: [],
    image_options: [],
    container: null,
    currentSlide: 0,
    image_elms: [],
    load_observers: [],
    loading_elements: [],
    goto_elements: $H(),
    windowWidth: 0,
    windowHeight: 0,
    autoSlideTimer: null,
    resizeTimer: null,
    sliding: false,
    options: {
        auto: false,
        auto_resume: false,
        /* vertical_resolve: 'height',  // options 'stretch', 'height', 'tile_both', 'tile_x'
         resize_option: 'height',
         vertical_aspect_ratio: 4/3,*/
        resize_function: null,
        vertical_align: 2,
		horizontal_align: 2,
        auto_interval: 5,
        animation_duration: 0.5,
        swap: null,
        slide_element: 'div'
    },
	
	observe: function(){
		this.container.observe.apply(this.container, arguments);
	},
	
	fire: function(){
		this.container.fire.apply(this.container, arguments);
	},
    
    initialize: function(container, sources, start_index, options){
        this.options = Object.extend(this.options, options ||
        {});
        this.container = $(container);
        if (!this.container) {
            throw 'Slideshow: container is not an element';
        }
        /* allow options to be passed in with each image */
        sources.each(function(s){
            var source = null;
            var options = {};
            if (Object.isString(s)) {
                source = s;
            }
            else {
                source = s.source;
                options = Object.extend(options, s.options ||
                {});
            }
            if (source) {
                this.image_src.push(source);
                this.image_options.push(options);
            }
        }, this);
        for (var i = 0; i < this.image_src.length; i++) {
            var temp = {};
            temp.div = new Element(this.options.slide_element, Object.extend({
                'class': 'slide'
            }, this.image_options[i]['attributes'] ||
            {}));
            temp.div.setStyle({
                position: 'absolute',
                top: '0px',
                left: '0px'
            });
            temp.img = new Element('img', {
                'id': this.identify() + '_slide_' + i
            });
            temp.height = 0;
            temp.width = 0;
            temp.loaded = false;
            temp.loading = false;
            temp.div.insert(temp.img);
            this.load_observers[i] = this.loadCallback.bind(this, i, false, null);
            temp.img.observe('load', this.load_observers[i]);
            this.image_elms[i] = temp;
            this.container.insert(temp.div.hide());
        }
        
        var id = this.identify();
        $$('.' + id + '_previous').invoke('observe', 'click', function(){
            if (this.options.auto) {
                this.stop();
            }
            this.prev();
            if (this.options.auto) {
                this.start('prev');
            }
        }
.bind(this));
        $$('.' + id + '_next').invoke('observe', 'click', function(){
            if (this.options.auto) {
                this.stop();
            }
            this.next();
            if (this.options.auto) {
                this.start('next');
            }
        }
.bind(this));
        this.loading_elements = $$('.' + id + '_loading');
        var selector = '[class*=' + id + '_goto_]';
        var gotos = $$(selector);
        var reg = new RegExp(id + '_goto_([^ ]+)');
        if (gotos.length) {
            for (i = 0; i < gotos.length; i++) {
                var match = reg.exec(gotos[i].className);
                if (match.length == 2) 
                    match = match[1];
                var num = parseInt(match);
                if (Object.isNumber(num) && num < this.image_src.length) {
                    this.goto_elements.set(num, gotos[i]);
                    gotos[i].observe('click', function(e, num){
                        this.go(num);
                        this.setGotoClass(num);
                        e.stop();
                    }
.bindAsEventListener(this, num));
                }
            }
        }
        this.currentSlide = start_index || 0;
        this.loadImage(this.currentSlide);
        var preload = this.nextIndex(this.currentSlide);
        if (this.currentSlide != preload) 
            this.loadImage(preload);
        
        Event.observe(window, 'resize', function(){
            if (this.resizeTimer) {
                window.clearTimeout(this.resizeTimer);
            }
            this.resizeTimer = this.resize.bind(this).delay(0.25);
        }
.bind(this));
        var c = this.currentSlide;
        new PeriodicalExecuter(function(pe){
            if (this.image_elms[c].loaded) {
                pe.stop();
                this.resize();
                this.loading_elements.invoke('hide');
                this.image_elms[c].div.show();
                this.setGotoClass(c);
                if (this.options.auto) {
                    this.start('next');
                }
            }
        }
.bind(this), 0.15);
    },
    
    start: function(){
        var dir = arguments[0] || 'next';
        if (!this.autoSlideTimer) {
            this.autoSlideTimer = new PeriodicalExecuter(this[dir].bind(this), this.options.auto_interval);
        }
    },
    
    stop: function(){
        if (this.autoSlideTimer) {
            this.autoSlideTimer.stop();
        }
    },
    
    loadImage: function(i){
        var s = this.image_src[i];
        var e = this.image_elms[i];
        if (!e.loaded && !e.loading) {
            e.loading = true;
            e.img.writeAttribute({
                'src': s
            });
        }
    },
    
    imageLoadCallback: function(i, img, go, t){
        this.image_elms[i].height = img.height;
        this.image_elms[i].width = img.width;
        this.image_elms[i].loaded = true;
        this.image_elms[i].loading = false;
        if (go) {
            this.swap(i, t, true);
        }
    },
    
    loadCallback: function(i, go, t){
        var temp = new Image();
        temp.onload = this.imageLoadCallback.bind(this, i, temp, go, t);
        temp.src = this.image_src[i];
    },
    
    swap: function(i, t, force){
		if( i == this.currentSlide ) return;
        if (this.sliding && force !== true) {
            return;
        }
        this.sliding = true;
        /* if the image is not yet loaded, replace the load observer with one that will call slide
         * and load the image. when slide is called via imageLoadCallback, the force flag is set to true
         * to override the sliding flag.
         */
        if (!this.image_elms[i].loaded) {
            if (this.loading) {
                return;
            }
            else {
                this.loading = true;
                this.loading_elements.invoke('show');
                this.image_elms[i].img.stopObserving('load', this.load_observers[i]);
                this.image_elms[i].img.observe('load', this.loadCallback.bind(this, i, true, t));
                this.loadImage(i);
                return;
            }
        }
        this.resizeImage(i);
        this.repositionImage(i);
        this.loading_elements.invoke('hide');
        this.fire('slideshow:before_swap', {
            current: this.image_elms[this.currentSlide],
            next: this.image_elms[i],
            current_options: this.image_options[this.currentSlide],
			next_options: this.image_options[i]
        });
        if (Object.isFunction(this.options.swap)) {
            this.options.swap.call(this, this.image_elms[this.currentSlide], this.image_elms[i], this._afterAnimation.bind(this, i));
        }
        else {
            this.image_elms[i].div.setStyle({
                'left': (this.windowWidth * (t ? 2 : 0)) + 'px',
                'zIndex': 2
            }).show();
            new Effect.Move(this.image_elms[i].div, {
                x: this.windowWidth,
                mode: 'absolute',
                duration: this.options.animation_duration,
                afterFinish: function(){
                    this.image_elms[this.currentSlide].div.hide();
                    this.image_elms[i].div.setStyle({
                        'zIndex': 1
                    });
                    this._afterAnimation(i);
                }
.bind(this)
            });
        }
    },
    
    _afterAnimation: function(i){
        this.fire('slideshow:after_swap', {
            current: this.image_elms[this.currentSlide],
            next: this.image_elms[i],
            current_options: this.image_options[this.currentSlide],
			next_options: this.image_options[i]
        });
        this.currentSlide = i;
        this.sliding = false;
        this.loading = false;
    },
    
    go: function(num){
        if (num < this.currentSlide) {
            this.swap(num, false);
            this.loadImage(this.prevIndex(num));
        }
        else 
            if (num > this.currentSlide) {
                this.swap(num, true);
                this.loadImage(this.nextIndex(num));
            }
    },
    
    prev: function(){
        var i = this.prevIndex();
        this.swap(i, false);
        this.setGotoClass(i);
        this.loadImage(this.prevIndex(i));
    },
    
    next: function(){
        var i = this.nextIndex();
        this.swap(i, true);
        this.setGotoClass(i);
        this.loadImage(this.nextIndex(i));
    },
    
    setGotoClass: function(num){
        if (num < 0 || num > this.image_elms.length) 
            return;
        this.goto_elements.values().invoke('removeClassName', 'current_image');
        var temp = null;
        if ((temp = this.goto_elements.get(num))) 
            temp.addClassName('current_image');
    },
    
    prevIndex: function(){
        var i = arguments.length ? arguments[0] : this.currentSlide;
        return i == 0 ? this.image_src.length - 1 : i - 1;
    },
    
    nextIndex: function(){
        var i = arguments.length ? arguments[0] : this.currentSlide;
        return (i + 1) % this.image_src.length;
    },
    
    resizeImage: function(i){
        if (Object.isFunction(this.options.resize_function)) {
            if (this.options.resize_function(this.windowWidth, this.windowHeight, this.image_elms[i], this.image_options[i], this.options)) 
                return;
        }
        this._resizeImage(i);
    },
    
    repositionImage: function(i){
		var e = this.image_elms[i];
        e.div.setStyle({
            left: this.windowWidth + 'px'
        });
		e.img.makePositioned();
		
		var w = parseInt(e.img.getStyle('width'));
		
		var horizontal_align = this.image_options[i] && !Object.isUndefined(this.image_options[i].horizontal_align) ? this.image_options[i].horizontal_align : this.options.horizontal_align;
		if (horizontal_align) {
			var diff = parseInt((this.windowWidth - w) / horizontal_align);
			e.img.setStyle({
				left: diff + 'px'
			});
		}
		
		var vertical_align = this.image_options[i] && !Object.isUndefined(this.image_options[i].vertical_align) ? this.image_options[i].vertical_align : this.options.vertical_align;
        if (vertical_align) {
            var height = w * e.height / e.width;
            var offset = parseInt((height - this.windowHeight) / vertical_align) * -1;
            e.img.setStyle({
                top: offset + 'px'
            });
        }
    },
    
    _resizeImage: function(i){
        var e = this.image_elms[i];
        e.div.setStyle({
            width: this.windowWidth + 'px'
        });
        var w = Math.ceil(e.width / e.height * this.windowHeight);
        var d = Math.max(e.width, this.windowWidth);
        w = isNaN(w) ? 0 : w;
        d = isNaN(d) ? 0 : d;
        w = Math.max(w, d);
        e.img.setStyle({
            width: w + 'px'
        });
		
		/*e.img.makePositioned();
		console.log('resize: ' + w);
		if( w > this.windowWidth ){
			e.img.setStyle({
				left: ( (this.windowWidth - w) / 2) + 'px'
			});
		}
		
        var vertical_align = this.image_options[i] && !Object.isUndefined(this.image_options[i].vertical_align) ? this.image_options[i].vertical_align : this.options.vertical_align;
        if (vertical_align) {
            var height = w * e.height / e.width;
            var offset = parseInt((height - this.windowHeight) / vertical_align) * -1;
            e.img.setStyle({
                top: offset + 'px'
            });
        }*/
    },
    
    resize: function(){
        this.getWindowSize();
        this.container.setStyle({
            width: (this.windowWidth * 3) + 'px',
            height: this.windowHeight + 'px',
            left: '-' + this.windowWidth + 'px'
        });
        this.resizeImage(this.currentSlide);
        this.repositionImage(this.currentSlide);
    },
    
    getWindowSize: function(){
        var myWidth = 0, myHeight = 0;
        if (typeof(window.innerWidth) == 'number') {
            //Non-IE
            myWidth = window.innerWidth;
            myHeight = window.innerHeight;
        }
        else 
            if (document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight)) {
                //IE 6+ in 'standards compliant mode'
                myWidth = document.documentElement.clientWidth;
                myHeight = document.documentElement.clientHeight;
            }
            else 
                if (document.body && (document.body.clientWidth || document.body.clientHeight)) {
                    //IE 4 compatible
                    myWidth = document.body.clientWidth;
                    myHeight = document.body.clientHeight;
                }
        this.windowWidth = myWidth;
        this.windowHeight = myHeight;
    },
    
    identify: function(){
        return this.container.identify();
    }
});

