/**
 * 
 *
 */

// namespace for effects
Ah.Effects =  {};

// Base for effects that can be toggled open and closed
Ah.Effects.ToggleEffect = new Class({
	
	 Implements: [Chain, Events, Options, Ah.DetachableEvents],
        
     id: null,
     effects: null,
     toggled: false,
	
	 setStart: function(){
			if(this.options.startOpen) {
				this.setOpen();
			}else{
				this.setClosed();
			}
		},
	
	 toggle: function(){
            if(this.toggled){
                this.close();
            }else{
                this.open();
            }
            return this;
        },
		
        onComplete: function(){
            if(this.toggled){
                this.onOpen();
            }else{
                this.onClose();
            }
			this.fireEvent('onComplete');
            this.callChain();
        },
        
        onOpen: function(){
            this.fireEvent('onOpen');
        },
        
        onClose: function(){
            this.fireEvent('onClose');
        },
        
        onStart: function(){
            this.fireEvent('onStart');
        },
        
        onCancel: function(){
            this.fireEvent('onCancel');
        }
	
});


/* morph styles for wipe top-bottom 

.wipe_morph_start {
	width: 800px;
	height: 0;
}

.wipe_morph_end {
	width: 800px;
	height: 700px;
}

*/

// 2 sections are wiped from one into the other
Ah.Effects.Wipe = new Class({
        
        Extends: Ah.Effects.ToggleEffect,
		
		name: 'Wipe',
        from: null,
        to: null,

        options: {
            id: null,
            duration:1000,
            transition: Fx.Transitions.Quint.easeInOut,
			morphStart: '.wipe_morph_start',
			morphEnd: '.wipe_morph_end',  
            width: { start: 800, end: 800 },
            height: { start: 0, end: 700 },
			link: 'cancel'
        },
        
        initialize: function(from_el, to_el, options){
            this.from = $(from_el);
            this.to = $(to_el);
            this.setOptions(options);
            this.id = this.options.id || this.name;
            this.applyEffects();       
            this.fireEvent('onInitialize');
        },
        
        applyEffects: function(){
            this.effects = new Fx.Elements($$(this.from, this.to), {duration: this.options.duration, transition: this.options.transition, link: this.options.link});
        	this.effects.addEvent('onStart', this.onStart.bind(this));
            this.effects.addEvent('onComplete', this.onComplete.bind(this));
            this.effects.addEvent('onCancel', this.onCancel.bind(this));
		},
        
        open: function(){
            this.toggled = true;
            this.effects.start({ 
                '0': {
                    'width': [this.from.getStyle('width').toInt(), this.options.width.start],
                    'height': [this.from.getStyle('height').toInt(), this.options.height.start]
                },
                '1': {
                    'width': [this.to.getStyle('width').toInt(), this.options.width.end],
                    'height': [this.to.getStyle('height').toInt(), this.options.height.end]
                }
            });
            return this;
        },
        
        close: function(){
            this.toggled = false;
            this.effects.start({ 
                '0': {
                    'width': [this.from.getStyle('width').toInt(), this.options.width.end],
                    'height': [this.from.getStyle('height').toInt(), this.options.height.end]
                },
                '1': {
                    'width': [this.to.getStyle('width').toInt(), this.options.width.start],
                    'height': [this.to.getStyle('height').toInt(), this.options.height.start]
                }
            });
            return this;
        }
         
});




