//Variables for use in the main file
	var Azexis = Azexis || {};

/*
	Azexis.eventDispatcher
	-----------------
	This is applied to an Object to install event dispatch capabilities.
	Obviously the Object you apply it to shouldn't already have such
	capabilities.
	
	Example:
	
	var myObj = {
		myMethod: function(){
			this.dispatchEvent('onEvent', {//event object});
		}
	};
	Azexis.EventDispatcher.setup(myObj);
	myObj.addListener('onEvent', function(e){//do something});
*/
	Azexis.eventDispatcher = function(obj){
			
			// Apply an addListener() method to the Object.
			obj.addListener = function(type, fn){
				if(this._listeners === undefined) this._listeners = [];
				this._listeners.push({'type':type, 'listener': fn});
			};
			
			// Apply a removeListener() method to the Object
			obj.removeListener = function(type, fn){
				if(this._isDispatching === undefined) this._isDispatching = false;
				if(this._listeners !== undefined){
					/*
						if an event is already dispatching we don't want to cause errors 
						as a result of removing the listener immediately. Better to add it
						to a queue and wait till the event has fully dispatched before 
						removing the listener from the list.
					*/
					if(this._isDispatching){
						if(this._removeListenerQueue === undefined) this._removeListenerQueue = [];
						this._removeListenerQueue.push({'type':type, 'listener':fn});
					} else {
						for(var i=0; i<this._listeners.length; i++){
							if((type == this._listeners[i].type) &&
							   (fn == this._listeners[i].listener)) var index = i;
						}
						if(index !== undefined) this._listeners.splice(index, 1);
					}
				}
			};
			
			// Apply a dispatchEvent() method to the Object
			obj.dispatchEvent = function(type, e){
				if(this._isDispatching === undefined) this._isDispatching = true;
				if(this._listeners !== undefined){
					for(var i=0; i<this._listeners.length; i++){
						if(this._listeners[i].type == type) {
							this._listeners[i].listener(e);
						}
					}
				}
				this._isDispatching = false;
				if(this._removeListenerQueue !== undefined){
					for(var i=0; i<this._removeListenerQueue.length; i++){
						this.removeListener(this._removeListenerQueue[i].type, this._removeListenerQueue[i].listener);
					}
					delete this._removeListenerQueue;
				}
			};
	};

/*
	Azexis.dynamicBanner
	--------------
	This requires a specific html structure:
	1. A wrapper which has height and width. In the example below this is banner-small.
	
	Example:
	
	<div class="banner-small">
		<div class="dynamic_banner" style="background-image: url(default-image.jpg);">
			<a href="" class="dynamic-banner-link"></a>
			<ul style="display:none;">
				<li>
					<span class="img">/anders-kern/content/assets/banner_company_01_1.jpg</span>
					<span class="ref"></span>
				</li>
				<li>
					<span class="img">/anders-kern/content/assets/test_03_2.jpg</span>
					<span class="ref"></span>
				</li>
			</ul>
		</div>
		<div class="banner-buttons">
			<ul>
				<li class="first"><a href="#" class="previous"><span class="label">Previous</span></a></li>
				<li><a href="#" class="active"><span class="label">1</span></a></li>
				<li><a href="#" class="inactive"><span class="label">2</span></a></li>				
				<li class="last"><a href="#" class="next"><span class="label">Next</span></a></li>
			</ul>
		</div>
	</div>

	
	Requires:
	1. Azexis.slideshow
	2. Azexis.controller
*/
	Azexis.dynamicBanner = function(el){
		var self = null;
		var banner = el;
		var container = el.parent();
		var images = [];
		var slideshow = null;
		var controller = null;
		
		function setup(){
			banner.css('height', container.height());
			banner.css('width', container.width());
			$('ul li', banner).each(function(){ images.push({'image': $('.img:first', this).text(), 'video':''}); });
			slideshow 	= 	Azexis.slideshow(banner, images, {wait:5000});
			controller 	= 	Azexis.controller($('.banner-buttons ul', container));
			if (images.length < 2) $('.banner-buttons ul').css('display', 'none');
			controller.addListener('onChange', function(e){slideshow.moveTo(e.id);});
			slideshow.addListener('onImageChange', function(e){controller.select(e.imageId);});
		};
		
		self = {};
		
		setup();
		return self;
	};

