package waterlevelsensor;

import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortDataListener;
import com.fazecast.jSerialComm.SerialPortEvent;
import com.fazecast.jSerialComm.SerialPortInvalidPortException;

import anywheresoftware.b4a.BA;

@BA.Events(values= {
		"log(msg as string)",
		"newmeasurement(value as UE3003Data)"
})
@BA.ShortName("Holykell_UE3003")
/**
 * Holykell UE3003 ultrasound Water Level sensor, RS-485 communication
 * @author rdkartono
 *
 */
public class Holykell_UE3003 {

	private BA ba;
	private Object caller;
	private String event;
	private final Object Me = this;
	private boolean inited = false;
	private boolean need_log_event = false;
	private boolean need_newmeasurement_event = false;
	private SerialPort sp;
	
	private byte[] databuffer = new byte[40];		// data always 40 bytes
	
	private final byte[] read_waterlevel_command = {(byte)0xF0, 6, 1, 1, 1, 0x79};
	
	private void PutData(byte[] data) {
		if (data != null) {
			if (data.length>0) {
				for(int ii=0;ii<data.length;ii++) {
					
					// geserin semua ke kiri (ke depan) 
					for(int jj=0;jj<39;jj++) {
						databuffer[jj] = databuffer[jj+1];
					}
					
					databuffer[39] = data[ii];		// databuffer paling akhir, diisi data masukan
				}
				
				// habis putdata, cek apakah data sudah siap dibaca
				if (valid_databuffer_header()) {
					raise_newmeasurement(new UE3003Data(databuffer));
					// abis itu di clear
					clear_databuffer();	
				}
			}
		}
	}
	
	// header nya = F1 28 01 01 28 1C
	// lokasi bacaan ada di byte 11 - 15, berupa string dalam ascii 5 byte
	private boolean valid_databuffer_header() {
		if (databuffer != null) {
			if (databuffer.length==40) {
				if (databuffer[0]==(byte)0xF1) {
					if (databuffer[1]==0x28) {
						if (databuffer[2]==1) {
							if (databuffer[3]==1) {
								if (databuffer[4]==0x28) {
									if (databuffer[5]==0x1C) {
										return true;
									}
								}
							}
						}
					}
				}
			}
		}
		return false;
	}
	
	private void clear_databuffer() {
		if (databuffer != null) {
			if (databuffer.length>0) {
				for (int ii=0;ii<databuffer.length;ii++) {
					databuffer[ii] = 0;
				}				
			}
		}
	}
	
	public void Initialize(BA bax, Object callerobject, String eventname) {
		ba = bax;
		caller = callerobject;
		event = eventname;
		if (ba instanceof BA) {
			if (caller != null) {
				if (event instanceof String) {
					if (!event.isEmpty()) {
						need_log_event = ba.subExists(event+"_log");
						need_newmeasurement_event = ba.subExists(event+"_newmeasurement");
					}
				}
			}
		}
		
		Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                Close();
            }
        });
		
		inited = true;
	}
	
	/**
	 * Check if Holykell_UE3003 class already initialized
	 * @return true if inited
	 */
	public boolean IsInitialized() {
		return inited;
	}
	
	public boolean OpenSerial(String serialname) {
		try {
			sp = SerialPort.getCommPort(serialname);
			
			if (sp.openPort()) {
				if (sp.addDataListener(serialevent)) {
					return true;					
				} else raise_log("Unable to AddDataListener to Serial "+serialname);
			} else raise_log("Unable to OpenPort to Serial "+serialname);
		} catch(SerialPortInvalidPortException e) {
			raise_log("Unable to Open Serial "+serialname+", Msg : "+e.getMessage());
		}
		return false;
	}
	
	
	
	/**
	 * Close Serial Port
	 */
	public void Close() {
		if (sp instanceof SerialPort) {
			sp.removeDataListener();
			sp.closePort();
		}
		sp = null;
	}
	
	
	/**
	 * Check if SerialPort is opened
	 * @return true if opened
	 */
	public boolean SerialOpened() {
		if (sp instanceof SerialPort) {
			return sp.isOpen();
		}
		return false;
	}
	
	/**
	 * Read Water Level
	 * check result on event newmeasurement(value as UE3003Data)
	 * @return true if command sent
	 */
	public boolean Read_WaterLevel() {
		if (SerialOpened()) {
			if (sp.writeBytes(read_waterlevel_command, read_waterlevel_command.length) == read_waterlevel_command.length) {
				return true;
			} else return false;
		}
		return false;		
	}
	
	private void raise_log(String msg) {
		if (need_log_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg});
	}
	
	private void raise_newmeasurement(UE3003Data value) {
		if (need_newmeasurement_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_newmeasurement", false, new Object[] {value});
	}
	
	private SerialPortDataListener  serialevent = new SerialPortDataListener () {

		@Override
		public int getListeningEvents() {
			return SerialPort.LISTENING_EVENT_DATA_RECEIVED;
		}

		@Override
		public void serialEvent(SerialPortEvent event) {
			if (event.getEventType()==SerialPort.LISTENING_EVENT_DATA_RECEIVED) {
				PutData(event.getReceivedData());
			}
			
		}
		
	};
}
