Core.FlowController = Class.create({
	initialize: function(sfId, states, extraMethods) {
		this.sfId = sfId;
		// define Configurator methods
		var methods = {
			startConfiguration: {method: "startConfiguration", args: ["request"]},
			endConfiguration: {method: "endSession", args: []}
		};
		extraMethods && (methods = $H(methods).merge(extraMethods).toObject());
		this.configuratorWS = new Core.WS("Configurator", methods, this.onWSSuccess.bind(this), this.onWSError.bind(this));
		this.configuratorWS.setContext("cframe");
		// define Setvices methods
		methods = {
			createSession: {method: "createSession", args: ["parameters"]},
			endSession: {method: "terminateSession", args: ["parameters"]}
		};
		this.servicesWS = new Core.WS("Services", methods, this.onWSSuccess.bind(this), this.onWSError.bind(this));
		this.servicesWS.setContext(this.sfId);
		// extra params
		this.extraParams = {};
		this.persistent = {};
		// listen for  events - from iframe
		document.observe("eco:Cframe.changeview", this.changeView.bindAsEventListener(this));
		document.observe("eco:Cframe.autoAdjust", this.autoAdjustEvent.bindAsEventListener(this));
		document.observe("eco:Cframe.scrollToTop", this.scrollToTopEvent.bindAsEventListener(this));
		// until we know
		this.frame = {
			window: window,
			element: null
		};
		// defer frame/window loading until document loaded
		document.observe("dom:loaded", this.startSession.bindAsEventListener(this));
		// end session when window unloads
		Event.observe(window, "unload", this.endSession.bindAsEventListener(this));
		// initialize state machine 
		this.states = states;
		this.currentState = {
			state: this.states["_initial_"]
		};
		this.wsCalled = false;
		// create session state
		//this.createSession();
	},
	init: function() {
		this.createSession();
	},
	initFrames: function() {
		// compute relations between frames
		this.frames = {};
		var elements = $$("frame", "iframe");
		for (var i = 0, n = elements.length; i < n; i++) {
			this.frames[elements[i].name] = {
				element: elements[i]
			};
		}

		for (var i = 0, n = window.frames.length; i < n; i++) {
			this.frames[window.frames[i].name].window = window.frames[i];
		}
	},
	setLanguage: function(value) {
		this.lang = value;
	},
	getLanguage: function() {
		return this.lang;
	},
	setExtraParam: function(key, value, persistent) {
		this.extraParams[key] = value;
		this.persistent[key] = !!persistent;
	},
	getExtraParam: function(key) {
		return this.extraParams[key];
	},
	clearExtraParams: function(all) {
		all = !!all;
		for (key in this.extraParams) {
			(all || !this.persistent[key]) && (delete this.extraParams[key]);
		}
	},
	autoAdjustEvent: function(ev){
		this.autoAdjustSize(ev.memo.target);
	},
	autoAdjustSize: function(name){
		var frame = this.getFrame(name);
		if (frame.element) {
			var doc = targetFrame = frame.window.document;
			iframeElement = frame.element;
			//Defect #328 - Resize the page using the scrollHeight as well
			if(doc.body.scrollHeight > doc.documentElement.scrollHeight)
				frame.element.style.height = doc.body.scrollHeight + 'px';
			else
				frame.element.style.height = doc.documentElement.scrollHeight + 'px';
			if (doc.body.scrollWidth > doc.documentElement.scrollWidth)
				frame.element.style.width = doc.body.scrollWidth + 'px';
			else
				frame.element.style.width = doc.documentElement.scrollWidth + 'px';
		}
	},
	scrollToTopEvent: function (ev){
		this.scrollToTop(ev.memo.target);
	},
	scrollToTop: function(name){
		var frame = this.getFrame(name);
		if (frame.element)
			frame.element.scrollTo(top);
	},
	// create, start and end session
	createSession: function(transport) {
		if (transport) {
			// WS response
			if (transport.headerJSON) {
				alert("creating session failed\n" + transport.headerJSON.message);
				return;
			}
			this.sessionParams = transport.responseText.evalJSON();
			this.sessionId = this.sessionParams.gui.session_id;
			var gui = this.sessionParams.gui;
			for (var name in this.extraParams) {
				var key = name.underscore();
				gui[key] && (this.extraParams[name] = gui[key]);
			}
			this.clearExtraParams();
			// start if document is loaded
			if (this.domLoaded) {
				this.startSession();
			}
		} else {
			// create session
			var parameters = [];
			parameters.push({'gui.sf_id' : this.sfId});
			for (var name in this.extraParams) {
				var p = {};
				var v = this.extraParams[name];
				if (v && v.length > 0) {
					p['gui.' + name.underscore()] = this.extraParams[name];
					parameters.push(p);
				}
			}
			this.kbVersion && parameters.push({'gui.kb_version' : this.kbVersion});
			this.servicesWS.createSession(parameters, this.createSession.bind(this), this.onWSError.bind(this));
		}
	},
	startSession: function() {
		// set up frame relations
		this.frames = {};
		var elements = $$("frame", "iframe");
		for (var i = 0, n = elements.length; i < n; i++) {
			this.frames[elements[i].name] = {
				element: elements[i]
			};
		}
		for (var i = 0, n = window.frames.length; i < n; i++) {
			try {
				this.frames[window.frames[i].name].window = window.frames[i];
			} catch (e) {}
		}
		if (this.sessionId) {
			// start if WS has returned session id
			this.changeView({memo: {action: "start", params: this.extraParams}});
		} else {
			// delay start
			this.domLoaded =  true;
		}		
	},
	endSession: function(obj) {
		if (this.cid) {
			delete this.cid;
			this.configuratorWS.block();
			this.configuratorWS.endConfiguration();
		}
		if (this.sessionId) {
			var parameters = [];
			parameters.push({
				'gui.sf_id': this.sfId
			});
			parameters.push({
				'gui.session_id': this.sessionId
			});
			this.servicesWS.block();
			this.servicesWS.endSession(parameters);
		}
	},
	// start an ECG configuration conversation
	startConfiguration: function(transport) {
		if (transport) {
			var obj = transport.responseText.evalJSON();
			var params = obj.landing_page.toQueryParams();
			//FIX : QC 246 - system has no response after user restore a wrong config ID
			//Added if loop th check the response of the startConfiguration
			//for restore if the config id is not valid it will send a error message in the landing page
			// and the CID will be missing. SO if the CID is missing remove the configID from the extraparams
			//to confinue the normal process.
			if(params.cid) {
				this.cid = params.cid;
				if(params.lang) this.lang=params.lang;
				if(params.country) this.setExtraParam('countryId', params.country, true);
				this.configuratorWS.setCid(this.cid);
				this.sessionOK = true;
				this.clearExtraParams();
				this.changeView();
			}
			else
			{
				alert(obj.landing_page);
				if(this.extraParams['id']) delete this.extraParams['id'];
			}
		} else {
			// startConfiguration
			var parameters = [{"gui.session_id": this.sessionId}];
			if(this.extraParams["kbVersion"]) parameters.push({"gui.kb_version": this.extraParams["kbVersion"]});
			if (this.saved.params) {
				var params = this.saved.params;
				for (var i = 0, n = params.length; i < n; i++) {
					parameters.push(params[i]);
				}
			}
			var request = {requester: {id: this.sfId, name: this.userId}, product: this.extraParams, parameters: parameters};
			this.configuratorWS.startConfiguration(request, this.startConfiguration.bind(this), this.onWSError.bind(this));
		}
	},
	// WS call backs
    onWSError: function(transport) {
		// do something
	},
	onWSSuccess: function(transport) {
		// redo event if necessary
		if (this.saved) {
			this.changeView();
		}
	},
	// change state
	changeView: function (ev) {
		function checkSession(obj) {
			if (obj.sessionId == undefined) {
				throw new Error("no session");
			}
			if (obj.cid == undefined || obj.cid == null) {
				obj.saved = {next: nextState, params: params, action: action};
				obj.startConfiguration();
				return false;
			}
			return true;
		}
		function callWS(ws, method, args, obj) {
			if (method == "startConfiguration" && this.cid) {
				method = "endConfiguration";
				//this.extraParams = params;
				args = [];
			}
			if (method == "endConfiguration" && obj.cid == 0) {
				return;
			}
			obj.wsCalled = true;
			ws[method].apply(ws, args);
			if (method == "endConfiguration") {
				ws.clearCid();
				obj.cid = null;
			}
			//ws.setCid(obj.cid);			
		}
		function getArgs(argNames, params) {
			var args = [];
			for (var i = 0, n = argNames.length;  i < n; i++) {
				var value = params[argNames[i]];
				value && args.push(value);
			}
			return args;
		}
		
		var action, params, nextState, backout = false;
		// figure out next state
		if (ev && ev.memo) {
			// what is next state
			var actions = this.currentState.state.actions;
			action = ev.memo.action;
			while (actions[action]) {action = actions[action]}
			if (action instanceof Object && action.next) {
				if (action.next == "previous") {
					nextState = this.previousState.state;
					params = this.previousState.params;
				} else if (action.next == "backout") {
					backout = true;
				} else {
					nextState = this.states[action.next];
					params = ev.memo.params ? Object.clone(ev.memo.params) : {};
				}
			}
			// go back ?
			if (backout || (nextState && nextState.backout)) {
				window.history.back();
				return;
			}
		} else if (this.saved) {
			// reentry after WS call, restore saved
			action = this.saved.action;
			params = this.saved.params;
			nextState = this.saved.next;
		}
		// transition to new state
		if (nextState) {
			// check if session is valid
			if (nextState.stateless || checkSession(this)) {
				// call [configurator] WS if necessary
				if (action.callWS && !this.wsCalled) {
					this.saved = {next: nextState, params: params, action: action};
					var args = getArgs(action.callWS.args, params);
					callWS(this.configuratorWS, action.callWS.method, args, this);
					if (this.wsCalled) {
						return;
					}
				}
				this.wsCalled = false;
				this.saved = null;
				// set up url and load in frame
				if (nextState.fullParams) {
					// include session id && possible conversation id
					params.sessionId = this.sessionId;
					this.cid && (params.cid = this.cid);
				}
				else {
					params.cid = this.cid
				}
				this.lang && (params.lang = this.lang);
				this.pricingcurrency && (params.pricingcurrency = this.pricingcurrency);
				this.transCounryName && (params.transCounryName = this.transCounryName);
				// extra params
				params = $H(params).merge(this.extraParams).toObject()
				// load new page in frame
				var url = nextState.page + '?' + Object.toQueryString(params);
				this.previousState = this.currentState;
				this.currentState = {state: nextState, params: params};
				// using Location#replace avoids history beeing set
				this.getFrame(action.frame).window.location.replace(url);
			}
		}
	},
	getFrame: function(name) {
		if (name) {
			this.frame = this.frames[name] ? this.frames[name] : {
				window: window,
				element: null
			};
		}
		return this.frame;
	}
	
});
console.info("core flow libraries loaded");
