dojo.provide("UI.Stage");

(function() {
	var DECLARED_CLASS = "UI.Stage";
	
	dojo.declare(DECLARED_CLASS, UI.Base, {
		constructor: function(container, classBase, startingStageIndex) {
			this.container  = container;
			this.classBase  = classBase || "stage";
			/**
			 * @deprecated
			 */
			this.events     = {};
			this.firstIndex = 0;
			this.lastIndex  = 0;
			this.wrap       = false;
			this.trackPaths = false;
			this.paths      = {};
			this.stages     = [];
			
			dojo.query("." + this.classBase, this.container).forEach(dojo.hitch(this, function(stageNode) {
				this.stages.push( new UI.Stage.StageHelper(stageNode) );
			}));
			
			this.setLimits();
			this.stageIndex = startingStageIndex || +(container.className.match(/stage(\d+)/) || [])[1] || this.firstIndex;
			
			return this;
		},
		
		set: function(nextStageIndex, event, allowSameStageSet) {
			try { dojo.stopEvent(event); } catch(e) {}
			
			var stageIndex = this.stageIndex,
				args = Array.prototype.slice.call(arguments);
			
			if ( !allowSameStageSet && stageIndex == nextStageIndex ) {
				return false;
			}
			
			// Bounds check
			if ( nextStageIndex > this.lastIndex ) {
				if ( this.wrap ) {
					nextStageIndex = this.firstIndex;
				} else {
					return false;
				}
			} else if ( nextStageIndex < this.firstIndex ) {
				if ( this.wrap ) {
					nextStageIndex = this.lastIndex;
				} else {
					return false;
				}
			}
	
			args.shift();
			args = [stageIndex, nextStageIndex].concat(args);
			
			// Event trigger: close current stage
			if (
				// @deprecated
				!getCallback(this.events, nextStageIndex, "onUnload", args)()
				// Global stage event
				|| this.triggerEvent.apply(this, [this.EVT_CLOSE].concat(args)) === false
				// Stage specific event
				|| this.getStageByIndex(stageIndex).triggerEvent.apply(this.getStageByIndex(stageIndex), [this.EVT_CLOSE].concat(args)) === false
			) {
				return false;
			}
			
			if ( this.isLocked(nextStageIndex) ) {
				return false;
			}

			if ( this.isPathTrackingEnabled() && PageStats ) {
				var path = this.getPath(nextStageIndex);
				
				if ( path ) {
					PageStats.Event(path, {gender: PPP.data.user.gender, age: PPP.data.user.age});
				}
			}
			
			// Change the stage
			this.initStage(nextStageIndex);

			// Event trigger: load next stage
			if (
				// @deprecated
				!getCallback(this.events, nextStageIndex, "onLoad", args)()
				// Global stage event
				|| this.triggerEvent.apply(this, [this.EVT_OPEN].concat(args)) === false
				// Stage specific event
				|| this.getStageByIndex(nextStageIndex).triggerEvent.apply(this.getStageByIndex(nextStageIndex), [this.EVT_OPEN].concat(args)) === false
			) {
				return false;
			}
			
			return true;
		},
		
		setWithoutEvents: function(nextStageIndex, event) {
			try { dojo.stopEvent(event); } catch(e) {}
			
			var stageIndex = this.stageIndex;
				
			if ( stageIndex == nextStageIndex ) {
				return false;
			}
				
			// Bounds check
			if ( nextStageIndex > this.lastIndex ) {
				if ( this.wrap ) {
					nextStageIndex = this.firstIndex;
				} else {
					return false;
				}
			} else if ( nextStageIndex < this.firstIndex ) {
				if ( this.wrap ) {
					nextStageIndex = this.lastIndex;
				} else {
					return false;
				}
			}
	
			if ( this.isLocked(nextStageIndex) ) {
				return false;
			}
			
			if ( this.isPathTrackingEnabled() && PageStats ) {
				var path = this.getPath(nextStageIndex);
				
				if ( path ) {
					PageStats.Event(path, {gender: PPP.data.user.gender, age: PPP.data.user.age});
				}
			}
			
			// Change the stage
			this.initStage(nextStageIndex);
			
			return true;
		},
		
		
		initStage: function(stageIndex) {
			this.container.className = this.container.className.replace(/\bstage\d+\b/gi, "");
			dojo.addClass(this.container, "stage" + stageIndex);
			this.stageIndex = +stageIndex;
			
			return this;
		},
		
		/**
		 * @deprecated
		 */
		get: function() {
			return this.getCurrentStageIndex();
		},
		
		previous: function(event) {
			this.set(this.stageIndex -1, event);
			return this;
		},
		
		next: function(event) {
			this.set(this.stageIndex + 1, event);
			return this;
		},
		
		getCurrentStage: function() {
			return this.getStageByIndex(this.stageIndex);
		},
		
		getCurrentStageIndex: function() {
			return this.stageIndex;
		},
		
		getStageByIndex: function(index) {
			return this.stages[index - 1] ? this.stages[index - 1] : false;
		},
		
		getStages: function () {
			return this.stages;
		},
		
		setLimits: function(first, last) {
			this.firstIndex = typeof first != "undefined" ? +first : 1;
			this.lastIndex  = typeof last  != "undefined" ? +last  : false;
			
			if ( this.lastIndex === false ) {
				this.firstIndex = false;
				this.lastIndex  = false;
				
				for ( var i=0, l=this.stages.length, index; i<l; ++i ) {
					index = this.stages[i].getIndex();
					
					this.firstIndex = this.firstIndex ? Math.min(this.firstIndex, index) : index;
					this.lastIndex  = this.lastIndex  ? Math.max(this.lastIndex, index)  : index;
				}
			}
			
			return this;
		},
		
		allowWrapping: function(wrap) {
			this.wrap = !!wrap;
			return this;
		},
		
		lockAll: function() {
			for ( var i=0, l=this.stages.length; i<l; ++i ) {
				this.stages[i].lock();
			}
			
			return this;
		},
		
		lockRange: function(startIndex, stopIndex) {
			for ( var i=Math.min(startIndex, stopIndex) - 1, l=Math.max(startIndex, stopIndex) - 1; i<=l; ++i ) {
				this.stages[i].lock();
			}
			
			return this;
		},
		
		lock: function() {
			for ( var i=0, l=arguments.length; i<l; ++i ) {
				this.stages[ arguments[i] ].lock();
			}
			
			return this;
		},
		
		isLocked: function(index) {
			return this.stages[index - 1].isLocked();
		},
		
		unlockAll: function() {
			for ( var i=0, l=this.stages.length; i<l; ++i ) {
				this.stages[i].unlock();
			}
			return this;
		},
		
		unlockRange: function(startIndex, stopIndex) {
			for ( var i=Math.min(startIndex, stopIndex) - 1, l=Math.max(startIndex, stopIndex) - 1; i<=l; ++i ) {
				this.stages[i].unlock();
			}
			
			return this;
		},
		
		unlock: function() {
			for ( var i=0, l=arguments.length; i<l; ++i ) {
				this.stages[ arguments[i] ].unlock();
			}
			
			return this;
		},
		
		isUnlocked: function(index) {
			return !this.stages[index - 1].isLocked();
		},
		
		getPath: function(index) {
			return this.paths[index];
		},
		
		setPath: function(stage, path) {
			this.paths[stage] = path;
			return this
		},
		
		enablePathTracking: function() {
			this.trackPaths = true;
			return this;
		},
		
		disablePathTracking: function() {
			this.trackPaths = false;
			return this;
		},
		
		isPathTrackingEnabled: function() {
			return this.trackPaths == true;
		}
	});
	
	
	dojo.declare("UI.Stage.StageHelper", UI.Base, {
		constructor: function(container) {
			this.container = container;
			this.index     = +container.className.match(/\bstage(\d+)\b/i)[1];
			this.locked    = false;
		},
		
		getIndex: function() { return this.index; },
		isLocked: function() { return this.locked; },
		lock:     function() { this.locked = true; return this; },
		unlock:   function() { this.locked = false; return this; }
	});
	
	/**
	 * @deprecated For future development, set events using StageHelper 
	 */
	var getCallback = (function() {
		var defaultCallback = function() { return true };
		
		return function(events, stage, callback, args) { 
			return events[this.classBase + stage] && events[this.classBase + stage][callback]
				? function() { return events[this.classBase + stage][callback].apply(null, args); }
				: defaultCallback;
		};
	})();
	
	UI.Stage.getInstance = function(container, classBase, startingStage) {
		if ( !UI.Base.getNodeAttribute(container, "instance", DECLARED_CLASS) ) {
			UI.Base.setNodeAttribute(container, "instance", new UI.Stage(container, classBase, startingStage), DECLARED_CLASS);
		}
		
		return UI.Base.getNodeAttribute(container, "instance", DECLARED_CLASS);
	};
})();
