package jGPIO;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;

import com.sun.jna.Native;
import com.sun.jna.Platform;

import anywheresoftware.b4a.BA;

@BA.Author("Rudy Darmawan")
@BA.Version(1.14f)

@BA.ShortName("jGPIO")


public class jGPIO {
	
	@BA.Hide
	public static Linux_C_lib_DirectMapping libC = new Linux_C_lib_DirectMapping();
	@BA.Hide
	public final static int I2C_RETRIES = 0x701;				/* number of times a device address should be polled when not acknowledging */
	@BA.Hide
	public final static int I2C_TIMEOUT = 0x702;				/* set timeout in units of 10 ms */
	@BA.Hide
	public final static int I2C_SLAVE = 0x703;				/* Command at ioctl, means : Use this slave address */
	@BA.Hide
	public final static int I2C_TENBIT = 0x704;				/* 0 for 7 bit addrs, != 0 for 10 bit */
	@BA.Hide
	public final static int I2C_FUNCS = 0x705;				/* Command at ioctl, means : Get the adapter functionality */
	@BA.Hide
	public final static int I2C_SLAVE_FORCE = 0x706;		/* Command at ioctl, means : Use this slave address, even if it is already in use by a driver! */
	@BA.Hide
	public final static int I2C_RDWR = 0x707;				/* Command at ioctl, means : Combined R/W transfer (one stop only) */
	@BA.Hide
	public final static int I2C_PEC = 0x708;				/* != 0 to use PEC with SMBus */
	@BA.Hide
	public final static int I2C_SMBUS = 0x720;				/* SMBus transfer */
	
	/**
	 * Detected OS Name
	 */
	static public String osname = "";
	
	/**
	 * Detected OS Architecture
	 */
	static public String osarch = "";
	
	/**
	 * Detected OS Version
	 */
	static public String osversion = "";
	
	/**
	 * Detected default path separator symbol
	 */
	static public String separator = "";
	
	/**
	 * Detected Java Library Path
	 */
	static public String javaLibPath = "";
	
	/**
	 * Detected Java Virtual Machine running on this machine
	 */
	static public String jvm = "";
	
	/**
	 * Detected current Run Directory
	 */
	static public String rundir = "";
	
	/**
	 * Choosen Library Path inside JAR 
	 */
	static public String nativepath = "";	
	
	static public boolean IsWindows = false;
	static public boolean IsLinux = false;
	static public boolean IsMac = false;
	static public boolean IsAndroid = false;
	
	
	public static void detectOS() {
		osname = System.getProperty("os.name");
		osarch = System.getProperty("os.arch");
		osversion = System.getProperty("os.version");
		separator = System.getProperty("path.separator");
		javaLibPath = System.getProperty("java.library.path");
		jvm = System.getProperty("java.vm.name");
		File xxx = new File("");
		//rundir = System.getProperty("user.dir");
		rundir = xxx.getAbsolutePath();
		
		IsAndroid = false;
		if (BA.debugMode) {
			BA.Log("OS Name : "+osname);
			BA.Log("OS Architecture : "+osarch);
			BA.Log("OS Version : "+osversion);
			BA.Log("JVM Name : "+jvm);
			BA.Log("Java Lib path : "+javaLibPath);	
			BA.Log("Default Separator : "+separator);
		}
		
		
		osname = osname.toLowerCase();
		osarch = osarch.toLowerCase();
		jvm = jvm.toLowerCase();
		if (osname.contains("window")) {
			if (osarch.contains("86")) {
				IsWindows = true;
				nativepath = "/lib/win32/";
				BA.Log("System : Windows 32bit");
			} else if (osarch.contains("64")) {
				IsWindows = true;
				nativepath = "/lib/win64/";
				BA.Log("System : Windows 64bit");
			} else {
				BA.Log("System : Unsupported Windows");
			}
		} else if (osname.contains("linux")) {
			if (Platform.isAndroid()) {
				IsAndroid = true;
				BA.Log("System : Android (auto extract)");	
				
			} else {
				if (osarch.contains("arm")) {
					nativepath = "/lib/raspberry";
					IsLinux = true;
					BA.Log("System : Raspberry / ARM");
				} else if (osarch.contains("aarch64")) {
					nativepath = "/lib/aarch64";
					IsLinux = true;
					BA.Log("System : ARM64");
				} else if (osarch.contains("86")) {
					nativepath = "/lib/linux32";
					IsLinux = true;
					BA.Log("System : Linux 32bit");
				} else if (osarch.contains("64")) {
					nativepath = "/lib/linux64";
					IsLinux = true;
					BA.Log("System : Linux 64bit");
				}
			}
		} else if (osname.contains("mac")) {
			nativepath = "/lib/macos";
			IsMac = true;
			BA.Log("System : Mac OS");
		}
		else {
			BA.Log("System : Unsupported OS");
		}
	}
	
