package devices;

import anywheresoftware.b4a.BA;
import jGPIO.jGPIO;
import jGPIO.I2C.I2C_BUS;
import jGPIO.I2C.I2C_BUS_Event;
import jGPIO.I2C.I2C_Device;
import jGPIO.I2C.I2C_Device_Event;


// belum bisa
@BA.ShortName("AT24C32")
@BA.Events(values= {
		"log(msg as string)"
})
public class AT24C32 implements I2C_BUS_Event, I2C_Device_Event {
	private BA bax;
	private Object myobject;
	private String event;
	private boolean need_log_event = false;
	
	private int dev_address = 0x57;
	private I2C_BUS i2cbus;
	private I2C_Device i2cdev;
	
	private boolean iscorrect = false;
	
	public AT24C32() {
		if (jGPIO.osname.isEmpty()) jGPIO.detectOS();
	}
	
	/**
	 * Initialize a AT24C32. Used in B4J only
	 * @param caller : caller object
	 * @param eventname : event name
	 */
	public void Initialize(BA ba, Object caller, String eventname) {
		bax = ba;
		myobject = caller;
		event = eventname.trim();
		if (bax!=null) {
			if (myobject!=null) {
				if (!event.isEmpty()) {
					need_log_event = bax.subExists(event+"_log");			
				}
			}
		}
		

		 Runtime.getRuntime().addShutdownHook(new Thread() {
	            @Override
	            public void run() {
	                Close();
	            }
	        });
		
	}
	
	public void Close() {
		if (i2cdev!=null) {
			i2cdev.CloseDevice();
		}
		i2cdev = null;
		
		if (i2cbus!=null) {
			i2cbus.Close_Bus();
		}
		i2cbus = null;
	}
	
	/**
	 * Open a Slave
	 * @param index : I2C bus Index
	 * @param slave_address : slave address of this PCF
	 * @return true if succcess
	 */
	public boolean Open(String bus_name, int slave_address) {
		iscorrect = false;
		if (!jGPIO.IsLinux) return iscorrect;
		
		i2cbus = new I2C_BUS(bus_name);
		i2cbus.SetJavaEvent(this);
		if (i2cbus.Open_Bus()) {
			i2cdev = new I2C_Device(i2cbus.I2C_Handle(), slave_address);
			i2cdev.SetJavaEvent(this);
			if (i2cdev.OpenDevice_SLAVE()) {
				iscorrect = true;
				this.dev_address = slave_address;
			}
		}
		
		if (iscorrect) {
			raise_log("AT24C32 is Opened");
		} else {
			raise_log("AT24C32 failed to Open");
		}
		return iscorrect;
	}
	
	/**
	 * Check associated Slave Address
	 * @return -1 if no address
	 */
	public int getSlaveAddress() {
		return this.dev_address;
	}
	
	/**
	 * Check if I2C to this AT24C32 is opened
	 * @return true if opened
	 */
	public boolean getIsOpened() {
		return iscorrect;
	}
	
	/**
	 * Write bytes to AT24C32
	 * AT24C32 only have 4096 bytes, addressed from 0 to 4095.
	 * @param start_address : 0 ~ 4095
	 * @param bb : bytes to write
	 * @return true if success
	 * @throws InterruptedException 
	 */
	public boolean Write(int start_address, byte[] bb) {
		if (start_address<0) {
			raise_log("AT24C32 Write Fail, start_address invalid");
			return false;
		}
		if (start_address>4095) {
			raise_log("AT24C32 Write Fail, start_address invalid");
			return false;
		}
		
		if (bb==null) {
			raise_log("AT24C32 Write Fail, bytes to write is null");
			return false;
		}
		
		if (bb.length<1) {
			raise_log("AT24C32 Write Fail, bytes to write is less than 1");
			return false;
		}
		
		if (bb.length>32) {
			raise_log("AT24C32 Write Fail, bytes to write more than 32 bytes");
			return false;
		}
		
		if (!iscorrect) {
			raise_log("AT24C32 Write Fail, I2C not opened");
			return false;
		}
		
		boolean result = false;
		byte[] towrite = new byte[2+bb.length];
		towrite[1] = (byte) (start_address);
		towrite[0] = (byte) (start_address >> 8);		// MSB di index 0
		for(int ii=0;ii<bb.length;ii++) towrite[2+ii] = bb[ii];
		
		int wr_result = i2cdev.WriteBytes(towrite);
		if (wr_result==towrite.length) {
			result = true;
		} else {
			raise_log("AT24C32 Address+Data="+towrite.length+" bytes, Write Result="+wr_result+" bytes");
		}
		
		return result;
	}
	
	/**
	 * Read bytes from AT24C32
	 * AT24C32 only have 4096 bytes, addressed from 0 to 4095
	 * @param start_address :: 0 ~ 4095
	 * @param readcount : number of bytes to read
	 * @return bytes array if success, or null if failed
	 * @throws InterruptedException 
	 */
	public byte[] Read(int start_address, int readcount)  {
		if (start_address<0) {
			raise_log("AT24C32 Read Fail, start_address invalid");
			return null;
		}
		if (start_address>4095) {
			raise_log("AT24C32 Read Fail, start_address invalid");
			return null;
		}
		if (!iscorrect) {
			raise_log("AT24C32 Read Fail, I2C not opened");
			return null;
		}
		
		byte[] address = new byte[2];
		address[1] = (byte) (start_address);
		address[0] = (byte) (start_address>>8);
		
		if (i2cdev.WriteBytes(address)==2) {
			byte[] result = i2cdev.ReadBytes(readcount);
			if (result!=null) {
				if (result.length==readcount) {
					return result;
				}
			}
		}
		
		return null;
	}
	
	private void raise_log(String msg) {
		if (need_log_event) {
			bax.raiseEventFromDifferentThread(myobject, null, 0, event+"_log", false, new Object[] {msg});
		}
	}

	@Override
	///from I2C_BUS_Event and I2C_Device_Event
	public void Log(String msg) {
		raise_log(msg);
	}
}
