var FeatureController = new Class({
	
	Implements: [Attachments, Initialize],
					  
	attachments	: [],
	selectors	: [],		
	name		: 'FeatureController',

	initialize: function() {
		this.features			= [];
		this.transitions		= ['Boxes.FadeIn','Boxes.Random','Rows.FadeIn','Columns.FadeIn','Rows.Alternate','Columns.Expand','Columns.Blinds'];
		this.container			= $('feature');
		this.controls			= this.container.getElement('.centred').getElement('.feature-controls');
		this.active				= 0;
		this.timer				= null;
		
		$$('.feature').each(function(feature) {
			this.features.push(new Feature(feature, this));							 
		}.bind(this));
		
		this.features[0].element.setStyle('display', 'block');
		//this.features[0].activate();
		
		this.setControls();
		this.startSlideshow();
	},
	
	startSlideshow: function() {
		this.timer = (function() {
			this.next();
		}.bind(this)).periodical(6000);
	},
	
	stopSlideshow: function() {
		$clear(this.timer);
	},
	
	activate: function(index) {
		this.features[index].activate();
	},
	
	next: function() {
		this.active++;
		
		if (this.active >= this.features.length) {
			this.active = 0;	
		}
		
		this.features[this.active].activate();
	},
	
	deactivateAll: function() {
		this.features.each(function(feature) {
			feature.deactivate();							
		});
	},
	
	setControls: function() {
		
		this.controls.empty();
		
		this.features.each(function(feature, index) {
			new Element('a', {
				'class'		: this.active == index ? 'active' : '',
				'href'		: '#',
				'events'	: {
					'click'		: function(e) {
						e.preventDefault();	
						this.activate(index);
					}.bind(this)
				}
			}).inject(this.controls);
		}.bind(this));
	}
	
});

var Feature = new Class({
	
	Implements: [Attachments, Initialize],
					  
	attachments	: [],
	selectors	: ['.feature'],		
	name		: 'Feature',

	initialize: function(element, controller) {
		
		// The prepare method of the Initialize class sets this.element, stores the current
		// class instance against the element, and attaches any functions to the element.
		this.prepare(element);
		
		this.controller			= controller;
		this.index				= this.controller.features.length;
		this.boxes				= [];
		this.boxWidth			= 80;
		this.boxHeight			= 80;
		this.width				= this.element.getSize().x;
		this.height				= this.element.getSize().y;
		this.rows				= this.height / this.boxHeight;
		this.columns			= this.width / this.boxWidth;
		this.backgroundImage	= this.element.getElement('.feature-background').getElement('img');
		this.boxContainer		= new Element('div.feature-box-container').inject(this.element);
		
		// If transition data is present within the markup, set transition accordingly, 
		// otherwise resort to a random transition
		if (this.element.get('data-transition-type') && this.element.get('data-transition')) {
			this.transition			= new Feature.Transitions[this.element.get('data-transition-type')][this.element.get('data-transition')](this);
		} else {
			var transition 			= this.controller.transitions.getRandom();
			var transitionData		= transition.split('.');
			
			this.transition			= new Feature.Transitions[transitionData[0]][transitionData[1]](this);
		}
		
		this.element.addEvents({
			'mouseenter' 	: function(e) {
				e.stop();
				this.controller.stopSlideshow();
			}.bind(this),
			'mouseleave'	: function(e) {
				e.stop();
				this.controller.startSlideshow();
			}.bind(this)
		});
	},
	
	show: function() {
		this.createBoxes();
		this.getBoxes().each(function(box) {
			box.retrieve('FeatureBox').show();				  
		});
	},
	
	deactivate: function() {
		this.element.removeClass('active');
	},
	
	activate: function() {
		if (!this.backgroundImage.hasClass('hidden')) {
			this.backgroundImage.addClass('hidden');
		}
		
		this.controller.deactivateAll();
		this.controller.active = this.index
		this.controller.setControls();
		this.deleteBoxes();
		
		this.element.removeClass('hidden');
		this.element.addClass('active');
		
		this.createBoxes();
		this.transition.start();
	},
	
	createBoxes: function() {
		this.boxes = new Array();
		
		for (var row = 0; row < this.rows; row++) {
			this.boxes.push(new Array);
			
			for (var column = 0; column < this.columns; column++) {
				this.boxes[row].push(new FeatureBox(row, column, this));
			}
		}
	},
	
	deleteBoxes: function() {
		this.boxContainer.empty();
		
		if (this.boxes.length > 0) {
			for (var row = 0; row < this.rows; row++) {
				for (var column = 0; column < this.columns; column++) {
					this.boxes[row][column] = null;
				}
			}
		}
		
		this.boxes = new Array();
	},
	
	getColumns: function() {
		
		var containers = [];
		
		this.columns.each(function(column) {
										
			var container = new Element('div.feature-column', {
				'styles'	: {
					'top'			: '-' + this.height + 'px',
					'width' 		: this.boxWidth,
					'height'		: this.height
				}
			}).inject(this.boxContainer);
			
			this.rows.each(function(row) {
				var box		= this.boxes[row][column].element;
				
				box.removeClass('transparent');
				box.inject(container);
			}.bind(this));
				
			containers.push(container);
				
		}.bind(this));			
		
		return containers;
	},
	
	getRows: function() {
		
		var containers = [];
		
		this.rows.each(function(row) {
										
			var container = new Element('div.feature-row', {
				'styles'	: {
					'top'			: this.width + 'px',
					'width' 		: this.width,
					'height'		: this.boxHeight
				}
			}).inject(this.boxContainer);
			
			this.columns.each(function(column) {
				var box		= this.boxes[row][column].element;
				
				box.removeClass('transparent');
				box.inject(container);
			}.bind(this));
				
			containers.push(container);
				
		}.bind(this));			
		
		return containers;
	},
	
	getBoxes: function() {
		
		var boxes = [];
		
		this.rows.each(function(row) {
			this.columns.each(function(column) {
				var box		= this.boxes[row][column].element;
				
				//box.removeClass('transparent');
				boxes.push(box);
				
			}.bind(this));
		}.bind(this));			
		
		return boxes;
	}	
	

});