			// copy library from JAR to outside
			@BA.Hide
			protected static boolean copylib(String sourcepath, String libname, String targetpath){
					if (sourcepath=="") {
						if (BA.debugMode) BA.Log("copylib "+libname+" failed, sourcepath is empty");
						return false;
					}
					if (libname=="") {
						if (BA.debugMode) BA.Log("copylib failed, libname is empty");
						return false;
					}
					if (targetpath=="") {
						if (BA.debugMode) BA.Log("copylib "+libname+" failed, targetpath is empty");
						return false;
					}
					File ft = new File(targetpath,"");
					if (!ft.exists()) {
						if (BA.debugMode) BA.Log("copylib "+libname+" failed, targetpath is not exist");
						return false;
					}
					if (!ft.isDirectory()) {
						if (BA.debugMode) BA.Log("copylib "+libname+" failed, targetpath is not directory");
						return false;
					}
					if (!ft.canWrite()) {
						if (BA.debugMode) BA.Log("copylib "+libname+" failed, targetpath is not writable");
						return false;
					}
					if (!ft.canRead()) {
						if (BA.debugMode) BA.Log("copylib "+libname+" failed, targetpath is not readable");
						return false;
					}
					if (!ft.canExecute()) {
						if (BA.debugMode) BA.Log("copylib "+libname+" failed, targetpath is not executable");
						return false;
					}
					File ftarget = new File(targetpath,libname);
					if (BA.debugMode) BA.Log("copylib targetfile is "+ftarget.getPath());
					try {
						File fsource = Native.extractFromResourcePath(sourcepath+libname);
						if (BA.debugMode)  BA.Log("copylib sourcefile is "+fsource.getPath());
						if (fsource.length()==ftarget.length()) {
							if (BA.debugMode) BA.Log("copylib "+libname+" canceled, file already exist");
							return false;
						}
						if (fsource.renameTo(ftarget)) {
							if (BA.debugMode) BA.Log("Copied "+libname+" to "+targetpath);
							return true;
						} else {
							if (BA.debugMode) BA.Log("Failed to copy "+libname+" to "+targetpath);
							return false;
						}
					} catch (IOException e) {
						if (BA.debugMode) BA.Log("Failed to extract "+sourcepath+libname+" Msg : "+e.getMessage());
						return false;
					}
					
				};
				
				@BA.Hide
				public static boolean loadLibrary(String Name)
			    {
			        boolean result = true;
			        try
			        {
			        	
			        	if (IsAndroid) {
			        		// android
			        		BA.Log("Trying to load Android library " + Name);
					        System.loadLibrary(Name);
				   	
			        	} else {
			        		// other
			        		File flib = new File(rundir,System.mapLibraryName(Name));
			        		BA.Log("Trying to load library "+flib.getPath());
			        		System.load(flib.getPath());
			        	}
			            BA.Log("Library " + Name + " loaded");
			        }
			        catch(UnsatisfiedLinkError e)
			        {
			        	BA.Log("Cannot load library \"" + Name + "\"");
			            e.printStackTrace();
			            result = false;
			        }

			        return result;
			    }
				
				@BA.Hide
				public static boolean extract_lib(String libname) {
					if (libname=="") return false;
					String libraryname = System.mapLibraryName(libname);
					return copylib(nativepath, libraryname, rundir);
				}
				
