package jGPIO.I2C;

import anywheresoftware.b4a.BA;
import jGPIO.SerialPort.SerialComm;
import jGPIO.SerialPort.jSerialPort_Event;

@BA.ShortName("USB_I2C")
@BA.Events(values= {
	"Log(msg as string)"
})

public class USB_I2C implements jSerialPort_Event {
	private BA ba;
	private Object myobject;
	private String event;
	private boolean need_log_event = false;
	private SerialComm myport;
	private boolean isopened = false;
	
	/**
	 * Initialize USB to I2C adapter
	 * Use jSerialPort component
	 * @param caller : caller object
	 * @param event : event name
	 */
	public void Initialize(BA bax, Object caller, String event) {
		this.ba = bax;
		this.myobject = caller;
		this.event = event;
		this.need_log_event = ba.subExists(event+"_Log");
		myport = new SerialComm();
		myport.SetJavaEvent(this);
	}
	
	/**
	 * Open it
	 * @param portname : a valid serialport name. use jSerialPort GetPortNames() result
	 * @return true if opened
	 */
	public boolean Open(String portname) {
		isopened = false;
		if (myport.OpenPort(portname)) {
			myport.SetBlockingMode(500, 500);
			isopened = true;
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * Check if port is opened
	 * @return true if opened
	 */
	public boolean PortIsOpened() {
		return isopened;
	}
	
	/**
	 * Close Port
	 */
	public void Close() {
		if (myport!=null) {
			myport.ClosePort();
		}
		isopened = false;
	}
	
	/**
	 * Write 1 byte in simple mode, to device without register address, example PCF8574
	 * @param address : slave address of the device
	 * @param databyte : byte to write , 0 - 255
	 * @return -1 = failed, 0 = write failed, 1 = write success
	 */
	public int Simple_Write(int address, int databyte) {
		if (isopened) {
			if (address>0) {
				byte write_address = (byte)((address & 0x7F)<<1);
				byte[] cmd = new byte[] {0x53, write_address, (byte) databyte};
				int writeresult = myport.Write(cmd);
				return writeresult;	
			}
		}
		return -1;
	}
	
	/**
	 * Read 1 byte in simple mode, to device without register address,  example PCF8574
	 * @param address : slave address of the device
	 * @return -1 = failed, 0 - 255 = read result;
	 */
	public int Simple_Read(int address) {
		if (isopened) {
			if (address>0) {
				byte read_address = (byte)(((address & 0x7F)<<1)|1);
				byte[] cmd = new byte[] {0x53, read_address};
				if (myport.Write(cmd)==2) {
					byte[] result = myport.Read(1);
					if (result!=null) {
						if (result.length>0) {
							return (result[0] & 0xFF);
						}
					}
				}
			}
			
		}
		return -1;
	}
	
	/**
	 * Write bytes data in simple mode, to device without register address
	 * @param address : slave address of the device
	 * @param databyte : array of bytes to write
	 * @return -1 = failed, 0 = write failed, positive = write success
	 */
	public int Block_Write(int address , byte[] databyte) {
		if (isopened) {
			if (address>0) {
				if (databyte!=null) {
					if (databyte.length>0) {
						byte write_address = (byte)((address & 0x7F)<<1);
						byte[] cmd = new byte[3+databyte.length];		// command 1B (0x54), slave (1B), writelength (1B), databytes
						cmd[0] = 0x54;
						cmd[1] = write_address;
						cmd[2] = (byte)databyte.length;
						for(int ii=0;ii<databyte.length;ii++) cmd[3+ii] = databyte[ii];
						
						int writeresult = myport.Write(cmd);
						return writeresult;
					}
				}
			}
		}
		return -1;
	}
	
	/**
	 * Read bytes data in simple mode, to device without register address
	 * @param address : slave address of the device
	 * @param readcount : how many bytes to read
	 * @return null if read failed
	 */
	public byte[] Block_Read(int address, int readcount) {
		if (isopened) {
			if (address>0) {
				if (readcount>0) {
					byte read_address = (byte)(((address & 0x7F)<<1)|1);
					byte[] cmd = new byte[] {0x54, read_address, (byte) readcount};
					if (myport.Write(cmd)==3) {
						byte[] result = myport.Read(readcount);
						if (result!=null) {
							if (result.length==readcount) {
								return result;
							}
						}
					}
				}				
			}
		}
		return null;
	}
	
	/**
	 * Write bytes data to device which have 8bit Register address, example RTC
	 * @param address : slave address of the device
	 * @param register : register address, where writing start
	 * @param bb : bytes of data
	 * @return -1 = failed, 0 = write failed, positive = write success
	 */
	public int Block_Write_with_Register_L(int address, int register, byte[] bb) {
		if (isopened) {
			if (address>0) {
				if (register>=0) {
					if (bb!=null) {
						if (bb.length>0) {
							byte write_address = (byte)((address & 0x7F)<<1);
							byte[] cmd = new byte[4+bb.length];	// command = 0x55, address 1B, register 1B, byte length 1B, data bytes
							cmd[0] = 0x55;
							cmd[1] = write_address;
							cmd[2] = (byte) register;
							cmd[3] = (byte)bb.length;
							for(int ii=0;ii<bb.length;ii++) cmd[4+ii] = bb[ii];
							int writeresult = myport.Write(cmd);
							return writeresult;
						}
					}
				}				
			}
		}
		return -1;
	}
	
	/**
	 * Read bytes data from device which have 8bit register address, example RTC
	 * @param address : slave address of the device
	 * @param register : register address where reading start
	 * @param readcount : how many bytes to read 
	 * @return null = failed
	 */
	public byte[] Block_Read_with_Register_L(int address, int register, int readcount) {
		if (isopened) {
			if (address>0) {
				if (register>=0) {
					if (readcount>0) {
						byte read_address = (byte)(((address & 0x7F)<<1)|1);
						byte[] cmd = new byte[] {0x55, read_address, (byte) register, (byte) readcount};
						if (myport.Write(cmd)==cmd.length) {
							byte[] result = myport.Read(readcount);
							if (result!=null) {
								if (result.length==readcount) {
									return result;
								}
							}
						}
					}
				}
			}
		}
		return null;
	}
	
	/**
	 * Write bytes data to device which have 16bit register address, example EEPROM
	 * @param address : slave address of the device
	 * @param register : register address (16bit) where writing start
	 * @param bb : bytes of data
	 * @return -1 = failed, 0 = write failed, positive = write success
	 */
	public int Block_Write_with_Register_HL(int address, int register, byte[] bb) {
		if (isopened) {
			if (address>0) {
				if (register>=0) {
					if (bb!=null) {
						if (bb.length>0) {
							byte write_address = (byte)((address & 0x7F)<<1);
							
							byte[] cmd = new byte[5+bb.length];	// command = 0x56, address 1B, register 2B, byte length 1B, data bytes
							cmd[0] = 0x56;
							cmd[1] = write_address;
							cmd[3] = (byte) (register);
							cmd[2] = (byte) (register >> 8);
							cmd[4] = (byte)bb.length;
							for(int ii=0;ii<bb.length;ii++) cmd[5+ii] = bb[ii];
							int writeresult = myport.Write(cmd);
							return writeresult;
						}
					}
				}
			}
		}
		return -1;
	}
	
	/**
	 * Read bytes data from device which have 16bit register address, example EEPROM
	 * @param address : slave address of the device
	 * @param register : register address (16bit) where reading start
	 * @param readcount : how many bytes to read
	 * @return null = failed
	 */
	public byte[] Block_Read_with_Register_HL(int address, int register, int readcount) {
		if (isopened) {
			if (address>0) {
				if (register>=0) {
					if (readcount>0) {
						byte read_address = (byte)(((address & 0x7F)<<1)|1);
						byte[] cmd = new byte[] {0x56, read_address, (byte) (register>>8), (byte)(register), (byte) readcount};
						if (myport.Write(cmd)==cmd.length) {
							byte[] result = myport.Read(readcount);
							if (result!=null) {
								if (result.length==readcount) {
									return result;
								}
							}
						}
					}
				}
			}
		}
		return null;
	}
	
	
	private void raise_log(String msg) {
		if (need_log_event) {
			ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_Log", false, new Object[] {msg});
		}
	}

	@Override
	@BA.Hide
	// event from SerialComm
	public void Log(String msg) {
		raise_log(msg);
	}

	@Override
	@BA.Hide
	// event from SerialComm
	public void newdata(byte[] bb) {
		// TODO belum tau mau ngapain pake event ini
		
	}
}