var FeatureBox = new Class({
	
	Implements	: [Attachments, Initialize],
	selectors	: [],		
	name		: 'FeatureBox',

	initialize: function(row, column, feature) {
		
		this.feature		= feature;
		this.row 			= row;
		this.column			= column;
		this.x				= column * this.feature.boxWidth;
		this.y				= row * this.feature.boxHeight;
		
		var element = new Element('div.feature-box.transparent', {
			'data-row'		: row,
			'data-column'	: column,
			'styles'		: {
				'width'					: feature.boxWidth,
				'height'				: feature.boxHeight,
				'background-image'		: 'url(' + feature.backgroundImage.src + ')',
				'background-position'	: '-' + this.x + 'px -' + this.y + 'px'
			}
		}).inject(this.feature.boxContainer);
		
		// The prepare method of the Initialize class sets this.element, stores the current
		// class instance against the element, and attaches any functions to the element.
		this.prepare(element);
	},
	
	show: function() {
		this.element.removeClass('transparent');
		this.element.setStyles({
			'display'	: 'block',
			'opacity'	: '1'
		});
	}

});

Feature.Transitions = new Class({
	
	initialize: function() {

	},
	
	start: function() {
		this.functions.empty();
		this.logic();
		
		this.chain.apply(this, this.functions);
		this.callChain();		
	},
	
	random: function() {
		//this.feature.controller.transitions;	
	}
});

Feature.Transitions.Boxes 		= {};
Feature.Transitions.Rows 		= {};
Feature.Transitions.Columns 	= {};

Feature.Transitions.Boxes.FadeIn = new Class({
							   
	Implements	: [Chain, Feature.Transitions],
	
	initialize: function(feature) {
		this.feature 	= feature;
		this.functions	= [];
		this.delay		= 2;
	},
	
	effect: function(box) {
		(function() {
				  
			var effect 	= new Fx.Morph(box, {
				duration		: 'long', 
				transition		: Fx.Transitions.Sine.easeOut,
				link			: 'chain'
			});				  
			
			effect.start({
				'opacity': [0, 1] //Morphs the 'height' style from 10px to 100px.
			});
			
			this.callChain();
			
		}.bind(this)).delay(this.delay);	
	},
	
	logic: function() {
		
		this.feature.getBoxes().each(function(box, index) {

			this.functions.push(function() {
				this.effect(box);
			}.bind(this));
				
		}.bind(this));
	}	
	
});

Feature.Transitions.Boxes.Random = new Class({
							   
	Implements	: [Chain, Feature.Transitions],
	
	initialize: function(feature) {
		this.feature 	= feature;
		this.functions	= [];
		this.delay		= 30;
	},
	
	effect: function(box) {
		(function() {
				  
			var effect 	= new Fx.Morph(box, {
				duration		: 'long', 
				transition		: Fx.Transitions.Sine.easeOut,
				link			: 'chain'
			});				  
			
			effect.start({
				'opacity': [0, 1] //Morphs the 'height' style from 10px to 100px.
			});
			
			this.callChain();
			
		}.bind(this)).delay(this.delay);	
	},
	
	logic: function() {

		this.feature.getBoxes().shuffle().each(function(box, index) {

			this.functions.push(function() {
				this.effect(box);
			}.bind(this));
				
		}.bind(this));
	}	
	
});

Feature.Transitions.Rows.FadeIn = new Class({
							   
	Implements	: [Chain, Feature.Transitions],
	
	initialize: function(feature) {
		this.feature 	= feature;
		this.functions	= [];
		this.delay		= 300;
	},
	
	effect: function(box, options) {
		(function() {
				  
			var effect 	= new Fx.Morph(box, {
				duration		: 'long', 
				transition		: Fx.Transitions.Sine.easeOut,
				link			: 'chain'
			});				  
			
			effect.start({
				'top'		: [options.top, options.top],
				'left'		: [1000, 0] //Morphs the 'height' style from 10px to 100px.
			});
			
			this.callChain();
			
		}.bind(this)).delay(this.delay);	
	},
	
	logic: function() {
		
		this.feature.getRows().each(function(row, index) {
				
			this.functions.push(function() {
				this.effect(row, {top: index * this.feature.boxHeight});
			}.bind(this));
				
		}.bind(this));

	}	
	
});

