package ui.parts
{
	import flash.desktop.NativeApplication;
	import flash.desktop.NativeProcess;
	import flash.desktop.NativeProcessStartupInfo;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.NativeProcessExitEvent;
	import flash.events.ProgressEvent;
	import flash.external.ExternalInterface;
	import flash.filesystem.File;
	import flash.net.Socket;
	import flash.utils.ByteArray;
	import flash.utils.setTimeout;
	
	import cc.makeblock.util.HexUtil;
	
	import extensions.ArduinoManager;
	import extensions.ArduinoSocket;
	import extensions.SerialManager;
	import extensions.SerproxyHelper;
	
	import util.ApplicationManager;
	
	public class SerialAgent extends Sprite
	{
		private static var _instance:SerialAgent;
		private var process:NativeProcess;

		public static function sharedManager():SerialAgent{
			if(_instance==null){
				_instance = new SerialAgent;
			}
			return _instance;
		}
		
		public function SerialAgent()
		{
           //load agent
		}
		
		private var output:String;
		private var errorText:String;
		private function onOutputData(event:ProgressEvent):void
		{
			output = process.standardOutput.readUTFBytes(process.standardOutput.bytesAvailable);
			var date:Date = new Date;
			IQCar.app.scriptsPart.appendMessage(""+(date.month+1)+"-"+date.date+" "+date.hours+":"+date.minutes+": SerialAgent Got: "+output);
			
			if (output.indexOf("init success") > 0)
			{
				connect();
			}
		}

		private function onErrorData(event:ProgressEvent):void
		{
			var errOut:String = process.standardError.readUTFBytes(process.standardError.bytesAvailable);

			if(null == errorText){
				errorText = errOut;
			}else{
				errorText += errOut;
			}
		}

		private function onExit(event:NativeProcessExitEvent):void
		{
			var date:Date = new Date;
			IQCar.app.scriptsPart.appendMessage(""+(date.month+1)+"-"+date.date+" "+date.hours+":"+date.minutes+": Process exited with "+event.exitCode);
			if(event.exitCode > 0)
			{
			  //fail
				IQCar.app.log(errorText);
				sa_status_notification_function(false);
			}
			else
			{
			  //success
				
			}

			process = null;
		}

		private var recv_data_function:Function = null;
		private var sa_status_notification_function:Function = null;
		public function open(serial:String, recv_data_func:Function, sa_status_notification_func:Function):void
		{
			if (serial == null || recv_data_func == null || sa_status_notification_func == null)
			{
			  return;
			}

			recv_data_function = recv_data_func;
			sa_status_notification_function = sa_status_notification_func;
			load_serial_agent(serial);
		}
		public var fNote:Boolean = true;
		public function close():void
		{
			if (isConnected == false)  return;
	
			fNote = false;
			clearHandler();
			socket.close();
			socket = null;
		}

		public function onExiting(evt:Event):void
		{
			//if(process.running){
				//if(evt){
				//	evt.preventDefault();
				//	evt.stopPropagation();
				//}
				//trace("Quitting Serproxy");
				//process.exit();
				//shuttingDown = true;
				
			//}else{
			//	trace("Serproxy already closed");
			//}
		}
		
		private function onIOError(event:IOErrorEvent):void
		{
			trace("IOError: ",event.toString());
		}
		
		private function load_serial_agent(serial:String):void
		{
			var nativeProcessStartupInfo:NativeProcessStartupInfo =new NativeProcessStartupInfo();
			var file:File = new File(ArduinoManager.sharedManager().arduinoInstallPath+"/hardware/tools/serial/serialagent"+(ApplicationManager.sharedManager().system==ApplicationManager.WINDOWS?".exe":""));
			var processArgs:Vector.<String> = new Vector.<String>();
			var workdir:File = new File( ArduinoManager.sharedManager().arduinoInstallPath);

			nativeProcessStartupInfo.executable = file;
			nativeProcessStartupInfo.workingDirectory = workdir;
			processArgs.push(serial);
	
			nativeProcessStartupInfo.arguments = processArgs;
			IQCar.app.scriptsPart.appendMessage(nativeProcessStartupInfo.executable.nativePath+" " + nativeProcessStartupInfo.arguments.join(" "));
			process = new NativeProcess();
			process.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, onOutputData);
			process.addEventListener(ProgressEvent.STANDARD_ERROR_DATA, onErrorData);
			process.addEventListener(NativeProcessExitEvent.EXIT, onExit);

			process.addEventListener(IOErrorEvent.STANDARD_OUTPUT_IO_ERROR, onIOError);
			process.addEventListener(IOErrorEvent.STANDARD_ERROR_IO_ERROR, onIOError);
			process.start(nativeProcessStartupInfo);
			NativeApplication.nativeApplication.addEventListener(Event.EXITING,onExiting);
			setTimeout(connect, 2000);
			setTimeout(connect_timeout, 5000);
		}

		private var isConnected:Boolean = false;
		private function connect_timeout():void
		{
		  if (isConnected == false)
		  {
		    IQCar.app.log("socket connect_timeout");
			sa_status_notification_function(false);
		  }
		}
		private function connect():void
		{
			//trace(ProgressEvent.SOCKET_DATA);
			socket=new Socket();
			socket.connect("127.0.0.1", 8888);
			socket.removeEventListener(ProgressEvent.SOCKET_DATA, connectHandler);
			socket.removeEventListener(Event.CONNECT, connectHandler);
			socket.removeEventListener(Event.CLOSE, closeHandler);
			socket.removeEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
			socket.addEventListener(Event.CONNECT, connectHandler);
			socket.addEventListener(Event.CLOSE, closeHandler);
			socket.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
		}

		private var socket:Socket;
		private var msg:String = "";

		public function sendBytes(send_data:Array):void
		{
			//socket.writeUTFBytes(send_data);
			if (isConnected == false)  return;

			socket.writeBytes(HexUtil.arrayToBytes(send_data));
			socket.flush();
		}

		private function onServerData(event:ProgressEvent):void{
			var data:ByteArray = new ByteArray();
			//trace('onServerData');
			if(socket.bytesAvailable){
				 socket.readBytes(data);
				
				//ExternalInterface.call("window.jsFunc", msg);
			    recv_data_function(HexUtil.bytesToArray(data));
			}
		}
		private function connectHandler(event:Event):void{
			isConnected = true;
			trace("connected");
			sa_status_notification_function(true);
			socket.addEventListener(ProgressEvent.SOCKET_DATA, onServerData,false,0,true);
		}
		private function closeHandler(event:Event):void{
			trace("closed");
			clearHandler();
		}
		private function ioErrorHandler(event:IOErrorEvent):void{
			//to do
			clearHandler();
		}
		private function clearHandler():void{
			isConnected = false;
			if (fNote)
			{
    			sa_status_notification_function(false);
			}
			socket.removeEventListener(ProgressEvent.SOCKET_DATA, connectHandler);
			socket.removeEventListener(Event.CONNECT, connectHandler);
			socket.removeEventListener(Event.CLOSE, closeHandler);
			socket.removeEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
		}
	}
}