document.observe("dom:loaded", function() {
	
	// Load application:
	Maestro.load();
	
	// Bind navigation to History manager:
	History.Adapter.bind(window, 'statechange', function(){
		var State = History.getState();
		//History.log(State.data, State.title, State.url);
		Utils.fire(States.CHANGE, {section : State.data.section});
	});
	
	// Others:
	EmailObfuscator.load('author');
});

Number.prototype.toPx = function() {
    return this + 'px';
};

var States = {
	CHANGE : 'state:change',
	PREPARE : 'state:prepare',
	ANIMATE : 'state:animate',
	FREEZE : 'state:freeze',
	UNFREEZE : 'state:unfreeze',
	REPLAY : 'state:replay',
	UNLOAD : 'state:unload'
}

var Maestro = {
	
	name : 'Maestro',
	
	controls : {},
	content : null,
	footer : null,
	backgroundEffect : null,
	observers : $A(),
	
	freezed : false,
	expanded : false,
	
	config : {
		contentHeight : null,
		tabInitialOffset : null,
		tabClosedTop : null,
		tabOpenedTop : -15,
		iconReducedRatio : 0.6
	},
	
	options : {
		duration : 2,
		queue : {
			position: 'end',
			scope: 'Maestro'
		}
	},
	
	load : function() {
		// Retrieve elements:
		this.controls.replay = $('replay');
		this.controls.statix = $('static');
		this.controls.dynamic = $('dynamic');
		this.content = $('content');
		this.footer = $('footer');
		
		// Init config:
		this.config.controlsTopOffset = this.controls.replay.getHeight() * -1;
		this.config.contentHeight = this.content.getHeight();
		this.config.tabInitialOffset = this.config.contentHeight * -1;
		this.config.tabClosedTop = this.config.tabInitialOffset / 3;
		
		// Init control buttons:
		this.controls.replay.on('click', this.replay.bind(this));
		this.controls.statix.on('click', Utils.fire.bind(Utils, States.FREEZE));
		this.controls.dynamic.on('click', Utils.fire.bind(Utils, States.UNFREEZE));
		
		// Init navigation tabs:
		$$('#navigation a').each(function(tab){
			new Tab(tab);
		});
		
		// Init tab sections:
		$$('#sections .section').each(function(section){
			new TabSection(section);
		});
		
		// Init background effect:
		this.backgroundEffect = new BGEffects.POVEffect(this.content, {
			cssSelector : '.povEffect',
			increment : 0.2, // Slow animations!
			passive : true
		}, this.content);
		
		// Prevent CPU consuming when window is not focused:
		Event.observe(window, 'blur', this.backgroundEffect.stop.bind(this.backgroundEffect));
		Event.observe(window, 'focus', this.onWindowFocus.bind(this));
		
		// Bind state listeners:
		Utils.observe(States.CHANGE, this.onStateChange.bind(this));
		Utils.observe(States.FREEZE, this.onFreeze.bind(this));
		Utils.observe(States.UNFREEZE, this.onUnfreeze.bind(this));
		Utils.observe(States.UNLOAD, this.onExit.bind(this));
		
		// Run Pony, Run!
		this.play();
	},
	
	play : function(){
		// Prepare content to be animated:
		this.prepare();
		
		// Restart animation:
		this.animate();
	},
	
	replay : function(){
		
		// Restore controls:
		this.toggleControls(this.controls.dynamic, this.controls.statix);
		
		// Hide footer:
		this.footer.morph('opacity: 0', {
			duration : this.options.duration / 2
		});
		
		// Collapse content:
		this.content.morph('height: 0', {
			duration : this.options.duration / 2,
			afterFinish : function(){
				// Play again:
				this.play();
			}.bind(this)
		});
	},
	
	prepare : function() {
		this.expanded = false;
		
		// Styling:
		this.controls.dynamic.setStyle('margin-top: ' + this.config.controlsTopOffset.toPx());
		this.controls.statix.setStyle('margin-top: 0');
		this.content.setStyle('height: 0');
		this.footer.setOpacity(0);
		
		// Notify observers for prepare state:
		Utils.fire(States.PREPARE);
	},
	
	animate : function(){
		this.content.morph('height: ' + this.config.contentHeight.toPx(), {
			duration : Maestro.options.duration,
			transition : Effect.Transitions.spring,
			delay : 0.1,
			queue : this.options.queue,
			afterFinish : function(){
				this.footer.morph('opacity: 1', {
					duration : this.options.duration / 1.5
				});
				
				if(Cookie.get(this.name) != States.FREEZE){
					// Notify observers for prepare state
					Utils.fire(States.ANIMATE, this.options.queue);
					
					// Start background effect:
					this.backgroundEffect.play();
				} else {
					Utils.fire(States.FREEZE);
				}
				
				this.checkHistory(this.options.queue);
			}.bind(this)
		});
	},
	
	onStateChange : function(event){
		if(Object.isElement($(event.memo.section))){
			this.expand();
		} else {
			this.reduce();
		}
	},
	
	onExit : function(event){
		// Collapse content:
		this.content.morph('height: 0', {
			duration : this.options.duration / 10
		});
	},
	
	onWindowFocus : function(event){
		if(!this.freezed){
			this.backgroundEffect.play();
		}
	},
	
	expand : function(queue){
		if(!this.expanded){
			this.expanded = true;
			this.content.morph('height: ' + (this.config.contentHeight + 120).toPx(), {
				duration : this.options.duration / 1.5,
				transition : Effect.Transitions.spring,
				queue : queue || this.options.queue
			});
		}
	},
	
	reduce : function(){
		if(this.expanded){
			this.expanded = false;
			this.content.morph('height: ' + this.config.contentHeight.toPx(), {
				duration : Maestro.options.duration,
				transition : Effect.Transitions.spring
			});
		}
	},
	
	onFreeze : function() {
		this.freezed = true;
		
		// Store user preference:
		Cookie.set('Maestro', States.FREEZE);
		
		this.toggleControls(this.controls.statix, this.controls.dynamic);
		this.backgroundEffect.stop();
	},
	
	onUnfreeze : function() {
		this.freezed = false;
		
		// Store user preference:
		Cookie.unset('Maestro');
		
		this.toggleControls(this.controls.dynamic, this.controls.statix);
		this.backgroundEffect.play();
	},
	
	toggleControls : function(outgoing, ingoing){
		outgoing.morph('margin-top: ' + this.config.controlsTopOffset.toPx(), {
			duration : Maestro.options.duration / 5,
			afterFinish : function(){
				ingoing.morph('margin-top: 0', {
					duration : Maestro.options.duration / 5
				});
			}
		});
	},
	
	getUrlSection : function(url){
		var section = unescape(url.split('?')[1]).split('#')[0];	
		return $(section) ? section : null;
	},
	
	getDocumentTitle : function(section){
		return 'Almeida - ' + section.capitalize();
	},
	
	checkHistory : function(queue){
		var section = this.getUrlSection(window.location.href);
		if(section){
			Utils.fire.bind(Utils, States.CHANGE, {section : section, queue: queue}).delay(1);
			document.title = this.getDocumentTitle(section);
		}
	},
	
	updateHistory : function(section){
		History.pushState({section : section}, this.getDocumentTitle(section), "?" + section);
	}
};

