function Subscriber(topic, obj, fn) {
//debugger
	var self = this;
	if (!fn) {
		self.obj = gadgetManager.getObj(obj[0]);
		self.fn = obj[1];
	} else {
		self.obj = obj;
		self.fn = fn;
	}
	dojo.event.topic.subscribe(topic, self.obj, self.fn);
	
	self.toString = function() {
		return "['"+self.obj.id+"','"+self.fn+"']";
	}
}

function Fun(owner, obj, name, des, hidden, para) {
	var self = this;
	self.owner = owner;
	self.obj = obj;
	self.name = name;
	self.des = des;
	self.hidden = hidden;
	self.para = para;
	//debugger
	self.getObj = function() {
		if ((typeof self.obj) == "object")
			return self.obj;
		return eval(self.obj);
		
	}
}

function Publisher(topic, obj, fn, des) {
//debugger
	var self = this;
	if (!fn) {
		self.obj = gadgetManager.getObj(obj[0]);
		self.fn = obj[1];
		self.des = obj[2];
		
	} else {	
		self.obj = obj;
		self.fn = fn;
		self.des = des||fn;
	}
	dojo.event.topic.registerPublisher(topic, self.obj, self.fn);
	
	self.toString = function() {
		var d = self.des||"";
		return "['"+self.obj.id+"','"+self.fn+"','"+d+"']";
	}
	
}

function Link(srcObj, srcFunc, adviceObj, adviceFunc) {
	var self = this;
	self.srcObj = srcObj;
	self.srcFunc = srcFunc;
	self.adviceObj = adviceObj;
	self.adviceFunc = adviceFunc;	
		
	self.connect = function() {
		dojo.event.connect(self.srcObj, self.srcFunc, self.adviceObj, self.adviceFunc);		
		gadgetManager.linkMap["{"+self.srcObj.id+","+self.adviceObj.id+"}"] = self;
	}
	self.disconnect = function() {
		dojo.event.disconnect(self.srcObj, self.srcFunc, self.adviceObj, self.adviceFunc);
		delete gadgetManager.linkMap["{"+self.srcObj.id+","+self.adviceObj.id+"}"];
	}
}

function Topic(topic) {

	var self = this;
	self.isEmpty = function() {
		if (!isArrayEmpty(self.subscriberList))
			return false;
		if (!isArrayEmpty(self.publisherList))
			return false;
		return true;	
	}
	self.update = function(topic) {		
		for (var i=0; i<topic[1].length; i++) {
			var sub = new Subscriber(self.topic, topic[1][i]);
			//debugger
			self.subscriberList[sub.obj.id] = sub;
		}
		for (var i=0; i<topic[2].length; i++) {
			var pub = new Publisher(self.topic, topic[2][i]);
			self.publisherList[pub.topic] = pub;
		}
	}
	
	self.getAvailableSub = function(fn) {
		if (!fn)
			return getFirst(self.availableSubList);
		return self.availableSubList[fn];		
	}
	self.addAvailableSub = function(fun) {
		if (!self.availableSubList)
			self.availableSubList = {};
		self.availableSubList[fun.name] = fun;		
	}
	self.removeAvailableSub = function(name) {
		var sub = self.availableSubList[name];
		if (sub) {
			delete self.availableSubList[name];
		}
	}
	
	self.subscribe = function(obj, fn) {
		self.subscriberList[obj.id] = new Subscriber(self.topic, obj, fn);		
		if (obj.fg)
			obj.fg["opt"]["Sub"][self.topic] = fn;
	}
	self.unsubscribe = function(id) {
		var sub = self.subscriberList[id];
		if (sub) {
			dojo.event.topic.unsubscribe(self.topic, sub.obj, sub.fn);
			delete self.subscriberList[id];
		}
	}
	
	self.registerPublisher = function(obj, fn, des) {
		self.publisherList[obj.id] = new Publisher(self.topic, obj, fn, des);		
		if (obj.fg)
			obj.fg["opt"]["Pub"][self.topic] = fn;
	}
	
	self.unregisterPublisher = function(id) {
		var pub = self.publisherList[id];
		if (pub) {
			//dojo.event.topic.unregister(self.topic, pub.obj, pub.fn);
			delete self.publisherList[id];
		}
	}
	
	self.addAvailablePub = function(fun) {
		if (!self.availablePubList)
			self.availablePubList = {};
		self.availablePubList[fun.name] = fun;		
	}
	
	self.getAvailablePub = function(fn) {
		if (!fn)
			return getFirst(self.availablePubList);
		return self.availablePubList[fn];		
	}
	
	self.removeAvailablePub = function(name) {
		var pub = self.availablePubList[name];
		if (pub) {
			delete self.availablePubList[name];
		}
	}
	
	self.clearById = function (id) {
		var b = false;
		var s = self.subscriberList[id];
		if (s) {
			self.unsubscribe(s.obj, s.fn);
			b = true;
			delete self.subscriberList[id];
		}
		s = self.publisherList[id];
		if (s) {
			self.unregisterPublisher(s.obj, s.fn);
			b = true;
			delete self.publisherList[id];
		}
		s = self.availableSubList;
		if (s) {
			for (i in s) {
				if (s[i].owner==id) {
					b = true;
					delete s[i];
				}
			}
		}
		s = self.availablePubList;
		if (s) {
			for (i in s) {
				if (s[i].owner==id) {
					b = true;
					delete s[i];
				}
			}
		}
		return b;
	}
	

	
	self.toString = function() {
		var res = "";
		//debugger
		res += "['"+self.topic+"',[";
		var j = 0;
		for (var i in self.subscriberList) {
			if (j++>0)
				res +=","
			res += self.subscriberList[i].toString();
		}
		res += "],["
		j = 0;
		for (var i in self.publisherList) {
			if (j++>0)
				res +=","
			res += self.publisherList[i].toString();
		}
		res += "]]";
		
		return res;
	}
	
	self.subscriberList = {};
	self.publisherList = {};
	self.availableSubList = null;
	self.availablePubList = null;	
	self.private = 0;	
	if ((typeof topic)=="string") {
		self.topic = topic;
	
	} else {
		self.topic = topic[0];
		self.update(topic);
	}
	
}

