package jGPIO.I2C;

import jGPIO.jGPIO;
import anywheresoftware.b4a.*;

@BA.ShortName("i2cdevice")
@BA.Events(values= {
		"log(msg as string)"
})

public class I2C_Device {
	
	private boolean opened = false;
	
	private int bus_handle = -1;
	private int dev_handle = -1;
	private int dev_address = -1;
	private I2C_Device_Event javaevent;
	
	private BA ba;
	private String event;
	private boolean need_log_event = false;
	
	public void Initialize(BA ba, String event, int bushandle, int address) {
		this.ba = ba;
		this.event = event;
		this.bus_handle = bushandle;
		this.dev_address = address;
		
		if (this.ba!=null) {
			if (this.event!=null) {
				if (!this.event.isEmpty()) {
					need_log_event = this.ba.subExists(event+"_log");
				}
			}
		}
	}
	
	// buat sediain B4X
	public I2C_Device() {}
	
	/**
	 * Create a I2C Device (Slave)
	 * @param bushandle : bus handle , get it from I2C_BUS object
	 * @param address : slave address to control
	 */
	@BA.Hide
	public I2C_Device(int bushandle, int address) {
		opened = false;
		if (bushandle < 0) return;
		if (address<0) return;
		this.bus_handle = bushandle;
		this.dev_address = address;		
	}
	
	@BA.Hide
	public void SetJavaEvent(I2C_Device_Event ev) {
		javaevent = ev;
	}
	
	/**
	 * Open this Device, in I2C_SLAVE mode
	 * @return true if opened
	 */
	public boolean OpenDevice_SLAVE() {
		opened = false;
		if (bus_handle<0) return false;
		if (dev_address<0) return false;
		dev_handle = jGPIO.libC.ioctl(bus_handle, jGPIO.I2C_SLAVE, dev_address);
		if (dev_handle<0) {
			raise_log("OpenDevice_SLAVE failed");
			opened = false;
		}
		else {
			raise_log("OpenDevice_SLAVE success");
			opened = true;			
		}
		return opened;
	}
	
	/**
	 * Open this Device, in I2C_SLAVE_FORCE mode.
	 * @return true if opened
	 */
	public boolean OpenDevice_SLAVEFORCE() {
		opened = false;
		if (bus_handle<0) return false;
		if (dev_address<0) return false;
		dev_handle = jGPIO.libC.ioctl(bus_handle, jGPIO.I2C_SLAVE_FORCE, dev_address);
		
		if (dev_handle<0) {
			raise_log("OpenDevice_SLAVEFORCE failed");
			opened = false;			
		}
		else {
			jGPIO.libC.ioctl(bus_handle, jGPIO.I2C_RETRIES, 3);
			jGPIO.libC.ioctl(bus_handle, jGPIO.I2C_TIMEOUT, 3);
			raise_log("OpenDevice_SLAVEFORCE success");
			opened = true;			
		}
		return opened;
	}
	
	/**
	 * Open this Device, in I2C_RDWR mode
	 * @return true if opened
	 */
	public boolean OpenDevice_RDWR() {
		opened = false;
		if (bus_handle<0) return false;
		if (dev_address<0) return false;
		dev_handle = jGPIO.libC.ioctl(bus_handle, jGPIO.I2C_RDWR, dev_address);
		if (dev_handle<0) {
			raise_log("OpenDevice_RDWR failed");
			opened = false;			
		}
		else {
			raise_log("OpenDevice_RDWR success");
			opened = true;			
		}
		return opened;
	}
	
	/**
	 * Open this Device, in I2C_TENBIT
	 * @return true if opened
	 */
	public boolean OpenDevice_SMBUS() {
		opened = false;
		if (bus_handle<0) return false;
		if (dev_address<0) return false;
		dev_handle = jGPIO.libC.ioctl(bus_handle, jGPIO.I2C_SMBUS, dev_address);
		if (dev_handle<0) {
			raise_log("OpenDevice_SMBUS failed");
			opened = false;			
		}
		else {
			raise_log("OpenDevice_SMBUS success");
			opened = true;			
		}
		return opened;
	}
	
	
	/**
	 * Close this Device
	 */
	public void CloseDevice() {
		opened = false;
		bus_handle = -1;
		dev_handle = -1;
		dev_address = -1;
		raise_log("Device Closed");
	}
	
