package devices;



import java.util.Timer;
import java.util.TimerTask;

import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.keywords.DateTime;
import jGPIO.jGPIO;
import jGPIO.DigitalInput.DigitalInput;
import jGPIO.DigitalInput.DigitalInputEvent;


@BA.ShortName("HardwareButton")
@BA.Events(values= {
		"log(msg as string)",
		"shortpressed(tick as long)",
		"longpressed(tick as long)",
		"currentstate(isON as boolean, tick as long)"
})

/**
 * Button class
 * use class DigitalInput
 * Button is always Active Low, so Pin need to be Pull Up using Resistor to VCC
 * @author rdkartono
 *
 */
public class Button implements DigitalInputEvent {
	private DigitalInput di;
	private BA bax;
	private Object myobject;
	private String event;
	private int mypinnumber;
	
	private Timer tt;						// scan DigitalInput using timer
	private final int timerms = 50;			// timer trigger per 50 ms
	private int statecounter = 0;			// for detecting shortpressed or longpressed
	private final int shortpresslimit = 4;	// 4 x 50ms = 200ms
	private final int longpresslimit = 40;	// 40 x 50ms = 2000ms = 2 seconds
	
	
	private boolean initialized = false;
	
	
	private boolean need_log_event = false;
	private boolean need_shortpressed_event = false;
	private boolean need_longpressed_event = false;
	private boolean need_currentstate_event = false;
	
	public Button() {
		if (jGPIO.osname=="") jGPIO.detectOS();
	}
	
	
	public boolean Initialize(BA ba, Object caller, String buttonname, int pin_number) {
		myobject = caller;
		bax = ba;
		
		event = buttonname.trim();
		mypinnumber = pin_number;
		initialized = false;
		
		if (bax!=null) {
			if (caller!=null) {
				if (!event.isEmpty()) {
					need_log_event = bax.subExists(event+"_log");
					need_shortpressed_event = bax.subExists(event+"_shortpressed");
					need_longpressed_event = bax.subExists(event+"_longpressed");
					need_currentstate_event = bax.subExists(event+"_currentstate");
				}
			}
		}
		
		
		if (jGPIO.IsLinux==false) {
			raise_log("Target device is not Linux");
			return false;
		}
		
		di = new DigitalInput();
		di.SetJavaEvent(this);		// link javaevent to this class
		
		di.Initialize(bax, caller, event, pin_number);
		
		if (di.getIsInitialized()) {
			
			 Runtime.getRuntime().addShutdownHook(new Thread() {
		            @Override
		            public void run() {
		                Release();
		            }
		        });
			 
			TimerTask task = new TimerTask() {

				@Override
				public void run() {
					if (di == null) {
						cancel();		// stop timer
						raise_log("DigitalInput is null, Button Timer stopped");
						return;
					}
					if (di.getIsInitialized()==false) {
						cancel();		// stop timer
						raise_log("DigitalInput is not initialized, Button Timer stopped");
						return;
					}
					int value = di.ReadState();
					
					if (value==-1) {
						cancel();		// stop timer
						raise_log("DigitalInput ReadState failed, Button Timer stopped");
						return;
					}
					
					
					if (value==1) {
						// high
						if (statecounter>=shortpresslimit) {
							if (statecounter<longpresslimit) {
								raise_shortpressed();								
							} 
						} 
						statecounter = 0;
					} else {
						// low
						if (statecounter<longpresslimit) {
							statecounter+=1;
							if (statecounter==longpresslimit) {
								raise_longpressed();
							}
						}
						
					}
				}
				
			};
			
			tt = new Timer();
			tt.scheduleAtFixedRate(task, timerms, timerms);
		
			initialized = true;
		} else {
			raise_log("Failed to Initialize DigitalInput on pinnumber="+pin_number);
		}
		
		
		return initialized;
	}
	
	public boolean getIsInitialized() {
		return initialized;
	}
	
	public void Release() {
		if (di instanceof DigitalInput) {
			di.Release();
			di = null;
		}
		if (tt != null) {
			tt.cancel();
			tt = null;
		}
		initialized = false;
	}
	
	public String getButtonName() {
		return event;
	}
	
	public int getPinNumber() {
		return mypinnumber;
	}
	
	private void raise_log(String msg) {
		if (need_log_event) {
			bax.raiseEventFromDifferentThread(myobject, null, 0, event+"_log", false, new Object[] {msg});
		}
	}
	
	private void raise_shortpressed() {
		if (need_shortpressed_event) {
			bax.raiseEventFromDifferentThread(myobject, null, 0, event+"_shortpressed", false, new Object[] {DateTime.getNow()});
		}
	}
	
	private void raise_longpressed() {
		if (need_longpressed_event) {
			bax.raiseEventFromDifferentThread(myobject, null, 0, event+"_longpressed", false, new Object[] {DateTime.getNow()});
		}
	}
	
	private void raise_currentstate(boolean state) {
		if (need_currentstate_event) {
			bax.raiseEventFromDifferentThread(myobject, null, 0, event+"_currentstate", false, new Object[] {state,DateTime.getNow()});
		}
	}

	@BA.Hide
	public void error(int pinnumber, String Msg) {
		// event from class DigitalInput
		raise_log("Button error, pin="+pinnumber+", Msg = "+Msg);
	}

	@BA.Hide
	public void newinstate(int pinnumber, boolean isOn) {
		// event from class DigitalInput
		if (BA.debugMode) {
			raise_log("Button pin="+pinnumber+" value = "+isOn);
		}
		raise_currentstate(isOn);
	}
}