/*
	Azexis.controller
	-----------
	This is a list of buttons. Each button in the list has an id starting at 0.
	Except for next and previous buttons.
	
	Example:
	<ul>
		<li class="first"><a href="#" class="previous"><span class="label">Previous</span></a></li>
		<li><a href="#" class="active"><span class="label">1</span></a></li>
		<li><a href="#" class="inactive"><span class="label">2</span></a></li>				
		<li class="last"><a href="#" class="next"><span class="label">Next</span></a></li>
	</ul>
*/
	Azexis.controller = function(el){
		var self = null;
		var ul = el;
		var items = 0;
		var currentItem = 0;
		
		function action(id){
			var selectId = id;
			if (id == 'next'){
				selectId = ((currentItem+1)<=(items-1))?(currentItem+1):0;
			} else if (id == 'previous'){
				selectId = ((currentItem-1)>=0)?(currentItem-1):(items-1);
			}
			selectItem(selectId);
			self.dispatchEvent('onChange', { type: 'onChange', id: currentItem });
		};
		
		function selectItem(id){
			currentItem = id;
			var i=0;
			$('li a', ul).each(function(){
				var item = $(this);				
				if(!item.hasClass('previous') && !item.hasClass('next')){
					item.removeClass(((i==id)?'inactive':'active'));
					item.addClass(((i==id)?'active':'inactive'));
					item.blur();
					++i;
				}
			});
		};
		
		function setup(el){
			// setup click action and currentItem
			$('li a', el).each(function(){
				var item = $(this);
				var id = (item.hasClass('previous'))?'previous':'next';
				if(!item.hasClass('previous') && !item.hasClass('next')){
					id = items;
					++items;
				} 
				item.bind('click', function(e){
												action(id);
												return false;
											   });
				
				if(item.hasClass('active')) currentItem = id;
			});
		};
		
		self = {
			select: function(id){
				selectItem(id);
			}
		};
		
		Azexis.eventDispatcher(self);
		setup(ul);
		return self;
	};

