var WU;
var url = "http://localhost:1112/adsWebService.asmx";
var ns  = "http://idatalink.com/";
var tracking;
/**
 * Main controlling object for the weblink
 * 
 * @return WeblinkUpdater WeblinkUpdater Object
 */
function Weblink_Updater(){
	
	that = WU = this;

	//throw 'bullshit';
	this.$ = $(WU);
	
	var pluginInterface;
	var userInterface;
	var devices = [];
	var services = [];
	
	this.isBusy = function(){
		if(pluginInterface){
			return pluginInterface.isBusy();
			}
	}
	
	var registerPluginInterface = function(plugin){
		if(plugin instanceof WU_Abstract){
			//alert('waaaa')
			plugin.setWU(WU);
			pluginInterface = plugin;
			services = plugin.getServices();
			return true;
		} else {
			WU.errorHandle(new InvalidPluginException());
		}
	};
	var registerUserInterface = function(plugin){
		if(plugin instanceof Weblink_UserInterface){
			plugin.setWU(WU);
			userInterface = plugin;
			plugin.init();
			return true;
		}
		return false;
	};
	
	this.getPluginInterfaceVersion = function(bool){
		if(!bool) bool = false;
		return pluginInterface.getVersion(bool);
	};
	
	registerUserInterface(new Weblink_UserInterface(document.body));
	
	var callDetectPlugin = function(){
		
		function getVersion(){
			
			function is5(){
				if(!in_array(5,Weblink_Versions)){
					throw new Weblink5NotFoundException();
				}
				//throw new Weblink5NotFoundException();
				if(typeof(ActiveXObject)!="function") throw new ActiveXNotSupportedException();
				try{
					var ActiveX = new ActiveXObject("adsService.Service");
				} catch(E){
					throw new Weblink5NotFoundException();
				}

				if(typeof(ActiveX)!="object") throw new Weblink5FailedToLoadException();
				//throw new Weblink5NotFoundException();
				WU.$.triggerHandler('versionDetected',[5,ActiveX]);
			}
			
			function is3or4(){
				function getPluginVersion(){
					
					try{
						var method = "plugInVersion";
						var body = buildRequest(method,ns,"");
						var xhr = new FlashXMLHttpRequest();
						//alert(7);
						xhr.onload = function() {
							//alert(8);
							pluginversion = rinse(xhr.responseText,method+'Result');
							if(pluginversion < "1.2.00"){
								WU.$.triggerHandler('versionDetected',[3]);
							} else if(pluginversion >= "1.2.1"){
								WU.$.triggerHandler('versionDetected',[4.1]);
							} else {
								WU.$.triggerHandler('versionDetected',[4]);
							}
							//alert(9);
							
						};
						
						xhr.onerror = function() {
							
							WU.errorHandle(new WeblinkNotRespondingException());
							};	
						xhr.open("POST", url);
						xhr.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
						xhr.send(body);
						
					} catch(e){
						alertX(e);
						}
				}
				
				var imgsrc = 'http://localhost:1112/idatalink.jpg?date='+ new Date();
				var img = new Image();
				img.onerror = function (evt) {
					WU.$.triggerHandler('versionDetected',[null]);
					};
				img.onload = function (evt) {
				  	getPluginVersion();
					};
				img.src = imgsrc;
			}

			
			WU.$.bind('versionDetected',function(evt,ver,ax){
				//alert('get');
				try {
					//alert(ver);
					versionDetected(ver,ax);
				} catch(e){
					alertX(e);
					//WU.errorHandle(e);
				}
			});
			
			try {
				//Tell UI that plugin detection starts
				WU.$.triggerHandler('pluginDetectStart');
				//try to detect 5
				is5();
			} catch(e) {
				//If not 5, see why
				if(e instanceof ActiveXNotSupportedException){
					//If activeX is not supported, 3 or 4 will not work either.
					//Then trigger missing requirements page, see UserInterface for implementation
					WU.$.triggerHandler('pluginRequirementsMissing',e);
				}
				if(e instanceof Weblink5NotFoundException){
					//if version 5 is not detected, try to detect version 3 or 4
					is3or4();
				}
			}
		}
		
		function versionDetected(ver,ax){
			try{
				//If the plugin version is not in the Weblink_Versions array, treat as null
				if(!in_array(ver,Weblink_Versions)){
					ver = null;
				}
				//Register appropriate PluginInterface
				switch(ver){
				case 3 : registerPluginInterface(new WU3()); break;
				case 4 : registerPluginInterface(new WU4()); break;
				case 4.1 : registerPluginInterface(new WU41()); break;
				case 5:  
					registerPluginInterface(new WU5(ax)); 
					//create tracking object
					tracking = new Weblink_Tracking(ax);
					break;
				//If version invalid, such as null, throw plug not found and return
				default: WU.$.trigger('pluginDetectError', [new WeblinkNotDetectedException('No Weblink plugin has been detected')]);return;
				}
				//If we have a working pluginInterface, trigger as ready, to be handled by UserInterface
				WU.$.triggerHandler('pluginReady');
			} catch(e){
				WU.$.trigger('pluginDetectError', [new WeblinkNotDetectedException('An error occured while initializing plugin.')]);
			}
		}
		
		getVersion();
		
	};

	this.callDetectDevice = function() {
		
		function deviceDetected(evt,type,infos){
			//Type should be integer. see Weblink_Types
			//Infos is object {'Serial':value,'BootLoader':value, etc...}
			WU.addDevice(type,infos);
		}
		//When device is detected, add it to devices array
		WU.$.bind('deviceDetected',deviceDetected);
		
		//Tell UI that devices are being probed/detected
		WU.$.triggerHandler('deviceDetectStart');
		
		//Tell plugin to try to detect devices
		pluginInterface.detectDevices();
	};
	
	//Function to add device to device list
	this.addDevice = function(type,infos){
		/*
		for(x in devices){
			//if type already in device list, 
			if(devices[x].getType() & type){
				try {
					var device = new Weblink_Device(type,infos);
					devices.push(device);
					return device;
				} catch(e){
					alertX(e);
					return false;
				}
			}
		}
		*/
		//If weblink_device is provided, just push into list
		if(type instanceof Weblink_Device){
			var device = type ;
			devices.push(device);
			return device;
		}
		//Otherwise, create one
		try {
			var device = new Weblink_Device(type,infos);
			devices.push(device);
			return device;
		} catch(e){
			alertX(e);
			return false;
		}
		
	};

	
	this.getDevices = function(){
		return devices;
	};
	
	this.getDevice = function(type,infos){
		if(null == type && null == infos) return null;
		for(x in devices){
			if(null != type){
				if(!(devices[x].getType() & type)) continue;
			}
			if(null != infos && typeof(infos)=='Object'){
				for(y in infos){
					var info = devices[x].get(y);
					if(info == null || infos[y]!=info) continue;
				}
			}
			return devices[x];
		}
		return null;
	};
	
	this.updateDeviceConfiguration = function(device){
		function updateModuleConf(device){
			//Lock eeprom object as to avoid modifications between writing and confirmation
			//When updated, unlock
			WU.$.one('deviceEepromUpdated',function(){device.eeprom.busy = false;})
			
			//if no eeprom object exists, create it
			if(!device.eeprom || !(device.eeprom instanceof Weblink_Device_ModuleConf)){
				device.eeprom = {busy : true};
				//If read success, go server side to validate readings
				WU.$.unbind('readComplete');
				WU.$.unbind('readError');
				WU.$.one('readComplete',function(evt,data){
					device.eeprom = data;
					device.eeprom.busy = true;
					var SI = new Weblink_ServerInterface();
					SI.$.one('dataLoaded',function(evt,data){device.eeprom.map = data; WU.$.triggerHandler('deviceEepromUpdated');});
					SI.getOptions(device,data);
				});
				//If fail, bubble up event
				WU.$.one('readError',function(){
					WU.$.triggerHandler('deviceEepromUpdateFailed');
				});
				//Try to read...
				WU.readConfiguration(device);
				return;
			}
			
			//If eeprom exists and changes have been requested, write to device
			var changes = device.eeprom.getChanges();
			if(changes && changes.length!=0 && device.eeprom.busy!=true){
				
				var SI = new Weblink_ServerInterface();
				//when Map from server, write to memory
				SI.$.one('dataLoaded',
						function(evt,data){ 
							var conf = new Weblink_Device_ModuleConf(data.eeprom);
							conf.map = data.map;

							WU.$.unbind('writeComplete');
							WU.$.unbind('writeError');
							WU.$.one('writeComplete',
									function(){
										if(typeof(tracking) != "undefined"){
											$.each(changes, function(key, value){
												if(typeof(value) != "undefined")
													tracking.addInfo("Changed the feature id: "+key+" to "+value);
											});
										}
										conf.changes = device.eeprom.changes;
										device.eeprom = conf;
										WU.$.triggerHandler('deviceEepromUpdated');
									});
							WU.$.one('writeError',
									function(){
										if(typeof(tracking) != "undefined"){
											$.each(changes, function(key, value){
												if(typeof(value) != "undefined")
													tracking.addInfo("Failed to change the feature id: "+key+" to "+value);
											});
										}
									});
							WU.writeConfiguration(device,conf);
						}
				);
				//Get Map from server
				SI.setOptions(device,device.eeprom);
				//Set reset eeprom change stack
				device.eeprom.resetChanges();
				return;
			}
			WU.$.triggerHandler('deviceEepromUpdated');
		}

		function updateStarterConf(starter){
			WU.$.one('starterEepromUpdated',function(){starter.eeprom.busy = false;});
			if(!starter.eeprom || !(starter.eeprom instanceof Weblink_Device_StarterConf)){
				starter.eeprom = {busy : true};
				function getFeatures(evt,data){
					if(!data.addresses){
						WU.$.triggerHandler('updateConfigurationError');
						return false;
					}
					WU.$.unbind('readComplete');
					WU.$.unbind('readError');
					WU.$.one('readComplete',
							function(evt,data){
								device.eeprom = data;
								WU.$.triggerHandler('starterEepromUpdated');
							}
					);
					WU.$.one('readError',
							function(evt,exc){
						WU_Interface.WU.$.triggerHandler('readError',[e]);WU.$.triggerHandler('updateConfigurationError');
								}
					);
					WU.readConfiguration(device,data.addresses);
				}
			
				var SI = new Weblink_ServerInterface();
				SI.$.one('dataLoaded',getFeatures);
				SI.getStarterFeatureAddresses(starter.get('Firmware'));
				return;
				}
			
			var changes = starter.eeprom.getChanges();
			if(changes && changes.length!=0 && starter.eeprom.busy!=true){
				var SI = new Weblink_ServerInterface();
				SI.$.one('dataLoaded',
						function(evt,data){ 
							var conf = new Weblink_Device_StarterConf(data.eeprom);
							WU.$.unbind('writeComplete');
							WU.$.unbind('writeError');
							WU.$.bind('writeComplete',
									function(){
										if(typeof(tracking) != "undefined"){
												tracking.addInfo("Changed the starter features: "+changes);
											}
										
										conf.changes = starter.eeprom.changes;
										starter.eeprom = conf;
										WU.$.triggerHandler('starterEepromUpdated');
										}
							);
							WU.$.bind('writeError',
									function(evt,exc){
										if(typeof(tracking) != "undefined"){
											tracking.addInfo("Failed to change the starter features: "+changes);
										}
										WU.$.triggerHandler('updateConfigurationError');
										}
							);
							WU.writeConfiguration(starter,conf);
						}
				);
				SI.setStarterFeatures(starter.get('Firmware'),starter.eeprom);
				starter.eeprom.resetChanges();
				return;
			}
			WU.$.triggerHandler('starterEepromUpdated');
		}
		
		if(device.isModule()){
			updateModuleConf(device);
		} else if(device.isStarter()){
			updateStarterConf(device);
		}
			
	};
	
	this.isAllowed = function(serviceName, options){
		for(var x=0; x<services.length; x++){
			if(services[x].getName() == serviceName){
				if(options){
					return services[x].validate(options);
				}
				return true;
			}
		}
		return false;
	};
	
	this.readConfiguration = function(device,conf){
		pluginInterface.readConfiguration(device,conf);
	};
	this.writeConfiguration = function(device,conf){
		pluginInterface.writeConfiguration(device,conf);
	};
	this.unlock = function(){
		pluginInterface.unlock();
	};
	this.updateFirmware = function(device, firmware) {
		if(!device || !(device instanceof Weblink_Device)){
			throw new InvalidDeviceException();
		}
		
		pluginInterface.flash(device,firmware);
	};
	
	callDetectPlugin();
}