// 2 sprites are animated simultaneously 
Ah.Effects.Double = new Class({
        
		Extends: Ah.Effects.ToggleEffect,
		
        name: 'Double',
        id: null,
        el: null,
        sprite1: null,
        sprite2: null,
		effects2: null,
        
        options: {
            id: null,
			sprite1Selector: '.sprite1',
			sprite2Selector: '.sprite2',
            duration:1000,
            transition: Fx.Transitions.Quint.easeOut,
			morphStart1: '.double_morph_start_1',
			morphEnd1: '.double_morph_end_1',  
			morphStart2: '.double_morph_start_2',
			morphEnd2: '.double_morph_end_2',
            zindex: {  
                inactive: 1
            },
			link: 'cancel',
			startOpen: false
        },
        
        initialize: function(el,options){
            this.el = $(el);
            this.setOptions(options);
			this.id = this.options.id || this.el.id || this.name;
            this.sprite1 = this.el.getElement(this.options.sprite1Selector);
            this.sprite2 = this.el.getElement(this.options.sprite2Selector); 
            this.applyEffects();
			this.setStart();
            this.fireEvent('onInitialize');
        },
        
        applyEffects: function(){
            var opts = {duration: this.options.duration, transition: this.options.transition, link: this.options.link};
			this.effects = new Fx.Morph(this.sprite1, opts);
			this.effects2 = new Fx.Morph(this.sprite2, opts);
			this.effects.addEvent('onStart', this.onStart.bind(this));
            this.effects.addEvent('onComplete', this.onComplete.bind(this));
            this.effects.addEvent('onCancel', this.onCancel.bind(this));
		},
        
        open: function(){
            this.toggled = true;
            Ah.Effects.Double.depth++;
            this.el.setStyle('z-index', Ah.Effects.Double.depth);
			this.effects.start(this.options.morphEnd1, this.options.morphStart1);
			this.effects2.start(this.options.morphEnd2, this.options.morphStart2);
            return this;
        },
        
        close: function(){
            this.toggled = false;
            this.el.setStyle('z-index', this.options.zindex.inactive); 
            this.effects.start(this.options.morphStart1, this.options.morphEnd1);
			this.effects2.start(this.options.morphStart2, this.options.morphEnd2);
            return this;
        },
		
		setOpen: function(){
			this.toggled = true;
			Ah.Effects.Double.depth++;
            this.effects.set(this.options.morphEnd1);
			this.effects2.set(this.options.morphEnd2);
			return this;
		},
		
		setClosed: function(){
			this.toggled = false;
			this.el.setStyle('z-index', this.options.zindex.inactive); 
            this.effects.set(this.options.morphStart1);
			this.effects2.set(this.options.morphStart2);
			return this;
		}
        
});

Ah.Effects.Double.depth = 2;
















Ah.Effects.Fade = new Class({
        
        Extends: Ah.Effects.ToggleEffect,
		
		name: 'Fade',
        el: null,

        options: {
            id: null,
            duration:1000,
            transition: Fx.Transitions.linear,
			morphStart: '.fade_morph_start',
			morphEnd: '.fade_morph_end',
			link: 'cancel',
			startOpen: false,
			openOpacity: 1,
			closedOpacity: 0
        },
        
        initialize: function(el, options){
            this.el = $(el);
            this.setOptions(options);
            this.id = this.options.id || this.name;
            this.applyEffects();
			this.setStart();
            this.fireEvent('onInitialize');
        },
        
        applyEffects: function(){
            this.effects = new Fx.Morph(this.el, {duration: this.options.duration, transition: this.options.transition, link: this.options.link});
        	this.effects.addEvent('onStart', this.onStart.bind(this));
            this.effects.addEvent('onComplete', this.onComplete.bind(this));
            this.effects.addEvent('onCancel', this.onCancel.bind(this));
		},
        
        open: function(){
            this.toggled = true;
            this.effects.start({ 
            	'opacity': [this.el.getStyle('opacity').toFloat(), this.options.openOpacity]
            });
            return this;
        },
        
        close: function(){
            this.toggled = false;
            this.effects.start({ 
                 'opacity': [this.el.getStyle('opacity').toFloat(), this.options.closedOpacity]
            });
            return this;
        },
		
		setOpen: function(){
			this.toggled = true;
			this.effects.set({
				'opacity':  this.options.openOpacity
			});
			return this;
		},
		
		setClosed: function(){
			this.toggled = false;
			this.effects.set({
				'opacity':  this.options.closedOpacity
			});
			return this;
		}
});



















/*

.fade_slide {
	position:absolute;
	top:0;
	left:0;
}

.fade_slide img {
	overflow:hidden;
}

 */