Feature.Transitions.Columns.FadeIn = new Class({
							   
	Implements	: [Chain, Feature.Transitions],
	
	initialize: function(feature) {
		this.feature 	= feature;
		this.functions	= [];
		this.delay		= 100;
	},
	
	effect: function(box, options) {
		(function() {
				  
			var effect 	= new Fx.Morph(box, {
				duration		: 'long', 
				transition		: Fx.Transitions.Sine.easeOut,
				link			: 'chain'
			});				  
			
			effect.start({
				'left'		: [options.left, options.left],
				'top'		: [-1000, 0] //Morphs the 'height' style from 10px to 100px.
			});
			
			this.callChain();
			
		}.bind(this)).delay(this.delay);	
	},
	
	logic: function() {
		
		this.feature.getColumns().each(function(column, index) {
										
			this.functions.push(function() {
				this.effect(column, {left: index * this.feature.boxHeight});
			}.bind(this));
				
		}.bind(this));

	}	
	
});

Feature.Transitions.Rows.Alternate = new Class({
							   
	Implements	: [Chain, Feature.Transitions],
	
	initialize: function(feature) {
		this.feature 	= feature;
		this.functions	= [];
		this.delay		= 500;
	},
	
	effect: function(box, options) {
		(function() {
				  
			var effect 	= new Fx.Morph(box, {
				duration		: 'long', 
				transition		: Fx.Transitions.Sine.easeOut,
				link			: 'chain'
			});				  
			
			effect.start({
				'top'		: [options.top, options.top],
				'left'		: [options.left, 0] //Morphs the 'height' style from 10px to 100px.
			});
			
			this.callChain();
			
		}.bind(this)).delay(this.delay);	
	},
	
	logic: function() {
		
		this.feature.getRows().each(function(row, index) {
				
			this.functions.push(function() {
				this.effect(row, {top: index * this.feature.boxHeight, left: (index % 2 == 0 ? '-1000px' : '1000px')});
			}.bind(this));
				
		}.bind(this));		
		
	}	
	
});

Feature.Transitions.Columns.Expand = new Class({
							   
	Implements	: [Chain, Feature.Transitions],
	
	initialize: function(feature) {
		this.feature 	= feature;
		this.functions	= [];
		this.delay		= 40;
	},
	
	effect: function(column, options) {
		(function() {
			
			column.getChildren().each(function(box, index) {
				
				var effect 	= new Fx.Morph(box, {
					duration		: 'long', 
					transition		: Fx.Transitions.Sine.easeOut,
					link			: 'chain'
				});				  
				
				effect.start({
					'opacity'	: [1, 1],
					'width'		: [0, this.feature.boxWidth],
					'height'	: [0, this.feature.boxHeight] //Morphs the 'height' style from 10px to 100px.
				});
			}.bind(this));
			
			this.callChain();
			
		}.bind(this)).delay(this.delay);	
	},
	
	logic: function() {
		
		this.feature.getColumns().each(function(column, index) {
			
			column.setStyles({
				'top'		: 0,
				'left'		: index * this.feature.boxWidth,
				'width'		: this.feature.boxWidth
		 	});
			
			column.getChildren().each(function(box, heightIndex) {
				box.setStyles({
					'width' 	: 0,
					'height'	: 0,
					'position'	: 'absolute',
					'top'		: heightIndex * this.feature.boxHeight
				});
			}.bind(this));
			
			this.functions.push(function() {
				this.effect(column);
			}.bind(this));
				
		}.bind(this));
	}	
	
});

Feature.Transitions.Columns.Blinds = new Class({
							   
	Implements	: [Chain, Feature.Transitions],
	
	initialize: function(feature) {
		this.feature 	= feature;
		this.functions	= [];
		this.delay		= 100;
	},
	
	effect: function(column, options) {
		(function() {
			
			column.getChildren().each(function(box) {
				var effect 	= new Fx.Morph(box, {
					duration		: 'long', 
					transition		: Fx.Transitions.Sine.easeOut,
					link			: 'chain'
				});				  
				
				effect.start({
					'opacity'	: [1, 1],
					'width'		: [0, this.feature.boxWidth]
				});
			}.bind(this));
			
			this.callChain();
			
		}.bind(this)).delay(this.delay);	
	},
	
	logic: function() {
		
		this.feature.getColumns().each(function(column, index) {
							
			column.setStyles({
				'top'		: 0,
				'left'		: index * this.feature.boxWidth,
				'width'		: this.feature.boxWidth
		 	});
			
			column.getChildren().each(function(box, heightIndex) {
				box.setStyles({
					'width' 	: 0,
					'position'	: 'absolute',
					'top'		: heightIndex * this.feature.boxHeight
				});
			}.bind(this));
			
			this.functions.push(function() {
				this.effect(column);
			}.bind(this));
				
		}.bind(this));

	}	
	
});
