////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SubscriptionListener Object
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// TIME GLOBALS
var DEFAULT_BASE_TIME = 30000; // in millisencondes between get_message() request is called
var DEFAULT_INC_TIME = 1000; // in millisencondes / when a get_message()  send an empty response time between reqest is increased,  this is the increment
var DEFAULT_MAX_TIME = 30000; // in millisencondes / when a get_message()  send an empty response time between reqest is increased, this the max time  between get_message() request


var SubscriptionsListener = Class.create();
SubscriptionsListener.prototype = {

    // constructor
    initialize: function() {
		
		// Private attributes
		var _this = this;
		this.subscriptions=[];
		this.userId = user.id;
		this.platformId = 1;
		this.format="json";
		this.id=0; 
		this.widgetsHash = {};
		this.sinceHasBeenModif = 0;
		
		//AJAX request to gateway
		var handleCallback = function(request) {
			var result = request.responseText;
			result=eval("\'"+result+"\'");
			_this.update(result,"initialize");
		};
		params="user_id="+this.userId+"&platform_id="+this.platformId+"&format="+this.format;
		new Ajax.Request('/subscriber/subscribe', {method: 'GET', parameters : params, onComplete : handleCallback, onException:_this.handleAjaxException});
    },
	
	getMessages: function(){
		if(this.readyToGetMessage){
			this.readyToGetMessage=false;
			//<subscriber id="1" format="json" user_id="4"><subscription id="2" state="TO_INIT"/><subscription id="3"><message id="3" state="DELIVERED"/><message id="1" state="DELIVERED"/></subscription><subscription id="4" state="TO_DELETE" type="persistant" service_url="http://service"/></subscriber>
			request='<subscriber id="'+this.id+'" format="'+this.format+'" user_id="'+this.userId+'">';
			var _this = this;
			this.subscriptions.each(function(subscription){
				var add_request2 = false;
				request2 = '<subscription id="'+subscription.id+'" ' ;
				if (subscription.state.substring(0,3) == "TO_"){
					request2 += 'state="'+subscription.state+'" service_url="'+subscription.serviceUrl+'" type="'+subscription.serviceType+'">';
					_this.nextState(subscription.id); // "TO_INIT" >> "RUNNING"   --  "TO_DELETE"  >>  subscription is deleted   -- "TO_STOP" >> "STOPED"
					add_request2 = true;
				}else{
					request2 += '>';
				}
				subscription.messages.each(function(msg){
					if(msg.state && msg.state!="NOT_DELIVERED"){
						request2 += '<message id="'+msg.id+'" state="'+msg.state+'"/>';
						delete _this.subscriptions[subscription.id].messages[msg.id];
						add_request2 = true;
					}
				});
				
				if(add_request2){
					request += request2 +'</subscription>';
				}
			});
			request+="</subscriber>";
			
			var handleCallback = function(request) {
				var result = request.responseText;
				result=eval("\'"+result+"\'");
				_this.update(result,"getMessages");
			};
			
			var handleAjaxException = function(request) {
				_this.readyToGetMessage=true;
			};
			
			params="xml_request="+encodeURIComponent(request);
			params+="&randomId="+Math.random();
			new Ajax.Request('/subscriber/get_messages', {method: 'POST', parameters : params, onComplete : handleCallback, onException: handleAjaxException});
		}
	},
	
	update: function(result,from){	
		//var result2='{"subscriber_id":1,"subscriptions":[{"subscription_id":31, "state":"RUNNING", "type":"persistant", "service_url":"http://127.0.0.1/proxy/rss.php?userId=4&format=json&url=http%3A%2F%2Fwww.rahan.org%2Frss%2Frahan.xml&lines=5&contentType=small_images,new_page_links","messages":[{"message_id":184,"message_type":"News","message_content":"{\\"channels\\" : [{\\"link\\":\\"http:\/\/www.rahan.org\/rss\/rahan.xml\\",\\"title\\":\\"Rahan.org : Fil RSS pour fils de Crao\\",\\"description\\":\\"Le flux RSS du site officiel de Rahan, le fils des ages farouches. Tout sur la BD, le film, le dessin anim\u00e9 du fils de Crao ...\\",\\"unseen\\":5,\\"image\\":null,\\"items\\": [{\\"snapshot\\": null,\\"link\\": \\"http:\/\/www.rahan.org\/fan\/fans.html\\",\\"date\\": \\"Tue, 03 Jun 2008 23:54:00 +0200\\",\\"title\\": \\"Rahan.org : Travaux de Fans\\",\\"status\\": \\"0\\",\\"description\\": \\"Article, photos et dessin de Pascal D. \u00e0 voir sur la page travaux de fans.\\",\\"content\\": \\"Article, photos et dessin de Pascal D. \u00e0 voir sur la \\\' \\\\\\" page travaux de fans.\\"}]}]}"}]}]}';
		// readyToGetMessage is false when a getMessage reqest is waiting a gateway response
		this.readyToGetMessage=true;
		// hasBeenModified is used to force the getMessage Gateway request now because new content has been sent  
		this.hasBeenModified=false;
		
		result=result.evalJSON();
		
		if(this.id==0 && result.subscriber_id){
			this.id = result.subscriber_id;
			this.hasBeenModified=true;
		}
		for(var i=0;i<result.subscriptions.length;i++){
			this.hasBeenModified=true;
			var subscription=result.subscriptions[i];
			// New subscription, allow the client to identify and get the new idsubscription
			if(subscription.subscription_neg_id){
				//alert("NEG_ID");
				var indice=subscription.subscription_neg_id*-1;
				if (this.subscriptions[subscription.subscription_id]){
					this.subscriptions[subscription.subscription_id].callback=this.subscriptions[indice].callback;
					this.subscriptions[subscription.subscription_id].objectType=this.subscriptions[indice].objectType;
					this.subscriptions[subscription.subscription_id].objectId=this.subscriptions[indice].objectId;
					this.subscriptions[subscription.subscription_id].updateStateFromServer(subscription.state);
					if(this.subscriptions[subscription.subscription_id].isRssWidget()){
						this.updateRssWidget();
					}
					
					delete this.subscriptions[indice];					
				}else{
					this.subscriptions[subscription.subscription_id]=this.subscriptions[indice];
					this.subscriptions[subscription.subscription_id].id=subscription.subscription_id;
					delete this.subscriptions[indice];
					this.subscriptions[subscription.subscription_id].updateStateFromServer(subscription.state);
					// Update widget if with subscriptionId setting
					if(this.subscriptions[subscription.subscription_id].isRssWidget()){
						this.updateRssWidget(subscription.subscription_id);
					}
				}				
			}
			if(this.subscriptions[subscription.subscription_id]){
				if(subscription.type){
					//Just after an INIT beacause of type don 't appear in normal case 
					this.subscriptions[subscription.subscription_id].updateFromServer(subscription.type,subscription.service_url,subscription.state);
					//alert("updateFromServer");
				}else{
					this.subscriptions[subscription.subscription_id].updateStateFromServer(subscription.state);
				}
			}else{
				//alert("ADD_FROM_SERVER id="+subscription.subscription_id);
				//New subscription from the the server not from the client subscription so it's an old subscription
				this.addSubscriptionFromServer(subscription.subscription_id,subscription.type,subscription.service_url,subscription.state);
			}
			for(var j=0;j<subscription.messages.length;j++){
				var msg=subscription.messages[j];
				if(this.subscriptions[subscription.subscription_id].messages[msg.message_id]){
					if(msg.state){
						this.subscriptions[subscription.subscription_id].messages[msg.message_id].updateState(msg.state);
					}
				}else{
				 //nouveau Msg
					this.subscriptions[subscription.subscription_id].addMessage(msg.message_id,msg.message_content,msg.message_type,state="NOT_DELIVERED");				 
				}
			}
		}
		//Update ou crée tous les éléments
		//- si la subscription n'est pas créée on met subscription.state=STOPED (client not connected to the listener.subscription)
		//- On met tous les nouveaux messages message.state="NOT_DELIVERED" (client don't get his msg)
			
		this.deliverMessages();
		
		if(from=="initialize"){
			clearTimeout(this.timerFunction);
			this.listen(true);
		}
		if(this.hasBeenModified){
			this.getMessages();
			this.sinceHasBeenModif=0;
		}else{
			if(DEFAULT_BASE_TIME+(this.sinceHasBeenModif*DEFAULT_INC_TIME)<DEFAULT_MAX_TIME){
				this.sinceHasBeenModif+=1;
			}
		}
		if(this.WidgetsSaveSettings){
			dataHandler.saveWidgets(this.widgetsHash, false, false);
			this.widgetsHash={};
			this.WidgetsSaveSettings=false;
		}
	},
	
	listen: function(now){
		var _this=this;
		this.timerFunction=setTimeout(function(){ //delayed for persistant getMessages
			_this.listen(true);
		},DEFAULT_BASE_TIME+(this.sinceHasBeenModif*DEFAULT_INC_TIME));
		if(now || this.hasBeenModified){
			this.getMessages();
		}
	},
	
	pause: function(){	
		clearTimeout(this.timerFunction);
	},
	
	play: function(){
		clearTimeout(this.timerFunction);
		this.listen(false);
	},
	
	subscribe: function(subscriptionId, serviceUrl, callback, serviceType, objectType, objectId){ 
		//alert("subscribe !!!"+serviceType);
		if(subscriptionId==0){
			subscriptionId=this.getUniqSubscriptionNegId();
			this.addSubscription(subscriptionId, serviceUrl, callback, serviceType, objectType, objectId);
		}else{
			if(this.subscriptions[subscriptionId]){
				this.subscriptions[subscriptionId].update(serviceUrl, callback, serviceType, objectType, objectId);
				if(this.subscriptions[subscriptionId].state!="TO_INIT"){
					this.subscriptions[subscriptionId].deliverMessages();
				}
			}else{
				//alert("addFromClient");
				this.addSubscription(subscriptionId, serviceUrl, callback, serviceType, objectType, objectId);
			}
		}
	},
	
	getUniqSubscriptionNegId: function(){  //function( serviceUrl, callback, serviceType  [,subscriptionId] )
		var subsId=(Math.floor(Math.random()*100000));
		while(this.subscriptions[subsId]){
			subsId=(Math.floor(Math.random()*100000));
		}
		return subsId*-1;
	},
	
	addSubscriptionFromServer: function(subscriptionId,serviceType,urlService,state) {
		var subscription = new Subscription(subscriptionId,serviceType,urlService,"");
		subscription.updateStateFromServer(state);
		this.subscriptions[subscriptionId]=subscription;
	},
	
	addSubscription: function(subscriptionId, serviceUrl, callback, serviceType, objectType, objectId) {
		//alert("add_from_client");
		var subscription = new Subscription(subscriptionId, serviceType, serviceUrl, "TO_INIT");
		subscription.objectType=objectType;
		subscription.objectId=objectId;
		subscription.callback=callback;
		if(subscriptionId<0){
			subscriptionId=subscriptionId*(-1);
		}
		this.subscriptions[subscriptionId]=subscription;
	},
	
	nextState: function(subscriptionId){
		if(this.subscriptions[subscriptionId]){
			var state=this.subscriptions[subscriptionId].nextState();
			if(state=="DELETE_ME"){
				this.deleteSubscription(subscriptionId)
			}
			//alert("nextState:"+this.subscriptions[subscriptionId].state);
		}else{
			if(subscriptionId<0){
				this.nextState(subscriptionId*-1)
			}
		}
	},
	
	cleanSubscription: function(subscriptionId, serviceType){  //function ( subscriptionId  [ ,serviceType] )
	},
	
	deleteRequestSubscription: function(subscriptionId){
		if(this.subscriptions[subscriptionId]){
			this.subscriptions[subscriptionId].state="TO_DELETE";
		}
	},
	
	deleteSubscription: function(subscriptionId){
		if(this.subscriptions[subscriptionId]){
			delete this.subscriptions[subscriptionId];
		}
	},
	
	pauseSubscription: function(subscriptionId){
		if(this.subscriptions[subscriptionId]){
			this.subscriptions[subscriptionId].state="TO_STOP";
		}
	},
	
	stopPersistantSubscriptions: function(){
		var _this=this;
		this.subscriptions.each(function(subscription){
			if(subscription.serviceType=="persistant"){
				_this.pauseSubscription(subscription.id);
			}
		});		
	},
	
	playSubscription: function(subscriptionId){
		if(this.subscriptions[subscriptionId]){
			this.subscriptions[subscriptionId].state="TO_INIT";
		}
	},
	
	deliverMessages: function(){
		this.subscriptions.each(function(subscription){
				subscription.deliverMessages();
		});
	},	
	
	save: function() {
	},
	
	removeSubscription: function(objectType, objectId) {
		if(objectType=="widget"){
			var widget = global.widgets[objectId];
			if(widget.getValue("subscriptionId")){
				var subscriptionId=widget.getValue("subscriptionId");
				if(this.subscriptions[subscriptionId]){
					this.subscriptions[subscriptionId].objectType="deleted";
					this.deleteRequestSubscription(subscriptionId);
				}
			}
		}
	},
	
	updateMessageSate: function(msg_id,state){
		this.subscriptions.each(function(subscription){
			if(subscription.messages[msg_id]){
				subscription.messages[msg_id].updateState(state);
			}
		});
	},
	
	updateRssWidget: function(subscription_id){
		var my_widget=global.widgets[this.subscriptions[subscription_id].objectId];
		my_widget.setValue("subscriptionId",subscription_id);
		this.widgetsHash[my_widget.id] = my_widget;
		this.WidgetsSaveSettings=true;
	},
	
// IN DATAMODEL REFACTOR- TO REMOVE IN REFACTORING  
	callback: function(name, args, bind) {
		if (args == undefined) {
			args = [];
		}
		if (typeof bind == 'undefined') {
			bind = this;
		}

		if (bind[name]) {
			try {
				this[name].apply(bind, [args]);
			} catch(e) {
				logger.log(this[name]+" callback: <b>Internal Error :</b> " + e.name + "\n\n" + e.message + "\n" + e.stack, ERROR);
			}
		}
	},
	
	handleAjaxException: function(request, e) {
		logger.log("Data Model handleAjaxException: Error during AJAX call to <a href=\""+request.url+"\">"+request.url+"</a>\n\n<span style=\"color:red;\">"+e.message+"</span>\n"+e.fileName+" - Line: "+e.lineNumber+"\n\n<b>Stack :</b>\n"+e.stack, ERROR);
	}
};





