package devices.FA.Simplex4100;

import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortEvent;
import com.fazecast.jSerialComm.SerialPortInvalidPortException;
import com.fazecast.jSerialComm.SerialPortMessageListener;
import com.intelligt.modbus.jlibmodbus.serial.SerialPort.Parity;

import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.objects.collections.Map;

@BA.ShortName("Simplex4100")
//@BA.DependsOn(values = { "jSerialComm-2.6.2" })
@BA.Events(values= {
		"log(msg as string)",
		"rxbytes(portname as string, bb() as byte)",
		"newdata(value as Simplex4100Data)",
		"statuschanged(oldvalue as Simplex4100Data, newvalue as Simplex4100Data)"
})
public class Simplex4100 {

	private BA bax;
	private String event = "";
	private Object caller;
	private final Object Me = this;
	private boolean need_log_event = false;
	private boolean need_rxbytes_event = false;
	private boolean need_newdata_event = false;
	private boolean need_statuschanged_event = false;
	
	private boolean inited = false;
	private boolean isopened = false;
	private String portname;
	private SerialPort myport;
	private Map EventMap = new Map();
	
	/**
	 * Initialize Simplex 4100 Protocol Listener
	 * @param callerobject : caller object
	 * @param eventname : eventname
	 */
	public void Initialize(BA ba, Object callerobject, String eventname) {
		bax = ba;
		caller = callerobject;
		event = eventname.trim();
		EventMap.Initialize();
		
		if (bax instanceof BA) {
			if (caller!=null) {
				if (!event.isEmpty()) {
					need_log_event = bax.subExists(event+"_log");
					need_rxbytes_event = bax.subExists(event+"_rxbytes");
					need_newdata_event = bax.subExists(event+"_newdata");
					need_statuschanged_event = bax.subExists(event+"_statuschanged");
				}
			}
		}
		
		if (SerialPort.getVersion()!="") inited = true; else inited = false;
		
		 Runtime.getRuntime().addShutdownHook(new Thread() {
	            @Override
	            public void run() {
	                ClosePort();
	            }
	        });
	}
	
	/**
	 * Check if Simplex 4100 already initalized
	 * @return true if initialized
	 */
	public boolean IsInitialized() {
		return inited;
	}
	
	/**
	 * Check if SerialPort is opened
	 * @return true if opened
	 */
	public boolean PortIsOpened() {
		return isopened;
	}
	
	private class SimplexEvent implements SerialPortMessageListener{

		@Override
		public int getListeningEvents() {
			// tertarik sama event ini doang
			return  SerialPort.LISTENING_EVENT_DATA_RECEIVED;
		}

		@Override
		public void serialEvent(SerialPortEvent event) {
			if (event instanceof SerialPortEvent) {
				if (event.getEventType()== SerialPort.LISTENING_EVENT_DATA_RECEIVED) {
					byte[] msg = event.getReceivedData();
					if (msg!=null) {
						if (msg.length>0) {
							raise_rxbytes(msg);		// raise event for raw bytes
							Simplex4100Data vv = new Simplex4100Data(new String(msg));
							raise_newdata(vv);			// taruh di sini dulu , supaya keluar event 

							if (vv.getIsValid()) {
								if (EventMap.IsInitialized()==false) EventMap.Initialize();
								Object old = EventMap.Put(vv.getDetectorLocation(), vv);
								if (old instanceof Simplex4100Data) {
									raise_statuschanged((Simplex4100Data)old,vv);
								}
							}
						}
					}
				}
			}
			
		}

		@Override
		public byte[] getMessageDelimiter() {
			// CR - LF
			return new byte[] {(byte) 13, (byte) 10};		
		}

		@Override
		public boolean delimiterIndicatesEndOfMessage() {
			// delimiter adalah akhir dari message
			return true;
		}

	};
	
	/**
	 * Open SerialPort for communication with Simplex 4100
	 * @param portname : portname
	 * @return true if success
	 */
	public boolean OpenPort(String portname) {
		if (!portname.isEmpty()) {
			try {
				myport = SerialPort.getCommPort(portname);
			} catch(SerialPortInvalidPortException e) {
				myport = null;
				raise_log("Error getCommPort for "+portname+", Msg : "+e.getMessage());
				return false;
			}
			
			if (myport instanceof SerialPort) {
				if (myport.openPort()) {
					this.portname = portname;
					if (myport.setBaudRate(9600)) {
						if (myport.setNumDataBits(8)) {
							if (myport.setNumStopBits(1)) {
								if (myport.setParity(Parity.NONE.getValue())) {
									myport.addDataListener(new SimplexEvent());
									
									isopened = true;
									return true;			// success
								} else raise_log("Unable to set Parity None");
							} else raise_log("Unable to set Stopbit 1");
						} else raise_log("Unable to set Databits 8");
					} else raise_log("Unable to set Baudrate 9600");
				} else raise_log("Unable to OpenPort");
			}
		}
		
		return false;
	}
	
	/**
	 * Close Serial Port connected to Simplex 4100
	 */
	public void ClosePort() {
		isopened = false;
		if (myport instanceof SerialPort) {
			myport.removeDataListener();
			myport.closePort();
			myport = null;
			raise_log("Serial Port Closed");
		}
	}
	
	private void raise_log(String msg) {
		if (need_log_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg});
	}
	
	private void raise_rxbytes(byte[] bb) {
		if (need_rxbytes_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_rxbytes", false, new Object[] {portname, bb});
	}
	
	private void raise_newdata(Simplex4100Data value) {
		if (need_newdata_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_newdata", false, new Object[] {value});
	}
	
	private void raise_statuschanged(Simplex4100Data oldvalue, Simplex4100Data newvalue) {
		if (need_statuschanged_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_statuschanged", false, new Object[] {oldvalue, newvalue});
	}
}