Ah.Effects.FadeSlideshow = new Class({
        
 		Implements: [Events, Chain, Options, Ah.DetachableEvents],
		
		name: 'FadeSlideshow',
        el: null,
		srcs: [],
		firstAsset: null,
		assets: null,
		usedAssets: [],
		slide1: null,
		slide2: null,
		fade1: null,
		fade2: null,
		periodical: null,
		currentSlideNumber: null,
		playing: false,
		descriptions: null,
    	usedDescriptions: [],
		originalAssets: null,
		originalDescriptions: null,
    
        options: {
            id: null,
			random: false,
            duration:10000,
			slide1Selector: '.slide1',
			slide2Selector: '.slide2',
			effectOptions: {duration: 1000, transition: Fx.Transitions.Quad.easeOut},
			morphStart: '.fade_slideshow_morph_start',
			morphEnd: '.fade_slideshow_morph_end',
			link: 'cancel'
        },
        
        initialize: function(el, srcs, options){
            this.el = $(el);
			this.slide1 = this.el.getElement(this.options.slide1Selector);
			this.slide2 = this.el.getElement(this.options.slide2Selector);
			this.srcs = srcs || this.srcs;
            this.setOptions(options);
            if (this.options.descriptions) {
				this.descriptions = this.options.descriptions;
				this.originalDescriptions = $A(this.descriptions);
			}
            this.id = this.options.id || this.name;
			this.fade1 = new Ah.Effects.Fade(this.slide1, this.options.effectOptions);
			this.fade2 = new Ah.Effects.Fade(this.slide2, this.options.effectOptions);
			this.addDetachableEvent('on_first_complete', this.fade1, 'onOpen', this.onFirstComplete.bind(this));
			this.switchSlides();
			this.loadFirst();
            this.fireEvent('onInitialize');
        },
		
		
		
	    switchSlides: function(){
			if(this.currentSlideNumber != 1){
				this.slide1.setStyle('z-index', 2);
				this.slide2.setStyle('z-index', 1);
				this.currentSlideNumber = 1;
			}else{
				this.slide1.setStyle('z-index', 1);
				this.slide2.setStyle('z-index', 2);
				this.currentSlideNumber = 2;
			}
			this['fade' + this.currentSlideNumber].setClosed();
		},
        
		loadFirst: function(){
			var n = (this.options.random) ?  $random(0,this.srcs.length - 1) : 0;
			this.firstAsset = new Asset.image(this.srcs[n], {'onload': this.onFirstLoaded.bind(this)});
			this.srcs.splice(n,1);
      		if (this.descriptions) this.usedDescriptions.push(this.descriptions.splice(n,1)[0]);
		},
		
		onFirstLoaded: function(){
            var slide = this['slide' + this.currentSlideNumber];
            if(Browser.Engine.trident4){
                slide.adopt(this.ie6HackCreateElWithBgImg(this.firstAsset));
            }else{
                slide.adopt(this.firstAsset);
            }
			this['fade' + this.currentSlideNumber].open();
			this.usedAssets.push(this.firstAsset);
	      if (this.descriptions) {
	        slide.adopt(this.createDescriptionEl(this.usedDescriptions[0]));
	      }
			if (this.srcs.length > 0) {
				this.assets = new Asset.images(this.srcs, {onComplete: this.onAllLoaded.bind(this)});
			}
			this.setupLinks();
			this.setCurrentLink(0);
			this.fireEvent('onFirstLoad');
		},
		
		onAllLoaded: function(){
			this.originalAssets = [this.firstAsset].extend(this.assets);
			this.start();
			this.fireEvent('onAllLoad');
		},
		
		start: function(){
			this.periodical = this.next.periodical(this.options.duration, this);
			this.playing = true;
		},
    
    	play: function(){
			this.next();
			this.start();
		},
		
		stop: function(){
			$clear(this.periodical);
			this.playing = false;
		},
		
		next: function(){
			this.switchSlides();
			var n = (this.options.random) ?  $random(0,this.assets.length - 1) : 0;
			var next = this.assets.splice(n,1)[0];
			this.setCurrentLink(this.originalAssets.indexOf(next));
			this.usedAssets.push(next);
	      
	      if (this.descriptions) {
	        var next_description = this.descriptions.splice(n, 1)[0];
	        this.usedDescriptions.push(next_description);
	      }
      
      var slide = this['slide' + this.currentSlideNumber];
			slide.empty();
      if(Browser.Engine.trident4){
          slide.adopt(this.ie6HackCreateElWithBgImg(next));
      }else{
          slide.adopt(next);
      }
      
      if (this.descriptions) slide.adopt(this.createDescriptionEl(next_description));
      
			this['fade' + this.currentSlideNumber].open();
			if(this.assets.length == 0){
				this.assets = this.usedAssets;
				this.usedAssets = [];
        
        if (this.descriptions) {
          this.descriptions = this.usedDescriptions;
          this.usedDescriptions = [];
        }
			}
			this.fireEvent('onChange');
		},
    
    
		previous: function(){
	      if (this.usedAssets.length > 1) {
	        this.assets.splice(0,0,this.usedAssets.pop());
	        this.assets.splice(0,0,this.usedAssets.pop());
	        if (this.descriptions) {
	          this.descriptions.splice(0,0,this.usedDescriptions.pop());
	          this.descriptions.splice(0,0,this.usedDescriptions.pop());
	        }
	      }else if(this.usedAssets.length == 1){
	        this.assets.splice(0,0,this.usedAssets.pop());
				  this.assets.splice(0,0,this.assets.pop());
	        if (this.descriptions) {
	          this.descriptions.splice(0,0,this.usedDescriptions.pop());
	          this.descriptions.splice(0,0,this.descriptions.pop());
	        }
	      }else{
	        this.assets.splice(0,0,this.assets.pop());
	        this.assets.splice(0,0,this.assets.pop());
	        if (this.descriptions) {
	          this.descriptions.splice(0,0,this.descriptions.pop());
	          this.descriptions.splice(0,0,this.descriptions.pop());
	        }
	      }
	      this.next();
		},
		 
		onFirstComplete: function(){
			this.detachEvent('on_first_complete');
			this.fireEvent('onFirstComplete');
		},
        
    // ie6 is lame and wont fade an image that is a child element of the one being being faded
    // so... hack it by making it a background image on a div
    ie6HackCreateElWithBgImg: function(img_el){
        var hack_el= $(document.createElement('div'));
        hack_el.setStyle('height', img_el.height);
        hack_el.setStyle('width', img_el.width);
        hack_el.setStyle('background-image', 'url(' + img_el.src + ')');
        return hack_el;
    },
    
    createDescriptionEl: function(text){
      return new Element('div').set('class', 'fade_slide_description' ).set('text', text);
    },
	
	setupLinks: function(){
		this.el.getElements('a').each(function(link, i){
			link.addEvent('click', this.onLinkClick.bindWithEvent(this, i));
		}, this);
	},
	
	onLinkClick: function(e, n){
		e.stop();
		if($(e.target).hasClass('current')) return;
		this.stop();
		this.assets = $A(this.originalAssets);
		this.usedAssets = this.assets.splice(0, n);
		this.descriptions = $A(this.originalDescriptions);
		this.usedDescriptions = this.descriptions.splice(0, n);
		this.next();
		this.start();
	},
	
	setCurrentLink: function(n){
		this.el.getElements('a').each(function(link, i){
			link.removeClass('current');
			if(n == i) link.addClass('current');
		}, this);
	}
});








