package devices.RTC_I2C;

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

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

public class DS1307 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 = 0x68;
	private I2C_BUS i2cbus;
	private I2C_Device i2cdev;
	
	private boolean iscorrect = false;
	
	private int controlbyte = -1;
	
	/**
	 * Initialize DS1307 (RTC)
	 * Warning : Untested !
	 * @param caller : caller object
	 * @param eventname : eventname
	 */
	public void Initialize(BA ba, Object caller, String eventname) {
		this.bax = ba;
		this.myobject = caller;
		this.event = eventname;
		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 DS1307
	 * @param bus_name : i2c bus name
	 * @param slave_address : DS1307 address, default at 0x68
	 * @return true if success
	 */
	public boolean Open(String bus_name, int slave_address) {
		iscorrect = false;
		if (!jGPIO.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_SLAVEFORCE()) {
				iscorrect = true;
				this.dev_address = slave_address;
			}
		}
		
		if (iscorrect) {
			raise_log("DS1307 is Opened");
		} else {
			raise_log("DS1307 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 DS3231 is opened
	 * @return true if opened
	 */
	public boolean getIsOpened() {
		return iscorrect;
	}
	
	/**
	 * Read date and time information from RTC
	 * @return RTC_Data class, check on IsCorrectData() to know valid or not
	 */
	public RTC_Data Read_RTC() {
		RTC_Data result = new RTC_Data();
		if (iscorrect) {
			int writeaddr_result = i2cdev.WriteBytes(new byte[] {0});	// start reading from address 0
			if (writeaddr_result==1) {
				byte[] bb = i2cdev.ReadBytes(7);
				result.Initialize_from_DS1307(bb);
			} else {
				raise_log("Read_RTC address write is failed");
			}
		}
		return result;
	}
	
	/**
	 * Set Internal OScillator ON or oFF
	 * @param isON : if true, internal oscillator will be ON
	 * @return true if success
	 */
	public boolean SetOscillator(boolean isON) {
		if (iscorrect) {
			int writeaddr_result = i2cdev.WriteBytes(new byte[] {0});
			if (writeaddr_result==1) {
				byte[] bb = i2cdev.ReadBytes(1);
				if (bb!=null) {
					if (bb.length>=1) {
						int temp = bb[0] & 0xFF;
						if (isON) {
							temp &= 0x7F;		// CH = 0
						} else {
							temp |= 0x80;		// CH = 1
						}
						byte[] writeback = new byte[] {0, (byte)temp};
						int wrback_result = i2cdev.WriteBytes(writeback);
						if (wrback_result==writeback.length) {
							return true;
						}
					}
				}
			}
		}
		return false;
	}
	
	/**
	 * Read Control bytes
	 * @return true if success
	 */
	public boolean Read_ControlByte() {
		controlbyte = -1;
		if (iscorrect) {
			byte[] address = new byte[] {0x07};
			if (i2cdev.WriteBytes(address)==1) {
				byte[] vv = i2cdev.ReadBytes(1);
				if (vv!=null) {
					if (vv.length>=1) {
						controlbyte = (vv[0] & 0xFF);
						return true;
					}
				}
			}
		}
		return false;
	}
	
	/**
	 * Get Square Wave Output status
	 * @return 0 = disabled, 1 = enabled, -1 = no data (call Read_ControlByte function)
	 */
	public int Get_SquareWaveOutput_status() {
		if (controlbyte==-1) return -1;
		if ((controlbyte & 0x40)==0) return 0; else return 1;
	}
	
	
	/**
	 * Set Square Wave Output
	 * @param isON : if true, SQW enabled
	 * @param freqcode : 0 (default) = 1 Hz, 1 = 4096 Hz, 2 = 8192 Hz, 3 = 32768 Hz
	 * @return true if success
	 */
	public boolean Set_SquareWaveOutput(boolean isON, int freqcode) {
		if (iscorrect) {
			if (controlbyte>=0) {
				int temp = controlbyte;
				if (isON) {
					temp |= 0x10;	// SQWE = 1
					temp &= 0xFC;	// RS1 = 0, RS0 = 0
					switch(freqcode) {
					case 1 :
							// RS1 = 0, RS0 = 1
							temp |= 1;	
							break;
					case 2 :
							// RS1 = 1 , RS0 = 0
							temp |= 2;	
							break;
					case 3 :
							// RS1 = 1, RS0 = 1
							temp |= 3;
							break;
					}
				} else {
					temp &= 0xE0;	// SQWE = 0, RS1 = 0, RS0 =0
				}
				byte[] addr_data = new byte[] {0x07, (byte) temp};
				int write_result = i2cdev.WriteBytes(addr_data);
				if (write_result==addr_data.length) {
					controlbyte = temp;
					return true;
				}
			}
		}
		return false;
	}
	
	/**
	 * DS1307 has internal RAM, sized 56 bytes
	 * This function write to its RAM
	 * @param start_address : 0 - 55
	 * @param bb : bytes data
	 * @return true if success
	 */
	public boolean Write_RAM(int start_address, byte[] bb) {
		if (iscorrect) {
			byte[] addr_data = new byte[1+bb.length];
			addr_data[0] = (byte) (0x08 + start_address);		// RAM start at address 8
			for(int ii=0;ii<bb.length;ii++) addr_data[1+ii] = bb[ii];	// copy to it
			int wr_result = i2cdev.WriteBytes(addr_data);
			if (wr_result==addr_data.length) {
				return true;
			}
			
		}
		return false;
	}
	
	/**
	 * DS1307 has internal RAM, sized 56 bytes
	 * This function read from its RAM
	 * @param start_address : 0 - 55
	 * @param readcount : how many bytes to read
	 * @return byte array, or null if failed
	 */
	public byte[] Read_RAM(int start_address, int readcount) {
		if (iscorrect) {
			byte[] addr = new byte[] {(byte)(0x08+start_address)};		// RAM start at address 8
			if (i2cdev.WriteBytes(addr)==1) {
				byte[] result = i2cdev.ReadBytes(readcount);
				return result;
			}
		}
		return null;
	}
	
	/**
	 * Write date and time information to RTC
	 * @param value : RTC_Data class. Must be initialized, and IsCorrectData must be true
	 * @return true if success
	 */
	public boolean Write_RTC(RTC_Data value) {
		if (iscorrect) {
			if (value!=null) {
				if (value.IsCorrectData()) {
					byte[] address_data = value.Get_DS1307_bytes();
					int writeresult = i2cdev.WriteBytes(address_data);
					if (writeresult==address_data.length) return true;
				}
			}
		}
		
		return false;
	}
	
	@Override
	///from I2C_BUS_Event and I2C_Device_Event
	public void Log(String msg) {
		raise_log(msg);
	}

	private void raise_log(String msg) {
		if (need_log_event) {
			bax.raiseEventFromDifferentThread(myobject, null, 0, event+"_log", false, new Object[] {msg});
		}
	}
}