/*
	Azexis.slideshow
	----------------------
	requires:
	* Azexis.eventDispatcher in azexis.core.js
*/
	Azexis.slideshow = function(banner, images, options) {
	/*
		Variables
		---------
	*/
		var banner = $(banner);
		var self = null;
		var springDuration = 500; // Spring a partially completed fade to completion.
		var images = images;
		var cachedImages = [true];
		var failedInARow = 0;
		// Actually mid-fade, which is never stopped.
		var animating = false;
		// Don't start any new fades automatically, but user interaction can still
		// force a single fade.
		var paused = false;
		var forceSingle = false;
		var lastTime = new Date;
		
		Azexis.slideshow.defaults = {
			autoPlay: true,
			imagesDir: "",
			duration: 2500,
			userDuration: 1000,
			wait: 10000,
			width: banner.innerWidth(),
			height: banner.innerHeight(),
			backgroundPosition: "left top"
		};
		
		var options = $.extend({}, Azexis.slideshow.defaults, options);
		
		// play automatically?
		paused = !options.autoPlay;
		
		var fader = $("<div>").css({
			position: "absolute",
			'z-index':1,
			top: 0,
			left: 0,
			width: options.width,
			height: options.height,
			backgroundRepeat: "no-repeat",
			backgroundPosition: options.backgroundPosition
		});
		
		// Current image is the index of the image that is fully loaded and moved to
		// the background div.
		var nxt = 1;
		
	/*
		Methods
		---------
	*/
		function mod(a,b) { var n = a%b; return n < 0 ? n+b : n; }
		function currentImage() { if(images) return mod(nxt-1,images.length); }
		function nextImage() { if(images) return mod(nxt,images.length); }
		function advanceImage() { nxt += 1; }
	
		// User manually changes to another slide.
		function changeNext(n) {
			nxt = n;
			
			// Don't interrupt in-progress animations, but switch immediately if we can.
			if (!animating) {
				resetFader();
			} else {
				// I don't know what these did but they look like there were left over from mootools
				// but, everytime i remove them something stops working even though they throw and error
				//fx.stop();
				//fx.setOptions({duration: springDuration});
				//fx.start(1);
				//resetFader();
			}
			
			// User interation always force a single move even if the animation is paused.
			forceSingle = true;
			eventHandler();
		}
		
		function imageToCSS(n) {
			return "url(" + options.imagesDir + images[n].image + ")";
		}
		
		function loadNextImage() {
			var img = new Image;
			
			// Store the current imageCache and number in a closure. If they change
			// during the time it takes to load, the next cache won't get the incorrect
			// values written to it.
			img.onload = (function(cache, n) {
				return function() {
					failedInARow = 0;
					fader.css("background-image", imageToCSS(n));
					cache[n] = true;
					eventHandler();
				}
			})(cachedImages, nextImage());
			
			img.onerror = function() {
				// Can't load the image, just try the next.
				if (failedInARow > 10) {
					// Don't keep looking forever.
					return;
				}
				failedInARow++;
				advanceImage();
				resetFader();
			}
			
			// Start load process.
			if(images) {
				img.src = options.imagesDir + images[nextImage()].image;
			}
		}
		
		function updateCurrentImage() {
			$(banner).css("background-image", fader.css("background-image"));
		}
		
		function resetFader() {
			fader.css("opacity", 0);
			loadNextImage();
		}
		
		function eventHandler() {
			
			if (animating) {
				return;
			}
			
			if (!forceSingle && ((new Date) - lastTime) < options.wait) {
				// Come back when the time is right.
				setTimeout(eventHandler, options.wait - ((new Date) - lastTime));
				return;
			}
			
			if (!cachedImages[nextImage()]) {
				// Not cached yet, the event handler will be called when the cache is
				// complete.
				return;
			}
			
			if (paused && !forceSingle) { return; }
			
			self.dispatchEvent('onBeforeImageChange', { 
				type: 'onBeforeImageChange',
				imageId: currentImage(),
				src: images[currentImage()].image,
				video: images[currentImage()].video
			});
			
			// check again to check if a listener paused 
			// the slideshow before the image was shown
			if (paused && !forceSingle) { return; }
			
			
			var fadeDuration = options.duration;
			if (forceSingle) {
				forceSingle = false;
				fadeDuration = options.userDuration;
			} 
			self.dispatchEvent('onImageChange', { 
				type: 'onImageChange',
				imageId: nextImage(),
				src: images[nextImage()].image,
				video: images[nextImage()].video
			});
			
			fader.animate({opacity: 1}, fadeDuration, "linear", function() {
				lastTime = new Date;
				updateCurrentImage();
				
				// Delay added to prevent flicker during image swap.
				setTimeout(function() {
					animating = false;
					resetFader();
					eventHandler();
					self.dispatchEvent('onAfterImageChange', { 
						type: 'onAfterImageChange',
						imageId: currentImage(),
						src: images[currentImage()].image,
						video: images[currentImage()].video
					});
				}, 100);
			});
			animating = true;
			advanceImage();
		}
		
	/*
		Public Methods
		---------------
	*/
		self = {
			loadImageSequence: function(seq) {
				images = seq;
				cachedImages = [];
				changeNext(0);
			},
			
			pause: function() {
				paused = true;
			},
			
			resume: function() {
				paused = false;
				eventHandler();
			},
			
			play: function(){
				paused = false;
				eventHandler();
			},
			
			moveTo: function(n) {
				changeNext(n);
			},
			
			moveToNext: function() {
				changeNext(currentImage()+1);
			},
			
			moveToPrevious: function() {
				changeNext(currentImage()-1);
			}
		};
	
	/*
		Logic
		------
	*/
		Azexis.eventDispatcher(self);
		banner.append(fader);
		resetFader();
		eventHandler();
		return self;
	};
	