	/**
	 * Check if slave is opened
	 * @return true if opened
	 */
	public boolean IsOpened() {
		return opened;
	}
	
	/**
	 * Get associated Bus handle
	 * @return -1 if closed
	 */
	public int BusHandle() {
		return this.bus_handle;
	}
	
	/**
	 * Get associated Slave address
	 * @return -1 if closed
	 */
	public int SlaveAddress() {
		return this.dev_address;
	}
	
	/**
	 * Get associated Device Open Handle
	 * @return -1 if closed
	 */
	public int DeviceHandle() {
		return this.dev_handle;
	}
	
	/**
	 * Write bytes
	 * @param bb : data bytes
	 * @return 0 if write fail
	 */
	public int WriteBytes(byte[] bb) {
		if (bus_handle<0) {
			raise_log("WriteBytes failed, Bus Handle closed");
			return 0;
		}
		if (dev_handle<0) {
			raise_log("WriteBytes failed, Device Handle closed");
			return 0;
		}
		if (dev_address<0) {
			raise_log("WriteBytes failed, Device Address invalid");
			return 0;
		}
		
		return jGPIO.libC.write(bus_handle, bb, bb.length);
	}
	
	/**
	 * Create two bytes  of data [register] [data] and send to i2c
	 * @param register_address register of data
	 * @param data data to send
	 * @return 0 if failed
	 */
	public int WriteRegisterAndData(int register_address, byte data) {
		return WriteBytes(register_address, data);
	}
	
	@BA.Hide
	public int WriteBytes(int register_address, byte bb) {
		byte[] cmd = new byte[2];
		cmd[0] = (byte) register_address;
		cmd[1] = bb;
		return WriteBytes(cmd);
	}
	
	/**
	 * Create N+1 bytes of data [register] [data in array]
	 * @param register_address register of data
	 * @param data array of data
	 * @return 0 if failed
	 */
	public int WriteRegisterAndMoreData(int register_address, byte[] data) {
		return WriteBytes(register_address, data);
	}
	
	@BA.Hide
	public int WriteBytes(int register_address, byte[] bb ) {
		if (bb!=null) {
			if (bb.length>0) {
				byte[] cmd = new byte[bb.length+1];
				cmd[0] = (byte) register_address;
				for(int ii=0;ii<bb.length;ii++) cmd[ii+1] = bb[ii];
				return WriteBytes(cmd);
			}
		}
		return 0;
	}
	
	@BA.Hide
	public int WriteBytes(int register_address, byte[] bb, int offset, int size) {
		if (bb!=null) {
			if (offset>=0) {
				if (bb.length > (offset+size)) {
					byte[] newbyte = new byte[size+1];
					newbyte[0] = (byte) register_address;
					for(int ii=0;ii<size;ii++) newbyte[1+ii] = bb[offset+ii];
					return WriteBytes(newbyte);
				}
			}
		}
		
		return 0;
	}
	
	@BA.Hide
	public int WriteBytes(byte[] bb, int offset, int size) {
		if (bb!=null) {
			if (offset>=0) {
				if (bb.length > (offset + size)) {
					
					byte[] newbyte = new byte[size];
					for(int ii=0;ii<size;ii++) newbyte[ii] = bb[offset+ii];
					return WriteBytes(newbyte);
				}
			}
				
		}
		return 0;
	}
	
	
	/**
	 * Read bytes
	 * @param maxreadsize : maximum bytes to read
	 * @return null if read failed
	 */
	public byte[] ReadBytes(int maxreadsize) {
		if (bus_handle<0) {
			raise_log("ReadBytes failed, Bus Handle closed");
			return null;
		}
		if (dev_handle<0) {
			raise_log("ReadBytes failed, Device Handle closed");
			return null;
		}
		if (dev_address<0) {
			raise_log("ReadBytes failed, Device Address invalid");
			return null;
		}
		
		byte[] buf = new byte[maxreadsize];
		int count = jGPIO.libC.read(bus_handle, buf, maxreadsize);
		if (count<1) {
			raise_log("ReadBytes failed, I2C read-count return zero");
			return null;
		}
		
		return buf;
	}
	
	private void raise_log(String msg) {
		if (javaevent!=null) {
			javaevent.Log(msg);
		}
		if (need_log_event) {
			ba.raiseEventFromDifferentThread(I2C_Device.this, null, 0, event+"_log", false, new Object[] {msg});
		}
	}
	
}