var Tab = Class.create({
	container : null,
	icon : null,
	tabSection : null,
	active : false,
	originalMarginTop : null,
	
	initialize: function(element) {
		this.container = $(element);
		this.originalMarginTop = this.container.getStyle('margin-top');

		// Retrieve tab icon:
		this.icon = this.container.down('img');
		this.icon.store('originalHeight', this.icon.getHeight());
		this.icon.store('reducedHeight', this.icon.retrieve('originalHeight') * Maestro.config.iconReducedRatio);
		
		// Check for tab section:
		this.tabSection = Maestro.getUrlSection(this.container.href);
		
		// Bind user events:
		this.meHandler = this.container.on('mouseenter', 'a', this.onEnter.bind(this, null, null));	
		this.mlHandler = this.container.on('mouseleave', 'a', this.onLeave.bind(this, null, null));
		if(!this.container.hasClassName('disabled')){
			this.container.on('click', 'a', this.onClick.bind(this));
		}

		// Bind state listeners:
		Utils.observe(States.CHANGE, this.onStateChange.bind(this));
		Utils.observe(States.PREPARE, this.onPrepare.bind(this));
		Utils.observe(States.ANIMATE, this.onAnimate.bind(this));
		Utils.observe(States.FREEZE, this.onFreeze.bind(this));
		Utils.observe(States.UNFREEZE, this.onUnfreeze.bind(this));
	},
	
	onPrepare : function(event){
		// Ensure tab is no more in active or static mode:
		this.active = false;
		this.container.removeClassName('static');
		this.container.removeClassName('active');
		
		// Reset styling:
		this.container.setStyle('margin-top: ' + Maestro.config.tabInitialOffset.toPx());
		this.icon.setStyle('height: ' + this.icon.retrieve('reducedHeight').toPx());
	},
	
	onAnimate : function(event){
		// Ensure handlers are registered:
		this.startHandlers();

		// Start animation:
		this.container.morph('margin-top: ' + Maestro.config.tabClosedTop.toPx(), {
				duration : Maestro.options.duration / 6,
				transition : Effect.Transitions.sinoidal,
				queue : event.memo.queue || {position: 'end', scope: 'Tab#animate'}
			}
		);
	},
	
	onClick : function(event){
		if(this.tabSection){
			event.stop();			
			// Update browser history:
			Maestro.updateHistory(!this.active ? this.tabSection: '');
		} else {
			Utils.fire(States.UNLOAD);
		}
	},

	onEnter : function(event, queue){
		if(!this.container.hasClassName('static')){ // TODO: remove this
			this.container.morph('margin-top: ' + Maestro.config.tabOpenedTop.toPx(), {
				duration : Maestro.options.duration / 10,
				transition : Effect.Transitions.sinoidal,
				queue : queue || {position: 'end', scope: 'Tab#' + this.container.identify(), limit : 2},
				beforeStart : this.stretchIcon.bind(this)
			});
		}
	},
	
	onLeave : function(event, queue){
		if(!this.container.hasClassName('static')){ // TODO: remove this
			this.container.morph('margin-top: ' + Maestro.config.tabClosedTop.toPx(), {
				duration : Maestro.options.duration / 10,
				queue : queue || {position: 'end', scope: 'Tab#' + this.container.identify(), limit : 2},
				beforeStart : this.shrinkIcon.bind(this)
			});
		}
	},
	
	onStateChange : function(event){
		if(event.memo.section == this.tabSection){
			this.open(event.memo.queue);
		}else{
			this.close(event.memo.queue);
		}
	},
	
	onFreeze : function(){
		this.stopHandlers();
		
		this.container.addClassName('static');
		this.container.morph('margin-top: ' + this.originalMarginTop, {
			duration : Maestro.options.duration / 1.5,
			transition : Effect.Transitions.spring,
			afterFinish : this.stretchIcon.bind(this)
		});
	},
	
	onUnfreeze : function(){
		this.container.removeClassName('static');
		if(!this.active){
			this.close(Maestro.options.queue);
		} else {
			this.open(Maestro.options.queue);
		}
	},
	
	open : function(queue){
		// Simulate tab activation:
		this.onEnter(null, queue);

		this.active = true;
		this.container.addClassName('active');
		this.stopHandlers();
	},
	
	close : function(queue){
		// Simulate tab unactivation:
		this.onLeave(null, queue);
		
		this.active = false;
		this.container.removeClassName('active');
		this.startHandlers();
	},
		
	shrinkIcon : function(){
		this.icon.morph('height: ' + this.icon.retrieve('reducedHeight').toPx(), {duration : Maestro.options.duration / 5});
	},
	
	stretchIcon : function(){
		this.icon.morph('height: ' + this.icon.retrieve('originalHeight').toPx(), {duration : Maestro.options.duration / 5});
	},
	
	startHandlers : function(){
		// (Re-)register handlers:
		this.meHandler.start();
		this.mlHandler.start();
	},
	
	stopHandlers : function(){
		// Unregister handlers:
		this.meHandler.stop();
		this.mlHandler.stop();
	}
});

var TabSection = Class.create({
	container : null,
	name : null,
	contentHeight : null,
	
	initialize: function(element) {
		this.container = $(element);
		this.name = this.container.identify();
		this.contentHeight = this.container.getHeight();
		
		// Bind state listeners:
		Utils.observe(States.CHANGE, this.onStateChange.bind(this));
		Utils.observe(States.PREPARE, this.onPrepare.bind(this));
	},
	
	onStateChange : function(event){
		if(event.memo.section == this.name){
			this.show(event.memo.queue);
		}else{
			this.hide(event.memo.queue);
		}
	},
	
	onPrepare : function(event){
		this.hide();
	},

	show : function(){
		this.container.morph('top: 0; opacity: 1', {
			duration : Maestro.options.duration / 4,
			beforeStart : this.container.show.bind(this.container)
		});
	},
	
	hide : function(){
		this.container.morph('top: ' + this.contentHeight.toPx() + '; opacity: 0', {
			duration : Maestro.options.duration / 4,
			afterFinish : this.container.hide.bind(this.container)
		});
	}
});
