package com.phidgets
{
	import flash.events.Event;
	import com.phidgets.events.PhidgetEvent;
	import com.phidgets.events.PhidgetErrorEvent;
	import flash.events.EventDispatcher;
	
	public class Phidget extends EventDispatcher
	{
		private var _attached:Boolean = false;
		
		private var _serialNumber:int = com.phidgets.Constants.PUNK_INT;
		private var _deviceVersion:int = 0;
		private var _deviceType:String = null;
		private var _deviceName:String = null;
		private var _deviceLabel:String = null;
		
		//private var _deviceTypeNumber:int = 0;
		private var _specificDevice:int = com.phidgets.Constants.PFALSE;
		
		protected var _phidgetSocket:PhidgetSocket = null;
		private var randInt:int = 0; //used for the open/close command
		protected var keyCount:int = 0; //used for keeping track of attach / detach events
		protected var keyCountNeeded:int = 4;
		
		private var calledClose:Boolean = false;
		
		internal function initForManager(serial:int, version:int, type:String, name:String, 
			attached:Boolean, socket:PhidgetSocket):void
		{
			_attached = attached;
			_serialNumber = serial;
			_deviceVersion = version;
			_deviceType = type;
			_deviceName = name;
			//_deviceLabel = label;
			_phidgetSocket = socket;
		}
		
		public function Phidget(type:String)
		{
			_deviceType = type;
		}
		
		public function open(address:String, port:Number, password:String = null, serialNumber:int = 0x7FFFFFFF):void {
			_serialNumber = serialNumber;
			_phidgetSocket = new PhidgetSocket();
			if(serialNumber == com.phidgets.Constants.PUNK_INT)
				_specificDevice = com.phidgets.Constants.PFALSE;
			else
				_specificDevice = com.phidgets.Constants.PTRUE;
			_phidgetSocket.connect(address, port, password, onConnected, onDisconnected, onError);
		}
		
		public function close():void {
			calledClose = true;
			var key:String = "/PCK/Client/0.0.0.0/"+randInt+"/"+_deviceType;
			if(_specificDevice == com.phidgets.Constants.PTRUE)
				key = key+"/"+_serialNumber.toString();
			_phidgetSocket.setKey(key, "Close", false);
			_phidgetSocket.close();
		}
		
		private function onConnected():void {
			
			//now send the open key
			var rand:Number = Math.random();
			randInt = int(rand * 99999);
			var key:String = "/PCK/Client/0.0.0.0/"+randInt+"/"+_deviceType;
			if(_specificDevice == com.phidgets.Constants.PTRUE)
				key = key+"/"+_serialNumber.toString();
			_phidgetSocket.setKey(key, "Open", false);

			//listen
			var pattern:String = "/PSK/"+_deviceType;
			if(_specificDevice == com.phidgets.Constants.PTRUE)
				pattern = pattern+"/"+_serialNumber.toString();
			_phidgetSocket.setListener(pattern, onPhidgetData);
			
			dispatchEvent(new PhidgetEvent(PhidgetEvent.CONNECT,this));
		}
		
		private function onDisconnected():void {
			if(!calledClose)
				dispatchEvent(new PhidgetEvent(PhidgetEvent.DISCONNECT,this));
			if(_attached)
				detachDevice();
			calledClose = false;
		}
		
		private function onError(error:PhidgetError):void {
			dispatchEvent(new PhidgetErrorEvent(PhidgetErrorEvent.ERROR,this,error));
		}
		
		protected function indexArray(array:Array, index:int, limit:int):Object{ 
			if(index >= limit || index < 0)
				throw new PhidgetError(com.phidgets.Constants.EPHIDGET_OUTOFBOUNDS);
			if(array[index] == null)
				throw new PhidgetError(com.phidgets.Constants.EPHIDGET_UNKNOWNVAL);
			return array[index];
		}
		
		protected function intToBool(val:int):Boolean{ 
			if(val == com.phidgets.Constants.PUNK_BOOL)
				throw new PhidgetError(com.phidgets.Constants.EPHIDGET_UNKNOWNVAL);
			if(val == com.phidgets.Constants.PFALSE) return false;
			if(val == com.phidgets.Constants.PTRUE) return true;
				throw new PhidgetError(com.phidgets.Constants.EPHIDGET_UNEXPECTED);
		}
		
		protected function boolToInt(val:Boolean):int{ 
			if(val == false) return com.phidgets.Constants.PFALSE;
			else return com.phidgets.Constants.PTRUE;
		}
		
		protected function makeKey(setThing:String):String{ 
			return "/PCK/"+_deviceType+"/"+_serialNumber+"/"+setThing;
		}
		protected function makeIndexedKey(setThing:String, index:int, limit:int):String{ 
			if(index >= limit || index < 0)
				throw new PhidgetError(com.phidgets.Constants.EPHIDGET_OUTOFBOUNDS);
			return "/PCK/"+_deviceType+"/"+_serialNumber+"/"+setThing+"/"+index;
		}
		
		//override in the subclasses
		protected function onSpecificPhidgetData(setThing:String, index:int, value:String):void{}
		protected function onSpecificPhidgetDetach():void{}
		
		private function onPhidgetData(key:String, val:String, reason:int):void
		{
			//trace("Key: "+key+" Val: "+val+" Reason: "+reason);
				
			if(reason != com.phidgets.Constants.ENTRY_REMOVING || val == "Detached")
			{
				var dataArray:Array = key.split("/");
				
				var serialNumber:int = int(dataArray[3]);
				var setThing:String = dataArray[4];
				var index:int = 0;
				if(dataArray.length>5)
					index = int(dataArray[5]);
					
				if(_specificDevice == com.phidgets.Constants.PFALSE && val != "Detached")
				{
					_specificDevice = 2;
					_serialNumber = serialNumber;
				}
				
				//trace("Serial: "+serialNumber+" SetThing: "+setThing+" Index: "+index+" Value: "+val);
				
				if(serialNumber == _serialNumber)
				{
					switch(setThing)
					{
						case "Label":
							_deviceLabel = val;
							keyCount++;
							break;
						case "Version":
							_deviceVersion = int(val);
							keyCount++;
							break;
						case "Name":
							_deviceName = val;
							keyCount++;
							break;
						case "Status":
							if(val == "Attached")
							{
								keyCount++;
							}
							else if(val == "Detached")
							{
								keyCount = 0;
								detachDevice();
							}
							else
								throw new PhidgetError(com.phidgets.Constants.EPHIDGET_NETWORK);
							break;
						default:
							onSpecificPhidgetData(setThing, index, val);
							break;
					}
					if(keyCount >= keyCountNeeded && _attached == false)
					{
						_attached = true;
						dispatchEvent(new PhidgetEvent(PhidgetEvent.ATTACH,this));
					}
				}
			}
			
		}
		
		private function detachDevice():void {
			_attached = false;
			if(!calledClose)
				dispatchEvent(new PhidgetEvent(PhidgetEvent.DETACH,this));
			if(_specificDevice == 2)
			{
				_specificDevice = com.phidgets.Constants.PFALSE;
				_serialNumber = com.phidgets.Constants.PUNK_INT;
			}
			_deviceLabel = null;
			_deviceVersion = com.phidgets.Constants.PUNK_INT;
			_deviceName = null;
			onSpecificPhidgetDetach();
		}
		
		//Getters
		public function get Type():String{
			if(_deviceType == null)
				throw new PhidgetError(com.phidgets.Constants.EPHIDGET_UNKNOWNVAL);
			return _deviceType;
		}
		public function get Name():String{
			if(_deviceName == null)
				throw new PhidgetError(com.phidgets.Constants.EPHIDGET_UNKNOWNVAL);
			return _deviceName;
		}
		public function get Label():String{
			if(_deviceLabel == null)
				throw new PhidgetError(com.phidgets.Constants.EPHIDGET_UNKNOWNVAL);
			return _deviceLabel;
		}
		public function get Version():int{
			if(_deviceVersion == com.phidgets.Constants.PUNK_INT)
				throw new PhidgetError(com.phidgets.Constants.EPHIDGET_UNKNOWNVAL);
			return _deviceVersion;
		}
		public function get serialNumber():int{
			if(_serialNumber == com.phidgets.Constants.PUNK_INT)
				throw new PhidgetError(com.phidgets.Constants.EPHIDGET_UNKNOWNVAL);
			return _serialNumber;
		}
		public function get isAttached ():Boolean{
			return _attached;
		}
		
		//From the socket
		public function get isConnected ():Boolean{
			return _phidgetSocket.isConnected;
		}
		public function get Address():String{
			if(_phidgetSocket.Address == null)
				throw new PhidgetError(com.phidgets.Constants.EPHIDGET_UNKNOWNVAL);
			return _phidgetSocket.Address;
		}
		public function get Port():int{
			if(_phidgetSocket.Port == com.phidgets.Constants.PUNK_INT)
				throw new PhidgetError(com.phidgets.Constants.EPHIDGET_UNKNOWNVAL);
			return _phidgetSocket.Port;
		}
		public function get ServerID():String{
			if(_phidgetSocket.ServerID == null)
				throw new PhidgetError(com.phidgets.Constants.EPHIDGET_UNKNOWNVAL);
			return _phidgetSocket.ServerID;
		}

		//Setters
		public function set Label(str:String):void{
			var key:String = "/PCK/"+_deviceType+"/"+_serialNumber+"/Label";
			_phidgetSocket.setKey(key, str, false);
		}
		
		override public function toString():String{
			return _deviceName+", Version: "+_deviceVersion+", Serial Number: "+_serialNumber;
		}
	}
}