function WU4(){
	var WU_Interface = this;
	this.WU;
	var Busy = false;
	this.isBusy = function(){
		return Busy;
	}
	this.setWU = function(w){
		WU_Interface.WU = w;
	};
	this.version = 4;
	this.getVersion = function(bool){
		if(bool) return {version:that.version,revision:'1'};
		return that.version;
	}
	var services = [];
	
	var service = new Weblink_Service('detectDevices');
	services.push(service);
	var service = new Weblink_Service('writeConfiguration');
	service.addValidator(Weblink_Validators.FEATURE_EDITABLE);
	services.push(service);
	var service = new Weblink_Service('readConfiguration');
	service.addValidator(Weblink_Validators.FEATURE_EDITABLE);
	services.push(service);
	var service = new Weblink_Service('flash');
	service.addValidator(Weblink_Validators.NO_STARTER_UPDATE);
	services.push(service);
	                
	this.getServices = function(){
		return services;
	};
	
	/**
	 * Detects and broadcasts 
	 * dispatches: 'deviceDetected', 'deviceDetectComplete','deviceDetectError'
	 * throws: DeviceNotDetectedException,CableNotDetectedException,DetectionErrorException, PluginTimeOutException
	 */
	this.detectDevices = function(){
		if(Busy==true) return false;
		Busy = true;
		function getModule(){
			var Serial;
			var Boot;
			/**
			 * Get module bootloaderversion
			 */
			function detectModule()
			{
				function getModuleVersion(){
					var method = "getModuleInfo";
					var body = buildRequest(method,ns,"");
					var xhr = new FlashXMLHttpRequest();
					xhr.onload = function(res) {
						var sOut = rinseAll(res,method);
						
						var Serial = sOut['ModuleSerial'];
						var Boot = sOut['ModuleBootLoader'];
						if(Serial == 0 || Boot == 0){ getLink(); return;}
						
						var Type = Weblink_Types.getByBootLoader(Boot);
						if(Type == Weblink_Types.REMOTE_STARTER){
							WU_Interface.WU.$.triggerHandler('deviceDetected',[Type,{'BootLoader':Boot}]);
							return;
						}
						//$(document.body).html('detectModule1');
						WU_Interface.WU.$.triggerHandler('deviceDetected',[Type,{'Serial':Serial,'BootLoader':Boot}]);
						//$(document.body).html('detectModule2');
					};
						
					xhr.onerror = function()
					{
						Busy = false;
						return WU_Interface.WU.$.triggerHandler('deviceDetectError',[new PluginTimeOutException()]);
					};
					
					xhr.open("POST", url);
					xhr.setRequestHeader("Content-Type", "text/xml; charset=utf-8");   	
					xhr.send(body);
				}
			
				function getLink()
				{
					var method = "errorStatus";
					var body = buildRequest(method,ns,"");
					
					var xhr = new FlashXMLHttpRequest();
					xhr.onload = function(res)
					{ 
						var name = method + "Result";
						var sOut = rinse(res,name);
						if((/weblink/).test(sOut.toLowerCase()))
						{
							Busy = false;
							return WU_Interface.WU.$.triggerHandler('deviceDetectError',[new CableNotDetectedException()]);
						}
						else if((/module/).test(sOut.toLowerCase()))
						{
							Busy = false;
							return WU_Interface.WU.$.triggerHandler('deviceDetectError',[new DeviceNotDetectedException()]);
						}
					};
					xhr.open("POST", url);
					xhr.setRequestHeader("Content-Type", "text/xml; charset=utf-8");   	
					xhr.send(body);
					}

				getModuleVersion();
			}
			detectModule();
		}
		function getStarter(){
			function readStarterConf(rules){
				var method = "readStarterInfo";
				if(typeof(rules)=="object"){
					for(var i=0; i<rules.length; i++){
						rules[i] = rules[i].replace(/0x/i,'');
					}
					rules = rules.join('');
				} else {
					rules = rules.replace(/0x/i,'');
				}
				var byteString = new String(rules);
				var content = "<ep_address>" + byteString + "</ep_address>";
				var body = buildRequest(method,ns,content);
				var xhr = new FlashXMLHttpRequest();
				
				xhr.onload = function(res) {
					var result = rinse(res,method+'Result');
					if(result=="No device found!" || result=="Can't read starter"){
						WU_Interface.WU.$.triggerHandler('readError',[new ReadException()]);
						return;
					}
					var conf = new Weblink_Device_StarterConf(result);
					WU_Interface.WU.$.triggerHandler('readComplete',[conf]);
				};
				xhr.onerror = function(){
					WU_Interface.WU.$.triggerHandler('readError',[new PluginTimeOutException()]);
				};
				xhr.open("POST", url+"?date=" + new Date());
				xhr.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
				xhr.send(body);
			}
			
			// addresses = ['d5','d6','d7','d8'];
			var Starter = new Weblink_Device(Weblink_Types.REMOTE_STARTER,{});
			var BrandAddress = new Array();
			BrandAddress.push('81');
			
			WU_Interface.WU.$.one(
					'readComplete',
					function(evt,conf){
						var SI = new Weblink_ServerInterface();
						SI.$.one('errorLoading',function(){
							WU_Interface.WU.$.triggerHandler('starterDetectError');
						});
						SI.$.one('dataLoaded',function(evt,data){
							if(typeof(data.Manufacturer)=="undefined" || data.Manufacturer == false){WU_Interface.WU.$.triggerHandler('starterDetectError'); return;}
							Starter.setInfos(data);
							var hardwareSignature;
							var firmwareSignature;
							WU_Interface.WU.$.one('readComplete',
									function(evt,data){
										hardwareSignature = data;
										WU_Interface.WU.$.one('readComplete',
												function(evt,data){
													firmwareSignature = data;
													var SI = new Weblink_ServerInterface();
													SI.$.one('errorLoading',function(){
															WU_Interface.WU.$.triggerHandler('starterDetectError');
														});
													SI.$.one('dataLoaded',function(evt,data){
														for(x in data){
															Starter.setInfos(x,data[x]);
														}
														WU_Interface.WU.$.triggerHandler('deviceDetected',[Starter]);
														});
													SI.getStarterInfo(Starter.get('Manufacturer'),hardwareSignature,firmwareSignature,false);
												});
										readStarterConf(Starter.get('Manufacturer').FirmwareAddress);
									});	
							readStarterConf(Starter.get('Manufacturer').HardwareAddress);
						});
						SI.getStarterManufacturer(conf);
					});
			readStarterConf(BrandAddress);
			
			
		}
		var onDetect = function(evt,type,infos){
			//$(document.body).html('onDetect');
			//Temporarily disable starter detection until server code is ready
			if(type == Weblink_Types.BLADE_MODULE){
				WU_Interface.WU.$.one('starterDetectError',
						function(){
							Busy = false;
							WU_Interface.WU.$.triggerHandler('deviceDetectComplete');
				});
				getStarter();
			} else {
				Busy = false;
				WU_Interface.WU.$.triggerHandler('deviceDetectComplete');
				WU_Interface.WU.$.unbind('deviceDetected',onDetect);
			}
			
		};
		WU_Interface.WU.$.bind(
				'deviceDetected',onDetect
				);
		getModule();
		
	};
	
	/**
	 * Writes to device memory
	 * dispatches 'writeProgress','writeComplete','writeError'
	 * throws WriteException, PluginTimeOutException
	 */
	this.writeConfiguration = function(device, conf){
		if(Busy==true) return false;
		Busy = true;
		function getFlashStatus() {
			var method = "errorStatus";
			var body = buildRequest(method,ns,"");
			var xhr = new FlashXMLHttpRequest();
			xhr.onload = function(res) {
				sOut = rinse(res,"errorStatusResult");
				//document.getElementById('errortext').innerHTML = xhr.responseText;
				
				if (sOut=="Success.")
				{
					Busy = false;
					WU_Interface.WU.$.triggerHandler('flashComplete');
					return;
				}
				else if (sOut=="Flash starts.")
				{
					WU_Interface.WU.$.triggerHandler('flashDelay');
					setTimeout(getStatus,150);
					return;
				}
				else if(sOut=="Update Module features success.")
				{
					Busy = false;
					WU_Interface.WU.$.triggerHandler('writeComplete');
				}
				else if(sOut=="Update Module features failed.")
				{
					Busy = false;
					WU_Interface.WU.$.triggerHandler('writeError',[new WriteConfException(sOut)]);
				}
				else if(sOut=="Flash starter firmware starts.")
				{
					WU_Interface.WU.$.triggerHandler('flashDelay');
					setTimeout(getStatus,150);
					return;
				}
				else if(sOut=="Success update starter firmware")
				{
					Busy = false;
					WU_Interface.WU.$.triggerHandler('flashComplete');
					return;
				}
				else
				{
					Busy = false;
					WU_Interface.WU.$.triggerHandler('writeError',[new FlashException(sOut)]);	
				}		
			};
			
			xhr.open("POST", url);
			xhr.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
			xhr.send(body);
		}
		function writeModuleConf(conf){
			if(!(conf instanceof Weblink_Device_ModuleConf)){
				return WU_Interface.WU.$.triggerHandler('writeError',[new InvalidConfigurationException()]);
			}
		
			var method = "writeModuleInfo";
			var byteString = new String(conf.toString());
			var content = "<msg>"+byteString+"</msg>";
			var body = buildRequest(method,ns,content);
			var xhr = new FlashXMLHttpRequest();
			xhr.onload = function(res) {
				setTimeout(getFlashStatus,300);
				};
			xhr.open("POST", url+"?date=" + new Date());
			xhr.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
			xhr.send(body);
		}
		function writeStarterConf(conf){
			if(!(conf instanceof Weblink_Device_StarterConf)){
				return WU_Interface.WU.$.triggerHandler('writeError',[new InvalidConfigurationException()]);
			}
			var byteString = new String(conf.toWrite());
			var method = "writeStarterInfo";
			var content = "<msg>" + byteString + "</msg>";
			var body = buildRequest(method,ns,content);
			var xhr = new FlashXMLHttpRequest();
			
			xhr.onload = function(res) {
				
				Busy = false;
				if(/>0</.test(res)){
					WU_Interface.WU.$.triggerHandler('writeComplete');
				} else {
					return WU_Interface.WU.$.triggerHandler('writeError',[new WriteConfException()]);
				}
			};
			
			xhr.onerror = function() {
				Busy = false;
				return WU_Interface.WU.$.triggerHandler('writeError',[new PluginTimeOutException()]);
			};
			
			xhr.open("POST", url+"?date=" + new Date());
			xhr.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
			xhr.send(body);
		}
		
		switch(device.getType()){
			case Weblink_Types.MODULE:
			case Weblink_Types.BLADE_MODULE:
				writeModuleConf(conf);
				break;
			case Weblink_Types.REMOTE_STARTER:
				writeStarterConf(conf);
				break;
		}
		
	};
	
	/**
	 * Reads from device memory
	 * 
	 * dispatches 'readStart','readProgress','readComplete(result)','readError'
	 * throws ReadException, PluginTimeOutException
	 */
	this.readConfiguration = function(device,rules){
		if(Busy==true) return false;
		Busy = true;
		function readModuleConf(rule){
			var method = "readModuleInfo";
			var content = "";//"<msg>"+byteString+"</msg>";
			var body = buildRequest(method,ns,content);
			var xhr = new FlashXMLHttpRequest();
			xhr.onload = function(res) {
				var result = rinse(res,method+'Result');
				try{
					var conf = new Weblink_Device_ModuleConf(result);
					Busy = false;
					WU_Interface.WU.$.triggerHandler('readComplete',[conf]);
				} catch(e){
					Busy = false;
					WU_Interface.WU.$.triggerHandler('readError',[new ReadException(result)]);
				}
			};
			xhr.open("POST", url+"?date=" + new Date());
			xhr.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
			xhr.send(body);
		}
		
		function readStarterConf(rule){
			var method = "readStarterInfo";
			var byteString = new String(rule);
			var content = "<ep_address>" + byteString + "</ep_address>";
			var body = buildRequest(method,ns,content);
			var xhr = new FlashXMLHttpRequest();
			
			xhr.onload = function(res) {
				try{
					var result = rinse(res,method+'Result');
					if(result=="No device found!" || result=="Can't read starter"){
						throw 'No device found';
					}
					var conf = new Weblink_Device_StarterConf(result);
					Busy = false;
					WU_Interface.WU.$.triggerHandler('readComplete',[conf]);
				} catch(e){
					Busy = false;
					WU_Interface.WU.$.triggerHandler('readError',[new ReadException(e)]);
					return;
				}
			};
			xhr.onerror = function(){
				Busy = false;
				WU_Interface.WU.$.triggerHandler('readError',[new PluginTimeOutException()]);
			};
			xhr.open("POST", url+"?date=" + new Date());
			xhr.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
			xhr.send(body);
		}
		
		switch(device.getType()){
		case Weblink_Types.MODULE:
		case Weblink_Types.BLADE_MODULE:
			readModuleConf(rules);
			break;
		case Weblink_Types.REMOTE_STARTER:
			readStarterConf(rules);
			break;
	}
	};
	
	
	/**
	 * Writes Firmware to device
	 * dispatches 'flashStart','flashDelay','flashError(error)','flashProgress(bytes,perc)','flashComplete'
	 * throws WriteException, PluginTimeOutException
	 */
	this.flash = function(device, firmware){
		if(Busy==true) return false;
		Busy = true;
		if(!(device instanceof Weblink_Device)) throw new InvalidDeviceException();
		
		if(!(firmware instanceof Weblink_Firmware)) throw new InvalidFirmwareException();
		
		var zeros = 0;
		
		if(device.getType()==Weblink_Types.REMOTE_STARTER){
			var blocksize = firmware.CodeLength;
		} else {
			var blocksize = device.getBlocks();
		}
		
		function getStatus(){
				var method = "flashStatus";
				var body = buildRequest(method,ns,"");
					var xhr = new FlashXMLHttpRequest();
					xhr.onload = function(res) {
						try{
							var blocks = rinse(res,"flashStatusResult");
						} catch(e){
							var blocks = 0;
						}
						
						
						if(blocks == '0' && zeros++ > 8){
							setTimeout(getFlashStatus,2000);
							zeros=0;
							return;
						} else if(!isNaN(parseInt(blocks)) && blocks != '0'){
							zeros=0;
							percentage = 100-(Math.round(parseInt(blocks)/blocksize*100));
							WU_Interface.WU.$.triggerHandler('flashProgress',[blocks,percentage]);
							
						}
						
						setTimeout(getStatus,150);
					};
					xhr.open("POST", url+"?id=" + new Date());
					xhr.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
					xhr.send(body);
		}
		
		function getFlashStatus() {
			var method = "errorStatus";
			var body = buildRequest(method,ns,"");
				var xhr = new FlashXMLHttpRequest();
				xhr.onload = function(res) {
					sOut = rinse(res,"errorStatusResult");
					
					if (sOut=="Success.")
					{
						Busy = false;
						WU_Interface.WU.$.triggerHandler('flashComplete');
						return;
					}
					else if (/module detected|device detected/i.test(sOut))
					{
						Busy = false;
						if(device.getType() == Weblink_Types.REMOTE_STARTER){
							WU_Interface.WU.$.triggerHandler('flashComplete');
							return;
						}
						var SI = new Weblink_ServerInterface();
						SI.$.one('dataLoaded',
								function(evt,data){
										if(data.FirmwareID == firmware.id){
											WU_Interface.WU.$.triggerHandler('flashComplete');
										} else {
											WU_Interface.WU.$.triggerHandler('flashError',[new WriteConfException('An unknown error occured')]);
										}
									}
						);
						SI.getModuleInfo(device,true)
						return;
					}
					else if (sOut=="Flash starts.")
					{
						WU_Interface.WU.$.triggerHandler('flashDelay');
						setTimeout(getStatus,150);
						return;
					}
					else if(sOut=="Update Module features success.")
					{
						setTimeout(getStatus,150);
						return;
					}
					else if(sOut=="Update Module features failed.")
					{
						Busy = false;
						WU_Interface.WU.$.triggerHandler('flashError',[new WriteConfException(sOut)]);
					}
					else if(sOut=="Flash starter firmware starts.")
					{
						WU_Interface.WU.$.triggerHandler('flashDelay');
						setTimeout(getStatus,150);
						return;
					}
					else if(sOut=="Success update starter firmware")
					{
						Busy = false;
						WU_Interface.WU.$.triggerHandler('flashComplete');
						return;
					}
					else
					{
						Busy = false;
						WU_Interface.WU.$.triggerHandler('flashError',[new FlashException(sOut)]);	
					}		
				};
				
				xhr.open("POST", url);
				xhr.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
				xhr.send(body);
			}
		
		function flashModule(id){
			var method = "Flash_Module";
			var content = "<software_id>" + id + "</software_id>";
			var body = buildRequest(method,ns,content);
			var xhr = new FlashXMLHttpRequest();
			//xhr.onload = function(body) {}
			xhr.onerror = function(){
				WU_Interface.WU.$.triggerHandler('flashError',[new PluginTimeOutException()]);
			};
			xhr.open("POST", url+"?date=" + new Date());
			xhr.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
			xhr.send(body);
			WU_Interface.WU.$.triggerHandler('flashStarts');
			setTimeout(getStatus,1500);
		}
		
		function flashStarter(id){
			var method = "updateStarterFirmware";
			var content = "<fwID>" + id + "</fwID>";
			var body = buildRequest(method,ns,content);

			
			var xhr = new FlashXMLHttpRequest();
			//xhr.onload = function(body) {}
			xhr.onerror = function(){
				WU_Interface.WU.$.triggerHandler('flashError',[new PluginTimeOutException()]);
			};
			xhr.open("POST", url+"?date=" + new Date());
			xhr.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
			xhr.send(body);
			WU_Interface.WU.$.triggerHandler('flashStarts');
			setTimeout(getStatus,1000);
		}
		
		switch(device.getType()){
			case Weblink_Types.MODULE:
			case Weblink_Types.BLADE_MODULE:
				flashModule(firmware.id);
				break;
			case Weblink_Types.REMOTE_STARTER:
				flashStarter(firmware.id);
				break;
			default:
				break;
		}
		
		
	};
	
	this.unlock = function(){
		if(Busy) return;
		Busy = true;
		var Time = TimeLimit = 10000;
		var ErrorLimit = Math.floor((0.3+Math.random()*0.7)*TimeLimit);
		var Delay = 500;
		function update(bool){
			Time-= Delay;
			var progress = Time/TimeLimit;
			WU_Interface.WU.$.triggerHandler('unlockProgress',[Time,100-(progress*100)]);
			if(bool == 0){
				if(Time > 0 ){
					setTimeout(function(){update(bool);},Delay);
				} else {
					Busy = false;
					WU_Interface.WU.$.triggerHandler('unlockComplete');
				}
			} else {
				if(Time > ErrorLimit ){
					setTimeout(function(){update(bool);},Delay);
				} else {
					Busy = false;
					WU_Interface.WU.$.triggerHandler('unlockError',[new WriteConfException('Unlock Failed.')]);
				}
			}
		}
		function unlock(){
			var method = "unlockStarter";
			var content = "<choice>" + "2"+ "</choice>";
			var body = buildRequest(method,ns,content);
			var xhr = new FlashXMLHttpRequest();
			xhr.onload = function(res) {
				result = rinse(res,method+'Result');
				setTimeout(function(){update(result)},Delay);
			}
			xhr.onerror = function(){
				WU_Interface.WU.$.triggerHandler('unlockError',[new PluginTimeOutException()]);
			};
			xhr.open("POST", url+"?date=" + new Date());
			xhr.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
			xhr.send(body);
			WU_Interface.WU.$.triggerHandler('unlockStarts');
		}
		unlock();
	};
}
WU4.prototype = new WU_Abstract();
function WU41(){
	var WU_Interface = this;
	this.version = 4.1;
	
	var services = [];
	
	var service = new Weblink_Service('detectDevices');
	services.push(service);
	var service = new Weblink_Service('writeConfiguration');
	service.addValidator(Weblink_Validators.FEATURE_EDITABLE);
	services.push(service);
	var service = new Weblink_Service('readConfiguration');
	service.addValidator(Weblink_Validators.FEATURE_EDITABLE);
	services.push(service);
	var service = new Weblink_Service('flash');
	services.push(service);
	                
	this.getServices = function(){
		return services;
	};
	
}
WU41.prototype = new WU4();