var Subscription = Class.create();
Subscription.prototype = {

    // constructor
    initialize: function(subscriptionId,serviceType,serviceUrl,state) {
		
		// Private attributes
		this.id=subscriptionId;
		this.serviceType=serviceType;
		this.serviceUrl=serviceUrl;
		this.state=state;
		this.callback="";
		this.messages=[];
		//this.callback=callback;
    },
	
	addMessage: function(message_id,message_content,message_type,state){
		var msg=new Message(message_id,message_content,message_type,state);
		this.messages[message_id]=msg;
    },
	
	deliverMessages: function(){	
		if(this.objectId || (this.objectType!="widget" && this.objectType!="rss")){	
			listener.pause();
			switch(this.objectType){
			case "rss":
				widget = global.widgets[this.objectId];
					var json = this.messagesRssToJson();
					if(json!="" && typeof(this.callback) == "function"){
						this.callback.call(widget, json);
					}
				break;
			case "widget":
				widget = global.widgets[this.objectId];
					var json = this.messagesToJson();
					if(json!="" && typeof(this.callback) == "function"){
						this.callback.call(widget, json);
					}
				break;
			case "Renderer":	
				var json = this.messagesToJson();			
				
				if(json!="" && typeof(this.callback) == "function"){
					this.callback.call(renderer, json);
				}				
				break;
			default:
				var json = this.messagesToJson();
				if(json!="" && typeof(this.callback) == "function"){
					this.callback.call(this.objectId, json);
				}	
			}
			listener.play();
		}
    },
	
	messagesRssToJson: function(){
		var output="";
		this.messages.each(function(msg){
			output=msg.content;			
			msg.updateState("DELIVERED");
		});
		if(output!=""){
			try{
				output=output.evalJSON();
			}
			catch(err){
				output="";
			}
		}
		return output;
	},
	
	messagesToJson: function(){
		var output='{"messages":[';
		var cpt=0;
		this.messages.each(function(msg){
			if(msg.state=="NOT_DELIVERED"){
				if(cpt>0){
					output+=',';
				}
				message_content= msg.content.replace(/\"/g,"\\\"");
				output+='{"message_id":"'+msg.id+'","message_type":"'+msg.type+'","message_content":"'+message_content+'"}';
				cpt++;
				msg.updateState("DELIVERED");
			}
		});
		output+=']}';
		if(cpt>0){
			try{
				output=output.evalJSON();
			}
			catch(err){
				output="";
			}
		}else{
			output="";
		}
		return output;
	},
	
	updateState: function(state){
		this.state=state;
    },
	
	updateStateFromServer: function(state){
		switch (state){
			case "TO_INIT" :
				this.state="WAITING_RUNNING";
				break;
			case "TO_STOP" :
				this.state="WAITING_STOPING";
				break;
			default :				
				this.state=state;
				break;
		}
	},
	
	updateFromServer: function(serviceType,serviceUrl,state){
		if(decodeURIComponent(this.serviceUrl)!=decodeURIComponent(serviceUrl)){
			//alert("Updatefromserver-url-dif");
			state="TO_INIT";
		}else if (this.serviceType!=serviceType){
			//alert("Updatefromserver-Type-dif");
			state="TO_INIT";
		}else{
			//alert("Updatefromserver-update_state");
			this.updateStateFromServer(state);
		}
    },
	
	update: function(serviceUrl, callback, serviceType, objectType, objectId){
		this.callback=callback;
		this.objectType=objectType;
		this.objectId=objectId;
		if(decodeURIComponent(this.serviceUrl)!=decodeURIComponent(serviceUrl)){
			//alert("serviceUrl");
			this.serviceUrl=serviceUrl;
			this.state="TO_INIT";
		}
		if(this.serviceType!=serviceType){
			//alert("serviceType");
			this.serviceType=serviceType;
			this.state="TO_INIT";
		}
		if(this.state=="STOPED"){
			this.state="TO_INIT";
		}
		if(this.state!="TO_INIT"){
			//this.deliverMessages();
		}
		
		//alert("SUBS_UPDATE : state = "+this.state);
    },
	
	nextState: function(){
		switch(this.state){
			case "TO_INIT":
				this.updateState("WAITING_RUNNING");
				break;
			case "TO_DELETE":
				this.updateState("DELETE_ME");
				break;
			case "TO_STOP":
				this.updateState("WAITING_STOPING");
				break;
		}
		return this.state;
    },
	
	isRssWidget: function(){
		if(this.objectType=="widget" && global.widgets[this.objectId]){
			return true;
		}
		return false;
	}
	
};

var Message = Class.create();
Message.prototype = {

    // constructor
    initialize: function(message_id,message_content,message_type,message_state) {
		
		// Private attributes
		this.id=message_id;
		this.content=message_content;
		this.type=message_type;
		this.state=message_state;
		//this.callback=callback;
    },

	updateState: function(state){
		this.state=state;
	}
};