/*
	Azexis.slides
	--------------
	This is usually used as the thumbnail slides beneath a gallery.
	But, may be used by itself. Generally this will have a next and previous button.
*/
	Azexis.slides = function(el, options){
		var self = null;
		var container = el;
		var mask = $('.mask', container);
		var items = 0;
		var slides = null;
		var isAnimating = false;
		var totalWidth = 0;
		var totalHeight = 0;
		
		Azexis.slides.defaults = {
			orientation: 'h',
			slides: '.mask ul',
			slide: '.mask ul li',
			duration: 1500
		};
		
		var options = $.extend({}, Azexis.slides.defaults, options);
		var property = (options.orientation == 'h')?'width':'height';
		var outerProperty = (options.orientation == 'h')?'outerWidth':'outerHeight';
		var positionProperty = (options.orientation == 'h')?'left':'top';
		
		function setup(){
			mask.css('overflow', 'hidden');
			slides = $(options.slides, container);
			$(options.slide, container).each(function(){
				totalWidth += ($(this).outerWidth(true));
				++items;
			});
			totalHeight = slides.outerHeight();

			if(options.orientation == 'h'){
				slides.width(totalWidth);
				slides.height(mask.height());
			} else {
				slides.height(totalHeight);
				slides.width(mask.width());
			}
			slides.css({ position: 'absolute', top:0, left:0 });
			$('.next', container).bind('click', function(e){ next(); return false; });
			$('.previous', container).bind('click', function(e){ previous(); return false; });
			updateButtons();
		};
		
		function next(){
			if(!isAnimating && (slides[property]()>mask[property]())){
				// calculate the position we need
				var position = slides.position();
				var marker = (mask[property]()-position[positionProperty]);
				var tally = 0;
				if((marker+mask[property]())>(slides[property]())){
					tally = (slides[property]() - mask[property]());
				} else {
					$(options.slide).each(function(){
						var dimension = $(this)[outerProperty](true);
						if((tally + dimension) <= marker){
							tally += dimension;
						} else {
							return false
						}
					});
				}
				isAnimating=true;
				var obj = {};
				obj[positionProperty] = (0-tally);
				slides.animate(obj, options.duration, function(e){animationComplete();});
			}
		};
		
		function previous(){
			if(!isAnimating && (slides[property]()>mask[property]())){
				var position = slides.position();
				var marker = (position[positionProperty]+mask[property]());
				var tally = 0;
				$(options.slide).each(function(){
					tally += $(this)[outerProperty](true);
					if(tally >= (0-position[positionProperty])){
						marker = 0-(tally - mask[property]());
						return false;
					}
				});
				tally = (marker<0)?marker:0;
				isAnimating = true;
				var obj = {};
				obj[positionProperty] = tally;
				slides.animate(obj, options.duration, function(e){animationComplete();});
			}
		};
		
		function scrollToItem(id){
			if(!isAnimating && (slides[property]()>mask[property]())){
				var tally = 0;
				jQuery.each($(options.slide), function(i, item){
					if(i == id){
						return false;
					} else {
						tally += $(item)[outerProperty](true);
					}
				});
				if((tally + mask[property]())>slides[property]()){
					tally = slides[property]()-mask[property]();
				}
				isAnimating = true;
				var obj={};
				obj[positionProperty] = (0-tally);
				slides.animate(obj, options.duration, function(e){animationComplete();});
			}
		};
		
		function updateButtons(){
			$('.next', container).removeClass('disabled');
			$('.previous', container).removeClass('disabled');
			if(slides[property]() <= mask[property]()){
				$('.previous', container).addClass('disabled');
				$('.next', container).addClass('disabled');
			}
			if(slides.position()[positionProperty] == 0){
				$('.previous', container).addClass('disabled');
			} else if((0-slides.position()[positionProperty])+mask[property]() == slides[property]()){
				$('.next', container).addClass('disabled');
			}
		};
		
		function animationComplete(e){
			isAnimating = false;
			updateButtons();
			var obj = {
				'type': 'onAnimationComplete',
				'visible': getVisibleItems(),
				'current': getCurrent()
			};
			self.dispatchEvent('onAnimationComplete', obj);
		};
		
		function getVisibleItems(){
			var minPosition = (0-slides.position()[positionProperty]);
			var maxPosition = minPosition + mask[property]();
			var items = [];
			jQuery.each($(options.slide), function(i, item){
				item = $(item);
				if((item.position()[positionProperty] >= minPosition) && (item.position()[positionProperty] < maxPosition)){
					items.push(i);
				}
			});
			return items;
		};
		
		function getCurrent(){
			var current = false;
			jQuery.each($(options.slide), function(i, item){
				item = $(item);
				if(item.hasClass('active')){
					current = i;
					return false;
				}
			});
			return current;
		};
		
		function selectItem(id){
			jQuery.each($(options.slide), function(i, item){
				item = $(item);
				item.removeClass('active');
				if(i == id) item.addClass('active');
			});
			var isVisible = (jQuery.inArray(id, getVisibleItems())>=0);
			if(!isVisible) scrollToItem(id);
		};
		
		self = {
			moveTo: function(id){
				scrollToItem(id);
			},
			select: function(id){
				selectItem(id);
			},
			selectNext: function(){
				next();
			},
			selectPrevious: function(){
				previous();
			}
		};
		
		Azexis.eventDispatcher(self);
		setup();
		return self;
	};
	
