package jGPIO.DigitalInput;


import java.io.IOException;

import anywheresoftware.b4a.BA;
import jGPIO.jGPIO;
import jGPIO.mycodes;

@BA.ShortName("DigitalInput")
@BA.Events(values={"error(pinnumber as int, Msg as String)"})

public class DigitalInput {
	private BA bax;
	private String event="";
	private int pinnya=0;
	private Object myobject;
	
	private boolean need_error_event = false;
	
	private int last_value = -1;
	private boolean currentIsON = false;
	private boolean initialized = false;
	private DigitalInputEvent javaevent;
	
	/**
	 * Buat di JAVA coding
	 */
	public DigitalInput() {
		// do nothing here, buat syarat
		if (jGPIO.osname=="") jGPIO.detectOS();
	}
	
	/**
	 * Buat di JAVA coding
	 * @param pin_number : pin number
	 */
	public DigitalInput(int pin_number) {
		if (jGPIO.osname=="") jGPIO.detectOS();
		pinnya = pin_number;
		if (Setup_Pin()) initialized = true;
	}
	
	/*
	 * Initialize Digital Input
	 * Return true if initialization success, or false if failed
	 */
	public boolean Initialize(BA xx, Object caller, String eventname,int pinnumber){
		bax = xx;
		event=eventname;
		pinnya = pinnumber;
		myobject = caller;
		
		if (bax!=null) {
			if (myobject!=null) {
				if (!event.isEmpty()) {
					need_error_event = bax.subExists(event+"_error");
				}
			}
		}
		
		
		
		initialized = false;
		
		if (jGPIO.IsLinux) {
			if (Setup_Pin()) {
				initialized = true;
				 Runtime.getRuntime().addShutdownHook(new Thread() {
			            @Override
			            public void run() {
			                Release();
			            }
			        });
			} else {
				initialized = false;
			}
		} else {
			raise_Error("System is not Linux based, or jGPIO function detectOS() has not been called");
		}
		
		return initialized;
		
	}
	
	private boolean Setup_Pin() {
		if (mycodes.GPIO_Exist(pinnya)==false) {
			// belum ada, coba export dulu
			try {
				if (mycodes.GPIO_Export(pinnya)==false) {
					// gagal export
					return false;
				}
			} catch (IOException e) {
				BA.Log("Gagal Export pin "+pinnya+" Untuk DigitalInput");
			}
		}
		
		// sampe sini, harusnya ada
		try {
			// harus set output dulu
			if (mycodes.GPIO_SetDirection(pinnya, true)) {
				// pasang value 1
				if (mycodes.GPIO_SetValue(pinnya, true)) {
					// ubah jadi input
					if (mycodes.GPIO_SetDirection(pinnya, false)) {
						BA.Log("DigitalInput Setup_Pin success !!");
						return true;
					} else BA.Log("Gagal SetDirection ke Input untuk DigitalInput pin "+pinnya);
				} else BA.Log("Gagal SetValue ke high untuk DigitalInput pin "+pinnya);
			} else BA.Log("Gagal SetDirection ke Output untuk DigitalInput pin "+pinnya);
			
		} catch (IOException e) {
			BA.Log("Gagal SetDirection pin "+pinnya+", Msg : "+e.getMessage());
		}
		return false;
	}
	