				@BA.Hide
				public static Method GetMethod_byName(Object classnya, String methodname) {
					if (classnya==null) return null;
					if (methodname=="") return null;
					try {
						return classnya.getClass().getMethod(methodname);
					} catch (NoSuchMethodException | SecurityException e) {
						if (BA.debugMode) BA.Log("No Method with Name="+methodname+" in "+classnya.getClass().getName()+", Msg :"+e.getMessage());
						return null;
					}
				}
				
				@BA.Hide
				public static Method GetMethod_byName(Object classnya, String methodname, Class<?> param) {
					if (classnya==null) return null;
					if (methodname=="") return null;
					try {
						return classnya.getClass().getMethod(methodname, param);	
					} catch (NoSuchMethodException | SecurityException e) {
						if (BA.debugMode) BA.Log("No Method with Name="+methodname+" in "+classnya.getClass().getName()+", Msg :"+e.getMessage());
						return null;
					}
					
				}
				
				@BA.Hide
				public static Method GetMethod_byName(Object classnya, String methodname, Class<?> param1, Class<?> param2)  {
					if (classnya==null) return null;
					if (methodname=="") return null;
					try {
						return classnya.getClass().getMethod(methodname, param1, param2);
					}catch (NoSuchMethodException | SecurityException e) {
						if (BA.debugMode) BA.Log("No Method with Name="+methodname+" in "+classnya.getClass().getName()+", Msg :"+e.getMessage());
						return null;
					}
					
				}
				
				@BA.Hide
				public static Method GetMethod_byName(Object classnya, String methodname, Class<?> param1, Class<?> param2, Class<?> param3)  {
					if (classnya==null) return null;
					if (methodname=="") return null;
					try {
						return classnya.getClass().getMethod(methodname, param1, param2, param3);
					}catch (NoSuchMethodException | SecurityException e) {
						if (BA.debugMode) BA.Log("No Method with Name="+methodname+" in "+classnya.getClass().getName()+", Msg :"+e.getMessage());
						return null;
					}
					
				}
				
				@BA.Hide
				public static Method GetMethod_byName(Object classnya, String methodname, Class<?> param1, Class<?> param2, Class<?> param3, Class<?> param4)  {
					if (classnya==null) return null;
					if (methodname=="") return null;
					try {
						return classnya.getClass().getMethod(methodname, param1, param2, param3, param4);
					}catch (NoSuchMethodException | SecurityException e) {
						if (BA.debugMode) BA.Log("No Method with Name="+methodname+" in "+classnya.getClass().getName()+", Msg :"+e.getMessage());
						return null;
					}
					
				}
				
				@BA.Hide
				public static Method GetMethod_byName(Object classnya, String methodname, Class<?> param1, Class<?> param2, Class<?> param3, Class<?> param4, Class<?> param5)  {
					if (classnya==null) return null;
					if (methodname=="") return null;
					try {
						return classnya.getClass().getMethod(methodname, param1, param2, param3, param4, param5);
					}catch (NoSuchMethodException | SecurityException e) {
						if (BA.debugMode) BA.Log("No Method with Name="+methodname+" in "+classnya.getClass().getName()+", Msg :"+e.getMessage());
						return null;
					}
					
				}
				
				@BA.Hide
				public static boolean ExtractJarToRunDir(String jarname, String resourcepath) {
					File jarfile = new File(rundir, jarname);
					if (jarfile.exists()) {
						// sudah ada
						BA.Log("File "+jarname+" already exist in "+rundir);
						return true;
					} else {
						// belum ada
						try {
							File source = Native.extractFromResourcePath(resourcepath);
							if (source.renameTo(jarfile)) {
								BA.Log("Success extract "+jarname+" to "+jarfile.getAbsolutePath());
								return true;
							} else {
								BA.Log("Failed extract "+jarname);
								return false;
							}
						} catch (IOException e) {
							BA.Log("Unable to extract "+resourcepath+", Msg : "+e.getMessage());
							return false;
						}
						
					
					}
				}

				
	public static void main(String[] args) {
		System.out.println("JGPIO main");
	}	
}