/*
	Azexis.gallery
	--------------
*/
	Azexis.gallery = function(el, options){
		var self = null;
		var container = el;
		var mask = $('.mask', container);
		var photo = $('.photo', container);
		var thumbnails = null;
		var controller = null;
		var slideshow = null;
		var videoPlayer = {
			url: Azexis.baseUri + '/flash/videoPlayer.swf'
		};
		
		function setup(){
			// setup the gallery thumbnail mask
			var thumbnailHeight = $('.thumbnails li:first', container).outerHeight(true);
			mask.height(thumbnailHeight);
			mask.css({
						'overflow': 'hidden',
						'width': mask.width(),
						'position': 'relative',
						'top':0,
						'left':0
					 });
			// setup the photo area
			photo.css({
						'overflow':'hidden',
						'width': photo.width(),
						'height':photo.height(),
						'position': 'relative',
						'top':0,
						'left':0
					  });
			// get images
			var images = [];
			$('.thumbnails li a', container).each(function(){
				images.push({'image': $(this).attr('href'), 'video': $(this).attr('data-video')});
			});
			
			// setup components
			thumbnails = Azexis.slides($('.thumbnails', container));
			controller = Azexis.controller($('ul', mask));
			slideshow = Azexis.slideshow($('.photo', container), images, {autoPlay:false});
			
			// add listeners
			controller.addListener('onChange', function(e){changeImage(e.id);});
			slideshow.addListener('onImageChange', function(e){onChange(e.imageId);});
			slideshow.addListener('onAfterImageChange', function(e){onAfterImageChange(e);});
			slideshow.addListener('onBeforeImageChange', function(e){onBeforeImageChange(e);});
		};
		
		function onChange(id){
			thumbnails.select(id);
			controller.select(id);
			self.dispatchEvent('onChange', {type:'onChange', 'id':id});
		};
		
		function changeImage(id){
			thumbnails.select(id);
			controller.select(id);
			slideshow.moveTo(id);
		};
		
		function onBeforeImageChange(obj){
			var video = $('#Video', photo);
			if(video){
				video.remove();
			}
		};
		
		function onAfterImageChange(obj){
			if(obj.video.match(/.flv/)){
				// load videoplayer via swfobject
				var video = $('<div id="Video" class="video"></div>');
				var fader = $('div:first', photo);
				video.insertBefore(fader);
				
				video.css({
					'width': photo.width(),
					'height': photo.height(),
					'position': 'absolute',
					'z-index': 99
				});
				
				var swf = new deconcept.SWFObject(videoPlayer.url, 'Video', photo.width(), photo.height(), 10, '#000');
				swf.addVariable('url', obj.video);
				swf.addParam("swLiveConnect", "true");
				swf.addParam("allowScriptAccess", "sameDomain");
				swf.addParam("allowFullscreen", "true");
				swf.addParam("wmode", "transparent");
				swf.write('Video');
				
			} else if(obj.video.match(/.swf/)){
			} else {
				return false;
			}
		};
		
		self ={
			select: function(id){
				changeImage(id);
			}
		};
		
		Azexis.eventDispatcher(self);
		setup();
		return self;
	};

/*
	Azexis Article
*/
	Azexis.article = function(el, options){
		var self = null;
		var container = el;
		var mask = $('.mask', container);
		var thumbnails = null;
		
		function setup(){
			mask.css({
						'overflow': 'hidden',
						'position': 'relative',
						'top':0,
						'left':0
					 });
			
			thumbnails = Azexis.slides(container, {orientation: 'v'});
			
			$('.next', container).bind('click', function(e){thumbnails.selectNext();})
			$('.previous', container).bind('click', function(e){thumbnails.selectPrevious();})
		};
		
		self ={

		};
		
		Azexis.eventDispatcher(self);
		setup();
		return self;
	};
