package devices;

import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.keywords.Bit;
import jGPIO.I2C.I2C_BUS;
import jGPIO.I2C.I2C_Device;

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

	private BA bax;
	private Object myobject;
	private String event;
	private boolean need_log_event = false;
	
	private I2C_BUS i2cbus;
	private I2C_Device i2cdev;
	
	/**
	 * Initialize a PCF8574. 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;
		if (bax!=null) {
			if (caller!=null) {
				if (!event.isEmpty()) {
					need_log_event = bax.subExists(event+"_log");			
				}
			}
		}
		
		 Runtime.getRuntime().addShutdownHook(new Thread() {
	            @Override
	            public void run() {
	                Close();
	            }
	        });
	}
	
	/**
	 * Open a Slave
	 * @param bus_name : bus name of I2c
	 * @param slave_address : slave address of this PCF
	 * @return true if succcess
	 */
	public boolean Open(String bus_name, int slave_address) {
		if (!jGPIO.jGPIO.IsLinux) return false;
		i2cbus = new I2C_BUS(bus_name);
		if (i2cbus.Open_Bus()) {
			i2cdev = new I2C_Device(i2cbus.I2C_Handle(), slave_address);
			if (i2cdev.OpenDevice_SLAVE()) {
				raise_log("PCF8574 Address="+slave_address+" is opened");
				return true;
			}
		}
		raise_log("Failed to open PCF8574 Address="+slave_address);
		return false;
	}
	
	
	/**
	 * Check associated Slave Address
	 * @return -1 if no address
	 */
	public int getSlaveAddress() {
		if (i2cdev instanceof I2C_Device) 
			return i2cdev.SlaveAddress();
		else
			return -1;
	}
	
	/**
	 * Check if I2C to this PCF is opened
	 * @return true if opened
	 */
	public boolean getIsOpened() {
		if (i2cdev instanceof I2C_Device) {
			return i2cdev.IsOpened();
		} else return false;
		
	}
	
	/**
	 * Close PCF8574
	 */
	public void Close() {
		if (i2cdev instanceof I2C_Device) {
			i2cdev.CloseDevice();
			i2cdev = null;
		}
		
		if (i2cbus instanceof I2C_BUS) {
			i2cbus.Close_Bus();
			i2cbus = null;
		}
	}
	
	/**
	 * Write a byte to i2C
	 * @param bb  : byte to write
	 * @return true if success
	 */
	public synchronized boolean Write(byte bb) {
		if (i2cbus instanceof I2C_BUS) {
			if (i2cbus.IsOpened()) {
				if (i2cdev instanceof I2C_Device) {
					if (i2cdev.IsOpened()) {
						if (i2cdev.WriteBytes(new byte[] {bb})>0) return true;
						
					}
				}
			}
		}
		
		return false;
	}
	
	/**
	 * Read from I2C. Result in int, but actually value will 0 - 255 (byte size)
	 * @return -1 if failed
	 */
	public synchronized int Read() {
		if (i2cbus instanceof I2C_BUS) {
			if (i2cbus.IsOpened()) {
				if (i2cdev instanceof I2C_Device) {
					if (i2cdev.IsOpened()) {
						byte[] result = i2cdev.ReadBytes(1);
						if (result!=null) {
							return Bit.And(result[0], 0xFF);
						}
						
					}
				}
			}
		}
		return -1;
	}
	
	private void raise_log(String msg) {
		if (need_log_event) {
			bax.raiseEventFromDifferentThread(myobject, null, 0, event+"_log", false, new Object[] {msg});
		}
	}
}