Ah.Effects.ToggleEffect.Collection = new Class({
    
    Extends: Ah.Collection,
    Implements: [Chain, Events, Options],
    
	name: 'ToggleEffectCollection',
	
    closeAll: function(){
        var toggled_items = [];
        this.items.each(function(v,k){
            if(v.toggled) toggled_items.push(v);
        });
        var group = new Group(toggled_items);
        group.addEvent('onComplete', this.onClose.bind(this) );
        toggled_items.each(function(item){
            item.close(); 
        });
        return this;
    },
       
    onClose: function(){
        this.fireEvent('onClose');
        this.fireEvent('onComplete');
        this.callChain();
    },
    
    openNext: function(){
        if($defined(this.next())) this.next().open();
    },
    
    openPrevious: function(){
        if($defined(this.next())) this.previous().open();
    },
    
    closeCurrent: function(){
        this.current().close();
    }
    
});






Ah.ContentLoader = new Class({
	
	Implements: [Events, Chain, Options, Ah.DetachableEvents],
	
	el: null,
	swf: null,
	asset: null,
	xhr: null,
	fade: null,
	url: null,
	
	options: {
		
	},
	
	initialize: function(el){
		this.el = $(el);
		this.fade = new Ah.Effects.Fade(this.el);
		this.fireEvent('onInitialize');
		return this;
	},
	
	clear: function(){
		if(this.xhr) this.xhr.cancel();
		if (this.fade.toggled) {
			this.fade.close();
			this.addDetachableEvent('on_fade_close', this.fade, 'onClose', this.clearEl.bind(this));
		}else{
			this.el.empty();
			this.onClear();
		}
		return this;
	},
	
	clearEl: function(){
		this.el.empty();
		this.detachEvent('on_fade_close');
		this.onClear();
	},
	
	load: function(url, extras){
		if(this.xhr) this.xhr.cancel();
		this.url = url;
		this.extras = extras || {};
		if(this.fade.toggled){
			this.clear();
			this.addDetachableEvent('on_clear_for_content', this, 'onClear', this.onClearForContent.bind(this));
		}else{
			this.loadContent();
		}
	},
	
	onClearForContent: function(){
		this.detachEvent('on_clear_for_content');
		this.loadContent();
	},
	
	loadContent: function(){
		switch(true){
			case this.url.test(/.swf$/):
				this.loadSwf();
				break;
			case this.url.test(/.jpg$/):
			case this.url.test(/.png$/):
			case this.url.test(/.gif$/):
				this.loadImage();
				break;
			default:
				this.loadXhr();
		}
		return this;
	},
	
	loadSwf: function(){
		this.swf = new Swiff(this.url, {
		    width:  this.extras.w, 
		    height: this.extras.h, 
		    container: this.el,
		    events: {
		       // onLoad: 
		    }
		});
		this.onSwfLoad();
	},
	
	loadImage: function(){
		this.asset = new Asset.image(this.url, {'onload': this.onImageLoad.bind(this)});
	},
	
	loadXhr: function(){
		this.xhr = new Request.HTML({
			url: this.url,
			update: this.el,
			onSuccess: this.onXhrLoad.bind(this)
		}).send();
	},
	
	onImageLoad: function(){
		this.fireEvent('onImageLoad');
        if(Browser.Engine.trident4){
            this.el.adopt(this.ie6HackCreateElWithBgImg(this.asset));
        }else{
            this.el.adopt(this.asset);
        }
		this.onLoad();
	},
	
	onSwfLoad: function(){
		this.fireEvent('onSwfLoad');
		this.onLoad();
	},
	
	onXhrLoad: function(){
		this.fireEvent('onXhrLoad');
		this.onLoad();
	},
	
	onLoad: function(){
		this.fade.open();
		this.fireEvent('onLoad');
		this.callChain();
	},
	
	onClear: function(){
		this.fireEvent('onClear');
		this.callChain();
	},
	
	clearChain: function(){
		this.chain.clearChain();
		return this;
	},
    
    // ie6 is lame and wont fade an image that is a child element of the one being being faded
    // so... hack it by making it a background image on a div
    ie6HackCreateElWithBgImg: function(img_el){
        var hack_el= $(document.createElement('div'));
        hack_el.setStyle('height', img_el.height);
        hack_el.setStyle('width', img_el.width);
        hack_el.setStyle('background-image', 'url(' + img_el.src + ')');
        return hack_el;
    }
	
});








