/*
	Author: Stephen W. Cote
	Email: wranlon@hotmail.com
	
	Copyright 2002, All Rights Reserved.
	
	Do not copy, archive, or distribute without prior written consent of the author.
*/

_js_package("org.cote.js.message");


/*
	Why this isn't a "Log" service/component:
	
	In the Web paradigm, if the UI is not prepared to display the "log" items,
	then that data really goes nowhere, even if it is stored.
	
	In a "messaging" context, the data is reported to other parts of the
	application that may be listening.  This was always the design,
	and the use of "log" was a bit inconsistent with the behavior.
	
	Core API:
	
		sendMessage - replaces LoggerFactory.log
			This sends a message.  It doesn't expect a response.
			
		subscribe - replaces EventFactory.addEventListener
			This instructs the Message service to register an object as a
			recipient to a published message.
			
		publish - replaces EventFactory.fireEvent
			This instructs the Message service to publish a message to 
			any object that is subscribed to that type of message.  This suggests
			that messages have a pre-identified set of names.
		
*/

org.cote.js.message={
	MessageService:null,
	MessageServiceImpl:function(){
		/*
			object_config and the pointers and status child hashes are defined
			under the generic_ and spec_ objects in the config.
			
			However, the message service must be online prior to that, and may never
			use the config at all, so here defines its own.
			
			This could also be wrapped into another base def.
		*/
		var t=this;
		t.object_config={
			pointers:{
				subscriptions:[],
				delayed_deliveries:[],
				entries:[],
				local_definitions:[]
			},
			status:{
				delivery_delay:50,
				maximum_entries:200,
				report_threshold:1
			},
			data:{
				message_levels:["ALL","DEBUG","ADVISORY","NORMAL","WARNING","ERROR","FATAL","NONE"],
				message_levels_map:{ALL:0,DEBUG:1,ADVISORY:2,NORMAL:3,WARNING:4,ERROR:5,FATAL:6,NONE:7},
				entry_definitions:[
					["Generic Error","An error occured.",100],

					["Status","[specify status message]",200],
					["Status","Created new instance.",201],
					["Status","Initialized.",202],
					["Status","Updated status.",203],

					["Operation Successful","The operation was successfully completed.",210],
					["Configuration Successful","An application component was successfully configured.",211],
					["Network Operation Successful","The network operation was successfully completed.",280],
					["Partial Operation Successful","A partial operation was successfully completed.",290],
					["Redirection","Operation was redirected to another process.",300],
					["Data Source Not Found","The specified data source was not found.",400],
					["File Not Found","The specified file source was not found.",410],		
					["Application Error","An application error occured.",500],
					["Application Configuration Error","An application error occured due to a missing or an incorrect configuration.",510],
					["Application Configuration Error","Invalid argument.",511],
					["Application Configuration Error","Object reference does not exist.",512],
					["Application Event Error","An error occured processing the event.",515],
					["Application Event Halted","An event handler was halted, probably because a UI element was mistakenly exposed.",516],
					["Late Callback Error","Late Callbacks are an internal event handling system.  Since only one late callback may be assigned per object, this error probably originated from multiple late callbacks being assigned at the same time.",517],
					["Custom Application Event Error","An error occured while processing an internal(custom) event.",518],
					["Internal Application Error","An internal application error occured, probably due to incomplete code.",520],
					["Internal Routine Halted","An internal routine was halted, probably because it would have caused an out-of-bounds exception, or it was going to exceed a pre-configured limit.",530],
					["Invalid Data Type","An unexpected data type was referenced.",540],
					["External Application Error","An external error occured with a referenced application.",550],
					["Network Connection Error","An error occured while connecting to the network.",600],
					["Network Configuration Error","An error occured while attempting to bind to a socket.",601],	
					["Network Disconnect Error","An error occured while disconnecting from the network.",610],
					["Network Timeout Error","An error occured because there was no network response (timed out).",620],
					["Network Transfer Error","An error occured while transferring data on the network.",650],
					["Network Transfer Error","An error occured while writing data to a socket.",651],
					["Network Transfer Error","An error occured while reading data from a socket.",652],
					["Syntax Error","The specified value is either not acceptible or not recognized.",700],
					["Input Required","You may not continue without specifying more information.",710]
				]
			}

		};


		t.setDeliveryDelay=function(i){
			if(typeof i == "number" && i >= 0){
				this.object_config.status.delivery_delay = i;
			}
		};

		t.subscribe=function(o,e,f,v){
			var c;
			c=t.object_config;
			
			if(arguments.length==4){
				if(!o) o=window;
				if(!v) v=null;
			}
			if(arguments.length==2){
				
				var t1=arguments[0],t2=arguments[1];
				o=window;
				e=t1;
				f=t2;
			}
	
			c.pointers.subscriptions[c.pointers.subscriptions.length]=org.cote.js._forName("basic_message_subscription",o,e,f,v);

		};

		t.unsubscribe=function(o,e,f){
			/*
				o = object
				e = event
				f = function pointer
			*/
			var t=this,a=[],l,i=0,z,c;
			c = t.object_config;
			
			if(arguments.length==2){
				var t1=arguments[0],t2=arguments[1];
				o=window;
				e=t1;
				f=t2;
			}
	
			l=c.pointers.subscriptions.length;
			for(;i<l;i++){
				z = c.pointers.subscriptions[i];
				if(
					z.object != o
					|| z.event_name != e
					|| z.function_pointer != f
				){
					a[a.length]=z;
				}
			}
			c.pointers.subscriptions=a;
		};

		/*
			sigterm is used to instruct the message service that it should stop
			processing delayed messages, move its readystate to an unloading state,
			and set the delivery delay to 0, which is immediate.
			
			Clear out all publications
		*/
		t.sigterm=function(){
			var t=this,c;
			c = t.object_config;
			t.ready_state = 5;
			c.status.delivery_delay=0;
			c.pointers.delayed_deliveries=[];
		};

		t._delayPublish=function(e,i){
			var t=this,d,c;
			c = t.object_config;
			if(t.ready_state != 4) return;
			if(typeof i == "string"){
				d = c.pointers.delayed_deliveries[i];
				t._publish(e,d);
				c.pointers.delayed_deliveries[i] = null;
			}
		};
		t.publish=function(e,o){

			var t=this,c,x,d;
			c = t.object_config;
			
			if(c.status.delivery_delay){
				d = c.pointers.delayed_deliveries;
				x = org.cote.js._get_gunid();
				d[x] = o;
				setTimeout("org.cote.js._application_scope.get_object('" + t.object_id + "')._delayPublish('" + e + "','" + x + "')",c.status.delivery_delay);
			}
			else{
				t._publish(e,o);
			}
		};
		t._publish=function(e,o){
			var t=this,c,j,l,i=0,z,x,d;

			c = t.object_config;
/*			j=org.cote.js._forName("basic_message_publication",e,o);*/
			l=c.pointers.subscriptions.length;
			for(;i<l;i++){
				z=c.pointers.subscriptions[i];
				if(
					z.subscription_name==e
					&& (!z.target || z.target==o)
				){

					try{
						if(typeof z.handler == "string")
							z.object[z.handler](e,o);
						
						if(typeof z.handler == "function")
							z.handler(e,o);

					}
					catch(e){
						alert("Publish Error: " + e.message + "\n" + z.subscription_name + "\n" + z.handler );
					}

				}
			}
		};
		
		t.setReportThreshold=function(i){
			var c=t.object_config;
			if(typeof i == "number") i = c.data.message_levels[i];
			if(typeof c.data.message_levels_map[i] == "number"){
				c.status.report_threshold = c.data.message_levels_map[i];
			}
		};

		t.sendMessage=function(d,s,p){
			/*
				d = data
				xxx l = level
				p = type
				s = string
				o = object
				t = this
			*/
			
			var o=null,v,c,t=this,ms;
			c=t.object_config;

			if(typeof s != "string") s = "200";

			v = t.parseMessageInstruction(s);

			if(v.threshold < c.status.report_threshold){
				return o;
			}

			if(typeof p != "number") p = 0;
		
			if(p) o=t._raiseBasicMessage(s,d,v);
			else o=t._createBasicMessage(s,d,v);

			ms = t.parseMessage(o);
			if(ms==null) ms = "[message error]";

			t.publish("onsendmessage",{message:ms,level:v.threshold,description:o.description});


			return o;
		};
		t._raiseBasicMessage=function(s,d,v){
			var t=this,o;
			
			o=t._createBasicMessage(s,d,v);
			
			alert(t.parseMessage(o));
			return o;
		};
		t._createBasicMessage=function(s,d,v){
			var o=org.cote.js._forName("basic_message",v,d),i,c,t=this;
			c = t.object_config;
			i = c.pointers.entries.length;
			
			if(i >= c.status.maximum_entries && c.status.maximum_entries > 0){
				c.pointers.entries.shift();
				i--;
			}
			
			o.index=i;
			c.pointers.entries[i]=o;
			return o;
		};
		

		t.parseMessage=function(o){
			var v = "[error]",a,l,d,c,t=this;
			c = t.object_config;

			if(typeof o=="object"){
				a=t._getMessageDefinition(o);

				d = o.time;
				
				var minutes = new String(d.getMinutes()),
					seconds = new String(d.getSeconds()),
					mseconds = new String(d.getMilliseconds())
				;

				if(seconds.length == 1) seconds="0" + seconds;
				if(minutes.length == 1) minutes = "0" + minutes;

				if(mseconds.length == 1) mseconds = "0" + mseconds;
				if(mseconds.length == 2) mseconds = "0" + mseconds;

				
				v =	d.getHours() + ":" + minutes + ":" + seconds + ":" + mseconds + "::";

				v+=t._getMessageStatusCode(o.entry.threshold) + ": ";


				v+=(a!=null?a[0]:"Default Message");

				v+=" (" + o.entry.message + ").";
				o.description = (a!=null?a[1]:""); /* was a[2]*/
				if(o.data){
					v+=" " + o.data;
				}
				else{
					v+=" " + o.description;
				}
			}

			
			return v;
		};
		t._getMessageStatusCode=function(i){
			return this.object_config.data.message_levels[i];
		};
	
		t._getMessageDefinition=function(o){
			var v=null,f=100,l,m,n,i=0,a,d,c,t=this;
			c = t.object_config;
			/*
				v = return value
				f = offset
				l = length
				m = major
				n = minor
				t = this
			*/
			if(typeof o=="object"){
				l=c.data.entry_definitions.length;
				m=o.entry.major_code;
		
				n=o.entry.minor_code;

				for(;i<l;i++){
					a=c.data.entry_definitions[i];
					if(a.length>=2){
						d=a[2];
						if(m >= d && m < (d + f))
							v=a;
						
						if(v != null && m > (d + f)) break;
					}
				}
			}	

			return v;
		};

		t.parseMessageInstruction=function(s){
			var a=[],c,i=100,o,m=0,n,x=900,d,p,y;
			o=i;
			if(s) a=s.split(".");
			/*
				 a = array
				 c = code
				 i = offset
				 o = offset
				 x  = max
				 m = major
				 n = minor
				 l = loc
				 d = class
				 p = id
				 
			*/
			
			c=(a[0])?parseInt(a[0]):200;
		
			for(y=i;y<=x;y+=100){
				if(c >= i && c < (y + i))
					m=i;
				
				if(m > 0 && m <= (y+100)) break;
			}
		
			if(m < 0 || m > 900 || c < 0 || c > 900){
				m=200;
				c=200;
			}
		
			n=c - m;
			d=(a[1])?parseInt(a[1]):3;
			if(d > 7 || d < 0)d=3;
			l=(a[2])?parseInt(a[2]):0;
			p=(a[3])?parseInt(a[3]):0;

			return {
				"major_code":c,
				"major_base":m,
				"minor_code":n,
				"threshold":d,
				"location":l,
				"id":p,
				"description":"",
				"message":c + "." + d + "." + l + "." + p
			};

		};

		org.cote.js._implements(t,"base_object","message_service","0.1a");

		org.cote.js._application_scope.add_object(t);
				
		t.ready_state = 4;
	}
}
org.cote.js.message.MessageService=new org.cote.js.message.MessageServiceImpl();