	/*
	private boolean Setup_Pin() {
		if (mycodes.GPIO_Exist(pinnya)==false) {
			// not exist, so export first
			try {
				if (mycodes.GPIO_Export(pinnya)==false) {
					// could not export, pin can not use
					raise_Error("DigitalInput pin="+pinnya+" GPIO_Export failed");
					return false;
				}
			} catch (IOException e) {
				raise_Error("DigitalInput pin="+pinnya+" GPIO_Export error, Msg:"+e.getMessage()+", Caused:"+e.getCause());
				return false;
			}
			
			// check existance after export
			if (mycodes.GPIO_Exist(pinnya)==false) {
				// still not exist, pin can not use
				raise_Error("DigitalInput pin="+pinnya+" GPIO_Exist still failed after GPIO_Export");
				return false;
			}
		}
		

		
		// Revision 08/01/2020
		// to set as Input, firstly PIN must be SetDirection as OUT , and SetValue = 1. This need to be done to force internal pull up become 1
		// then SetDirection set as IN
		
		// force SetDirection to OUT
		try {
			boolean force1 = mycodes.GPIO_SetDirection(pinnya, true);
			if (!force1) {
				raise_Error("Failed to Force GPIO_SetDirection=out at pin="+pinnya);
				return false;
			}
		} catch (IOException e1) {
			raise_Error("DigitalInput pin="+pinnya+" Force GPIO_SetDirection=out is failed, Msg:"+e1.getMessage()+", Caused:"+e1.getCause());
			return false;
		}
		
		// force SetValue to TRUE
		try {
			boolean force2 = mycodes.GPIO_SetValue(pinnya, true);
			if (!force2) {
				raise_Error("Failed to Force GPIO_SetValue=1 at pin="+pinnya);
				return false;
			}
		} catch (IOException e1) {
			raise_Error("DigitalInput pin="+pinnya+" Force GPIO_SetValue=1 is failed, Msg:"+e1.getMessage()+", Caused:"+e1.getCause());
			return false;
		}
		
		// now SetDirection to IN
		try {
			boolean force3 = mycodes.GPIO_SetDirection(pinnya, false);
			if (!force3) {
				raise_Error("Failed to GPIO_SetDirection=in at pin="+pinnya);
				return false;
			}
		} catch (IOException e1) {
			raise_Error("DigitalInput pin="+pinnya+" GPIO_SetDirection=in is failed, Msg:"+e1.getMessage()+", Caused:"+e1.getCause());
			return false;
		}
		
		
		
		last_value = -1;
		try {
			last_value = mycodes.GPIO_GetValue(pinnya);
		} catch (IOException e) {
			raise_Error("DigitalInput pin="+pinnya+" GPIO_GetValue #1 error, Msg:"+e.getMessage()+", Caused:"+e.getCause());
			return false;
		}
		
		if (last_value==-1) {
			// could not read from this GPIO
			raise_Error("DigitalInput pin="+pinnya+" GPIO_GetValue #1 failed");
			return false;
		}

		
		if (last_value==0) 
			currentIsON = false;
		else
			currentIsON = true;
		
		return true;
	}
	*/
	
	@BA.Hide
	public void SetJavaEvent(DigitalInputEvent xx) {
		javaevent = xx;
	}
	
	public boolean getIsInitialized(){
		return initialized;
	}
	
	
	/**
	 * Read State of GPIO
	 * @return 0 = Low, 1 = high, -1 = failed reading
	 */
	public int ReadState(){
		try {
			int value = mycodes.GPIO_GetValue(pinnya);
			if (value==0) {
				currentIsON = false;
			} else if (value==1) {
				currentIsON = true;
			}
			
			if (last_value != value) {
				last_value = value;
				if (javaevent!=null) {
					if (last_value == 0) {
						javaevent.newinstate(pinnya, false);
					} else {
						javaevent.newinstate(pinnya, true);	
					}
					
				}
			}
				
			return value;
		} catch (IOException e) {
			raise_Error("DigitalInput pin="+pinnya+" ReadState error, Msg:"+e.getMessage()+", Caused:"+e.getCause());
			return -1;
		}
	}
	
	
	/*
	 * Get last ReadState value
	 * return true if High 1
	 * return false if Low  0
	 */
	public boolean getIsON(){
		return currentIsON;
	}
	
	/*
	 * Release control of Digital Output (not controlling anymore)
	 * return true if success operation
	 */
	public boolean Release(){
		try {
			return mycodes.GPIO_UnExport(pinnya);
		} catch (IOException e) {
			raise_Error("DigitalInput pin="+pinnya+" Release error, Msg:"+e.getMessage()+", Caused:"+e.getCause());
			return false;
		}
	}
	
	private void raise_Error(String msg) {
		if (need_error_event) {
			bax.raiseEventFromDifferentThread(myobject, null, 0, event+"_error", false,new Object[]{pinnya,msg});			
		}
		if (javaevent!=null) {
			javaevent.error(pinnya, msg);
		}
		
	}
	
}