Ah.Effects.Mover = new Class({
        
        Extends: Ah.Effects.ToggleEffect,
		
		name: 'Mover',
        el: null,

        options: {
            id: null,
            duration:1000,
            transition: Fx.Transitions.Quint.easeOut,
			morphStart: '.mover_morph_start',
			morphEnd: '.mover_morph_end',
			link: 'cancel',
			startOpen: false
        },
        
        initialize: function(el, options){
            this.el = $(el);
            this.setOptions(options);
            this.id = this.options.id || this.name;
            this.applyEffects();
			this.setStart();
            this.fireEvent('onInitialize');
        },
        
        applyEffects: function(){
            this.effects = new Fx.Morph(this.el, {duration: this.options.duration, transition: this.options.transition, link: this.options.link});
        	this.effects.addEvent('onStart', this.onStart.bind(this));
            this.effects.addEvent('onComplete', this.onComplete.bind(this));
            this.effects.addEvent('onCancel', this.onCancel.bind(this));
		},
        
        open: function(){
            this.toggled = true;
            this.effects.start(this.options.morphEnd, this.options.morphStart);
            return this;
        },
        
        close: function(){
            this.toggled = false;
            this.effects.start(this.options.morphStart, this.options.morphEnd);
            return this;
        },
		
		setOpen: function(){
			this.toggled = true;
            this.effects.set(this.options.morphEnd);
			return this;
		},
		
		setClosed: function(){
			this.toggled = false;
            this.effects.set(this.options.morphStart);
			return this;
		}
});







