$.extend(Function.prototype, {
	
	createCallback : function(obj){
        var method = this;
		
        return function() {
			return method.apply(obj, arguments);
        };
    },

});







var Deeper = function() {
	this.init();
}

$.extend(Deeper.prototype, {
		 
	queue: [],					// Coda percorsi da aprire
	current: null,
	opening: null,			
	openDefaultPageOnInit: true,

	init: function() {
		if (!$.address) throw new Error("JQuery address not found");

		var self = this;
		$.address.change(function(event) {
			self.openPath(event.pathNames);
		});
		
		if ($.address.path() == '/')
			self.openDefaultPage();
	},
	
	openPath: function(path) {
		this.queue[this.queue.length] = path;
		this.runQueue();
	},
	
	pageNotFoundListener: [],
	
	onPageNotFound: function(fn) {
		this.pageNotFoundListener.push(fn);
	},
	
	raisePageNotFoundEvent: function(path) {
		var l = this.pageNotFoundListener;
		for(var i = 0; i < l.length; i++)
			l[i](path);
	},
	
	// Inserisce 
	runQueue: function() {
		if (this.opening) return;
		
		var q = this.queue;
		if (q.length > 0) {
			for(var i = 0; i < q.length; i++) {
				if (i < q.length - 1)
					this.log("Skipping path: " + q[i]);
				else {
					this.queue = [];
					this.doLoadPath(q[i]);
				}
			}
		}
	},
	
	openDefaultPage: function() {
		if (!this.openDefaultPageOnInit) return;
		
		var count = 0;
		var home = null;
		
		for (var t in this.pages) {  
			var page = this.pages[t];
			if (page.homepage == true)
				home = page;
			count++;
		}
		
		if (count == 0)
			setTimeout(this.openDefaultPage.createCallback(this), 200);
		
		if (home)
			this.doLoadPage(home);
	},
	
	doLoadPath: function(path) {
		var page = this.getPage(path);
		this.doLoadPage(page);
	},
	
	doLoadPage: function(page) {
		if (page == null) {
			this.raisePageNotFoundEvent(path);
			return;
		}
		
		this.opening = [];
		
		if (this.current == null) {
			this.current = page;
			
			while(page != null) {
				this.opening.push({page: page, method: "in"});
				page = page.getParent();
			}
			this.loadNextBranch();
		} else {
			var int = this.getIntersect(page);
			var c = this.current;
			var p = page;

			if (int.getDepth() < p.getDepth()) {
				while(p != int) {
					if (p != page.getParent())
						this.opening.push({page: p, method: "in"});
					p = p.getParent();
				}
			}
			
			c = this.current;
			while(c != int && c != null) {
				if (c != page.getParent())
					this.opening.push({page: c, method: "out"});
				c = c.getParent();
			}

			this.current = page;
			this.loadNextBranch();
		}
		
					
		
		
	},
	
	loadNextBranch: function() {
		if (this.opening.length == 0) {
			this.opening = null;
			this.runQueue();
		} else  {
			var p = this.opening.pop();
			if (p.method == "in")
				p.page.onTransitionIn();
			else
				p.page.onTransitionOut();
		}
	},
	
	log: function(message) {
		if (console)
			console.debug(message);
	},
	
	
	pages: {},
	rootPage: null,
	
	registerPage: function(page) {
		this.pages[page.getName()] = page;
		
		if (page.parent == null) {
			if (this.rootPage != null)
				throw new Error("Try to register another root page");
			
			this.rootPage = page;
		}
	},
	
	getPage: function(path) {
		var page = this.rootPage;

		for(var i = 0; i < path.length; i++) {
			page = page.getChild(path[i]);
			page.setCurrentBranch(path[i]);
			
			if (page == null)
				return null;
		}

		return page;
	},
	
	getRootPage: function() {
		return this.rootPage;
	},
	
	getIntersect: function(page) {
		var d = Math.min(this.current.getDepth(), page.getDepth()) - 1;
		if (d < 0) d = 0;

		while(page.getDepth() != d)
			page = page.getParent();
		
		return page;
	}
	
});


Deeper = new Deeper();







var Page = function(config) {
	// Apply configuration (only accepted value)
	$.extend(this, {
		name: config.name,
		parent: config.parent,
		homepage: config.homepage,
		onTransitionIn: config.onTransitionIn,
		onTransitionOut: config.onTransitionOut
	});
	
	this.child = [];
	
	// Validate
	if (this.name == null)
		throw new Error("Cannot create page with no name");
	
	if (this.parent) {
		if (!(this.parent instanceof Page))
			throw new Error("Parent must be instance of Page");
		this.parent.child.push(this);
	}
	
	// Init
	Deeper.registerPage(this);
}

$.extend(Page.prototype, {
	
	name: null,
	title: null,
	
	homepage: false,
	
	parent: null,
	child: null,
	currentBranch: null,
	
	getName: function() {
		return this.name;
	},
	
	getTitle: function() {
		return this.title || this.name;
	},
	
	setCurrentBranch: function(branch) {
		this.currentBranch = branch;
	},
	
	getCurrentBranch: function() {
		return this.currentBranch;
	},
	
	getChild: function(child) {
		for(var i = 0; i < this.child.length; i++)
			if (this.child[i].getName() == child)
				return this.child[i];
		// Cerco pagine *
		for(var i = 0; i < this.child.length; i++)
			if (this.child[i].getName() == '*')
				return this.child[i];
				
		return null;
	},
	
	getParent: function() {
		return this.parent;
	},
	
	getDepth: function() {
		var d = 0;
		var p = this;
		
		while(p != null) {
			p = p.getParent();
			d += 1;
		}
		
		return d - 1;
	},
	
	onTransitionIn: function() {
		throw new Error("Transition in not implemented");
	},
	
	onTransitionOut: 	function() {
		throw new Error("Transition out not implemented");
	},
	
	transitionInComplete: function() {
		$('a[class!=disable]').address();
		Deeper.loadNextBranch();
	},
	
	transitionOutComplete: function() {
		$('a[class!=disable]').address();
		Deeper.loadNextBranch();
	}
	
});







