package jGPIO.DigitalOutput;


import java.io.IOException;

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

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

public class DigitalOutput {
	private BA bax;
	private String event="";
	private int pinnya=0;
	private Object myobject;
	private boolean need_error_event = false;
	
	
	private boolean initialized = false;
	
	private DigitalOutputEvent javaevent;
	
	/**
	 * Buat di JAVA coding
	 */
	public DigitalOutput(){
		// do nothing here, cuma buat syarat
		if (jGPIO.osname=="") jGPIO.detectOS();
		
	}
	
	/**
	 * Buat di JAVA coding
	 * @param pin_number : pin number
	 * @param initialstate : true --> initial state = high, false --> initial state = low
	 */
	public DigitalOutput(int pin_number, boolean initialstate){
		pinnya = pin_number;
		if (jGPIO.osname=="") jGPIO.detectOS();
		if (Setup_Pin(initialstate)) initialized = true;
	}
	
	/*
	 * Initialize Digital Output
	 * Return true if initialization success, or false if failed
	 */
	public boolean Initialize(BA xx, Object caller, String eventname,int pinnumber, boolean initialstate){
		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(initialstate)) {
				initialized = true;
				 Runtime.getRuntime().addShutdownHook(new Thread() {
			            @Override
			            public void run() {
			                Release();
			            }
			        });
				return true;
					
			} else {
				initialized = false;
			}
		} else {
			raise_Error("System is not Linux based, or jGPIO function detectOS() has not been called");
		}
		return false;
		
		
		
	}
	
	@BA.Hide
	public void SetJavaEvent(DigitalOutputEvent xx) {
		javaevent = xx;
	}
	
	public boolean getIsInitialized(){
		return initialized;
	}
	
	
	
	
	private boolean Setup_Pin(boolean initialstate) {
		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("DigitalOutput pin="+pinnya+" GPIO_Export failed");
					return false;
				}
			} catch (IOException e) {
				raise_Error("DigitalOutput 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("DigitalOutput pin="+pinnya+" GPIO_Exist still failed after GPIO_Export");
				return false;
			}
		}
		
		// Exported pin exist, now check if direction is correct
		String currentdirection;
		try {
			currentdirection = mycodes.GPIO_GetDirection(pinnya).trim();
			
		} catch (IOException e) {
			raise_Error("DigitalOutput pin="+pinnya+" GPIO_GetDirection #1 error, Msg:"+e.getMessage()+", Caused:"+e.getCause());
			return false;
		}
		
		if (currentdirection.startsWith("out")==false) {
			// wrong direction, change direction
			try {
				if (mycodes.GPIO_SetDirection(pinnya, true)==false) {
					// could not change direction, pin can not use
					raise_Error("DigitalOutput pin="+pinnya+" GPIO_SetDirection failed");
					return false;
				}
			} catch (IOException e) {
				raise_Error("DigitalOutput pin="+pinnya+" GPIO_SetDirection error, Msg:"+e.getMessage()+", Caused:"+e.getCause());
				return false;
			}
			
			// check again after change direction
			try {
				currentdirection = mycodes.GPIO_GetDirection(pinnya).trim();
				
			} catch (IOException e) {
				raise_Error("DigitalOutput pin="+pinnya+" GPIO_GetDirection #2 error, Msg:"+e.getMessage()+", Caused:"+e.getCause());
				return false;
			}
			
			if (currentdirection.startsWith("out")==false) {
				// direction is not changing, pin can not use
				raise_Error("DigitalOutput pin="+pinnya+" GPIO_GetDirection still not 'out' after GPIO_SetDirection");
				return false;
			}
		}
		
		// pin direction is correct
		try {
			if (mycodes.GPIO_SetValue(pinnya, initialstate)==false) {
				// could not set Value, pin can not use
				raise_Error("DigitalOutput pin="+pinnya+" GPIO_SetValue failed");
				return false;
			}
		} catch (IOException e) {
			raise_Error("DigitalOutput pin="+pinnya+" GPIO_SetValue error, Msg:"+e.getMessage()+", Caused:"+e.getCause());
			return false;
		}
		
		return true;
	}
	
	
	/*
	 * Set Digital Output to High
	 * return true if success operation
	 */
	public boolean SetHigh(){
		try {
			return mycodes.GPIO_SetValue(pinnya, true);
		} catch (IOException e) {
			raise_Error("DigitalOutput pin="+pinnya+" SetHigh error, Msg:"+e.getMessage()+", Caused:"+e.getCause());
			return false;
		}
	}
	
	/*
	 * Set Digital Output to Low
	 * return true if success operation
	 */
	public boolean SetLow(){
		try {
			return mycodes.GPIO_SetValue(pinnya, false);
		} catch (IOException e) {
			raise_Error("DigitalOutput pin="+pinnya+" SetLow error, Msg:"+e.getMessage()+", Caused:"+e.getCause());
			return false;
		}
	}
	
	/*
	 * 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("DigitalOutput 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);
		}
		
	}
	
}