function GadgetManager() {
  	var self = this;
  	
  	self.gadgetMap = {};
  	self.topicMap = {};
  	self.linkMap = {};
  	self.topicFunMap = {};
  	self.funMap = {};
  	self.id = "GadgetManager";
  	self.tbTable = {};
  	
  	self.mapTopicFun = function(topic, ff) {
  		var i = ff.name.indexOf("_");
		if (i<0) {
			self.topicFunMap[topic] = ff;
		} else {
			var id = ff.name.substring(i+1);
			var funs = 	self.topicFunMap[topic];
			if (!funs) {
				funs = {}
				self.topicFunMap[topic] = funs;
			}
			funs[id] = ff;
		}
  		
  	}
  	self.getTopicFun = function(topic, id) {
  		if (!id) {
  			return self.topicFunMap[topic];
  		}
  		var res = self.topicFunMap[topic];
  		if (!res) {
  			//alert("Topic "+topic+" not found");
  			return null;
  		}
  		return res[id];
  	}
  	
  	self.getTopic = function(topic, force) {
		var res = self.topicMap[topic];
		if (force && !res)
			res = self.addTopic(topic);
		return res;
	}
	
  	self.subscribe = function(topic, obj, fn, private) {
  		var t = self.topicMap[topic];
  		if (!t) {
  			t = new Topic(topic);
  			self.topicMap[topic] = t;
  		}
		t.subscribe(obj, fn);
			//debugger
		t.private = private||t.private;
	}
	self.unsubscribe = function(topic, id) {
  		var t = self.topicMap[topic];
  		if (!t) {
  			return;
  		}
		t.unsubscribe(id);
	}
	
	self.registerPublisher = function(topic, obj, fn, des, private) {

  		var t = self.topicMap[topic];
  		if (!t) {
  			t = new Topic(topic);
  			self.topicMap[topic] = t;
  		}
		t.registerPublisher(obj, fn, des);
			//debugger
		t.private = private||t.private;
	}
	self.unregisterPublisher = function(topic, id) {
  		var t = self.topicMap[topic];
  		if (!t) {
  			return;
  		}
		t.unregisterPublisher(id);
	}
	
	self.clearTopic = function (id) {
		var res = [];
		for (var i in self.topicMap) {
			if (self.getTopic(i).clearById(id))
				res[res.length] = i;
		}
		return res;
	}
	
	self.clearGadget = function (id) {

		self.clearLink(id);
		var deletedTopic = self.clearTopic(id);
		self.clearFun(id);		
		
		self.clearOtherGadgets(id, deletedTopic);
		for (var i=0; i<deletedTopic.length; i++) {
			var t = self.topicMap[deletedTopic[i]];
			if (t.isEmpty())
				delete self.topicMap[deletedTopic[i]];
		}
	}
	
	self.clearOtherGadgets = function(id, deletedTopic) {
		for (var i in self.gadgetMap) {
			var g = self.gadgetMap[i];
			g.clearFunById(id);		
		}
		for (var i in self.gadgetMap) {
			var g = self.gadgetMap[i];
			g.clearTopicById(id, deletedTopic);		
		}
	}
	
	self.connect = function(srcObj, srcFunc, adviceObj, adviceFunc) {
		var link = new Link(srcObj, srcFunc, adviceObj, adviceFunc);
		link.connect();
	}
	
	self.disconnect = function(id1, id2) {
		var id = "{"+id1+","+id2+"}";
		var link = self.linkMap[id];
		if (link) {
			link.disconnect();
			return true;
		}
		
		return false;
	}
	
	self.disconnectById = function(id) {
		var res = false;
		var m = self.linkMap;
		for (var i in m) {
			var k = eval(m[i]);
			if (k[id]) {
				self.linkMap[i].disconnect();			
				res = true;
			}
		}
		return false;
	}
	
	
	self.clearLink = function (id) {
		self.disconnectById(id);
	}
	
	self.addFun = function(owner, fn, fun, des, obj, hidden, para, topic, private) {
	//debugger;
		if (!obj)
			obj = self;
		obj[fn] = fun;
		gadgetManager.funMap[fn] = new Fun(owner, obj, fn, des, hidden, para);
		if (topic) {
			var t = gadgetManager.topicMap[topic];
			if (!t) {
				t = gadgetManager.topicMap[topic] = new Topic(topic);
			}
			t.addAvailableSub(gadgetManager.funMap[fn]);
			//gadgetManager.subscribe(topic, obj, fn);
			t.private = private||t.private;
		}
		return gadgetManager.funMap[fn];
	}
	
	self.getFun = function(fn) {
		return self.funMap[fn];
	}

	self.clearFun = function (id) {
		self.removeFunById(id);
		self.removeFunByOwner(id);
	}
	
	self.removeFunById = function(id) {
		var res = new Array();
		for (var i in self.funMap) {
			var f = self.funMap[i];
			if (f.obj.id==id)
				res[res.length] = i;
		}
		for (var i=0; i<res.length; i++) {
			delete self.funMap[res[i]];
		}
	}
	
	self.removeFunByOwner = function(id) {
		var res = new Array();
		for (var i in self.funMap) {
			var f = self.funMap[i];
			if (f.owner==id)
				res[res.length] = i;
		}
		for (var i=0; i<res.length; i++) {
			delete self.funMap[res[i]];
		}
	}
	
	self.getGadget = function(id) {
		return self.gadgetMap[id];
	}

	
	self.defineDefaultFunctions = function() {
	
		self.addFun(self.id, "hideAllGadgets", (function () {
			return function() {
				    for (var i in self.gadgetMap) {
				    	var g = self.gadgetMap[i];
			        	g.tr.style.display = "none";
			        }
		    }
		})(), "Hide all gadgets");
		self.addFun(self.id, "hideAllGadgets", (function () {
			return function() {
				    for (var i in self.gadgetMap) {
				    	var g = self.gadgetMap[i];
			        	g.tr.style.display = "none";
			        }
		    }
		})(), "Hide all gadgets");
		self.addFun(self.id, "showAllGadgets", (function () {
			return function() {
				    for (var i in self.gadgetMap) {
				    	var g = self.gadgetMap[i];
			        	g.tr.style.display = "block";
			        }
		    }
		})(), "Show all gadgets");
		self.addFun(self.id, "minimizeAllGadgets", (function () {
			return function() {
				    for (var i in self.gadgetMap) {
				    	var g = self.gadgetMap[i];
			        	g.wipeOut();
			        }
		    }
		})(), "Minimize all gadgets");
		self.addFun(self.id, "restoreAllGadgets", (function () {
			return function() {
				    for (var i in self.gadgetMap) {
				    	var g = self.gadgetMap[i];
			        	g.wipeIn();
			        }
		    }
		})(), "Restore all gadgets");
		
	}
	
	self.doAfterLoad = function() {
		self.defineDefaultFunctions();
		gadgetManager.deserializeEvents(readCookie("events"));
	}
	
	self.addTopic = function(topic) {
		return self.topicMap[topic] = new Topic(topic);
	}
	
	self.serializeEvents = function() {
	//debugger
		var res = "[";
		var j=0;
		for (var i in self.topicMap) {
			if (j++>0)
				res +=",";
			res += self.topicMap[i].toString();
		}
		res +="]";
	//msg(res)		
		return res;
	}
	
	self.deserializeEvents = function(s) {
	//msg(s)
	//debugger
		if (!s)
			return;
		var ss = eval(s);
		for (var i=0; i<ss.length; i++) {
			//debugger
			var t = self.getTopic(ss[i][0]);
			if (t) {
				t.update(ss[i]);
			} else {
				t = new Topic(ss[i]);
				self.topicMap[t.topic] = t;				
			}
		}
		
	}
	
	self.getObj = function(id) {
		var res = null;
		if (id=="GadgetManager") 
			res = this;
		else if (id=="_win_")
			res = window;
		else  {
			res = self.gadgetMap[id];
			if (!res)
				res = dojo.byId(id);
		}
		return res;
	}
	

}

var gadgetManager = new GadgetManager();