Ah.Effects.FadeStack = new Class({
        
 		Implements: [Events, Chain, Options],
		
		name: 'FadeStack',
    el: null,
		htmls: [],
		used_html: [],
		slide1: null,
		slide2: null,
		fade1: null,
		fade2: null,
		periodical: null,
		currentSlideNumber: null,
		playing: false,
		
        options: {
            id: null,
			      random: false,
            duration:10000,
      			slide1Selector: '.slide1',
      			slide2Selector: '.slide2',
      			effectOptions: {duration: 1000, transition: Fx.Transitions.Quad.easeOut},
      			morphStart: '.fade_slideshow_morph_start',
      			morphEnd: '.fade_slideshow_morph_end',
      			link: 'cancel',
            auto_start: true
        },
        
        initialize: function(el, htmls, options){
            this.el = $(el);
      			this.slide1 = this.el.getElement(this.options.slide1Selector);
      			this.slide2 = this.el.getElement(this.options.slide2Selector);
      			this.htmls = htmls || this.htmls;
            this.setOptions(options);
            this.id = this.options.id || this.name;
      			this.fade1 = new Ah.Effects.Fade(this.slide1, this.options.effectOptions);
      			this.fade2 = new Ah.Effects.Fade(this.slide2, this.options.effectOptions);
      			this.switchStack();
      			if(this.options.auto_start) this.start();
            this.next();
            this.fireEvent('onInitialize');
        },
		
		
		
	    switchStack: function(){
    			if(this.currentSlideNumber != 1){
    				this.slide1.setStyle('z-index', 2);
    				this.slide2.setStyle('z-index', 1);
    				this.currentSlideNumber = 1;
    			}else{
    				this.slide1.setStyle('z-index', 1);
    				this.slide2.setStyle('z-index', 2);
    				this.currentSlideNumber = 2;
    			}
    			this['fade' + this.currentSlideNumber].setClosed();
    		},
           
    		
    		
    		
    		start: function(){
    			this.periodical = this.next.periodical(this.options.duration, this);
    			this.playing = true;
    		},
    		
    		stop: function(){
    			$clear(this.periodical);
    			this.playing = false;
    		},
    		
    		next: function(){
          var previousSlideNumber = this.currentSlideNumber;
    			this.switchStack();
    			var n = (this.options.random) ?  $random(0,this.htmls.length - 1) : 0;
    			var next = this.htmls.splice(n,1)[0];
    			this.used_html.push(next);
    			var slide = this['slide' + this.currentSlideNumber];
    			slide.empty();
          slide.set('html', next);
    			this['fade' + previousSlideNumber].close();
          this['fade' + this.currentSlideNumber].open();
    			if(this.htmls.length == 0){
    				this.htmls = this.used_html;
    				this.used_html = [];
    			}
    			this.fireEvent('onChange');
    		}
});
