package org.flashtool.jna.win32;

import org.flashtool.jna.adb.AdbUtility;
import org.flashtool.jna.linux.JUsb;
import org.flashtool.jna.win32.SetupApi.HDEVINFO;
import org.flashtool.jna.win32.SetupApi.SP_DEVICE_INTERFACE_DETAIL_DATA;
import org.flashtool.jna.win32.SetupApi.SP_DRVINFO_DATA;

import com.sun.jna.platform.win32.SetupApi.SP_DEVICE_INTERFACE_DATA;
import com.sun.jna.platform.win32.SetupApi.SP_DEVINFO_DATA;
import com.sun.jna.platform.win32.Guid.GUID;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.Native;
import com.sun.jna.win32.W32APIOptions;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class JsetupAPi {
	
	static SetupApi setupapi = (SetupApi) Native.loadLibrary("setupapi", SetupApi.class, W32APIOptions.UNICODE_OPTIONS);
    public static GUID USBGuid = new GUID();
    private static SP_DEVINFO_DATA DeviceInfoData = new SP_DEVINFO_DATA();
    private static SP_DRVINFO_DATA DriverInfoData = new SP_DRVINFO_DATA();
    
    static {
	    USBGuid.Data1=0xA5DCBF10;
	    USBGuid.Data2=0x6530;
	    USBGuid.Data3=0x11D2;
	    USBGuid.Data4=new byte[8];
	    USBGuid.Data4[0]=(byte)0x90;
	    USBGuid.Data4[1]=(byte)0x1F;
	    USBGuid.Data4[2]=(byte)0x00;
	    USBGuid.Data4[3]=(byte)0xC0;
	    USBGuid.Data4[4]=(byte)0x4F;
	    USBGuid.Data4[5]=(byte)0xB9;
	    USBGuid.Data4[6]=(byte)0x51;
	    USBGuid.Data4[7]=(byte)0xED;
	    DeviceInfoData.cbSize = DeviceInfoData.size();
	    DriverInfoData.cbSize = DriverInfoData.size();
    }
	
	public static String getClassName(GUID guid) {
		char[] ClassName = new char[100];
		boolean result = setupapi.SetupDiClassNameFromGuid(guid, ClassName, 100, null);
		if (result) {
			String name = new String();
			for (int i=0;i<100;i++) {
				if (ClassName[i]!=0)
					name = name+ClassName[i];
			}
			return name;
		}
		else {
			log.error("Error calling SetupDiClassNameFromGuid");
		}
		return "";
	}

	public static GUID getGUID(String classname) {
		GUID[] ClassGuidList= new GUID[100];
		IntByReference size = new IntByReference();
		boolean result = setupapi.SetupDiClassGuidsFromName(classname, ClassGuidList, 100, size);
		if (result && (size.getValue()==1)) {
			return ClassGuidList[0];
		}
		else {
			log.error("Error calling SetupDiClassNameFromGuid for "+classname);
		}
		return null;
	}

	public static void destroyHandle(HDEVINFO hDevInfo) {
		setupapi.SetupDiDestroyDeviceInfoList(hDevInfo);
	}
	
	public static SP_DEVINFO_DATA enumDevInfo(HDEVINFO hDevInfo, int index) {
		int result = setupapi.SetupDiEnumDeviceInfo(hDevInfo, index, DeviceInfoData);
		if (result == 0) {
			return null;
		}
		return DeviceInfoData;
	}

	public static boolean buildDriverList(HDEVINFO hDevInfo, SP_DEVINFO_DATA DeviceInfoData) {
		int buildresult = setupapi.SetupDiBuildDriverInfoList(hDevInfo, DeviceInfoData, SetupApi.SPDIT_COMPATDRIVER);
		if (buildresult == 0) {
			return false;
		}
		return true;
	}

	public static SP_DRVINFO_DATA enumDriverInfo(HDEVINFO hDevInfo, SP_DEVINFO_DATA DeviceInfoData, int index) {
		int result = setupapi.SetupDiEnumDriverInfo(hDevInfo, DeviceInfoData, SetupApi.SPDIT_COMPATDRIVER, index, DriverInfoData);
		if (result == 0) {
			return null;
		}
		return DriverInfoData;
	}

	public static SP_DRVINFO_DATA getDriver(HDEVINFO hDevInfo, SP_DEVINFO_DATA DeviceInfoData) {
	    SP_DRVINFO_DATA DriverInfoData = new SP_DRVINFO_DATA();
	    DriverInfoData.cbSize=DriverInfoData.size();
		int result = setupapi.SetupDiGetSelectedDriver(hDevInfo, DeviceInfoData, DriverInfoData);
		if (result == 0) {
			return null;
		}
		return DriverInfoData;
	}
	
	public static HDEVINFO getHandleForConnectedInterfaces() {
		return setupapi.SetupDiGetClassDevs(USBGuid, null, null, SetupApi.DIGCF_PRESENT|SetupApi.DIGCF_DEVICEINTERFACE);
	}
	
	public static HDEVINFO getHandleForConnectedDevices() {
		return setupapi.SetupDiGetClassDevs(null, null, null, SetupApi.DIGCF_PRESENT|SetupApi.DIGCF_ALLCLASSES);
	}

	public static boolean isInstalled(HDEVINFO DeviceInfoSet, SP_DEVINFO_DATA DeviceInfoData) {
		byte[] res = {'a','a','a','a'};
		setupapi.SetupDiGetDeviceRegistryProperty(DeviceInfoSet, DeviceInfoData, SetupApi.SPDRP_INSTALL_STATE,null,res,4,null);
		return (res[0]==0);
	}
	
	public static String getDevId(HDEVINFO DeviceInfoSet, SP_DEVINFO_DATA DeviceInfoData) {
		char[] DeviceId = new char[100];
		boolean result = setupapi.SetupDiGetDeviceInstanceId(DeviceInfoSet,	DeviceInfoData,	DeviceId, 100,null);
		if (result) {
			String name = new String();
			for (int i=0;i<100;i++) {
				if (DeviceId[i]!=0)
					name = name+DeviceId[i];
			}
			return name;
		}
		return "";
	}

	public static String getDevicePath(HDEVINFO hDevInfo, SP_DEVINFO_DATA DeviceInfoData) {
		String devpath = "";
        SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA();
        DeviceInterfaceData.cbSize = DeviceInterfaceData.size();
        /* Query the device using the index to get the interface data */
        int index=0;
        do {
        	int result = setupapi.SetupDiEnumDeviceInterfaces(hDevInfo, DeviceInfoData, USBGuid,index, DeviceInterfaceData);
        	if (result == 0) {
        		break;        		
        	}
    		/* A successful query was made, use it to get the detailed data of the device */
    	    IntByReference reqlength = new IntByReference();
    	    /* Obtain the length of the detailed data structure, and then allocate space and retrieve it */
    	    result = setupapi.SetupDiGetDeviceInterfaceDetail(hDevInfo, DeviceInterfaceData, null, 0, reqlength, null);
    	    // Create SP_DEVICE_INTERFACE_DETAIL_DATA structure and set appropriate length for device Path */
    	    SP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData      = new SP_DEVICE_INTERFACE_DETAIL_DATA(reqlength.getValue());
    	    result = setupapi.SetupDiGetDeviceInterfaceDetail(hDevInfo, DeviceInterfaceData, DeviceInterfaceDetailData, reqlength.getValue(), reqlength, null);
    	    devpath = Native.toString(DeviceInterfaceDetailData.devicePath);
    	    if (devpath.length()==0) {
        	    SP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailDataDummy      = new SP_DEVICE_INTERFACE_DETAIL_DATA();
        	    DeviceInterfaceDetailData.cbSize=DeviceInterfaceDetailDataDummy.size();
        	    result = setupapi.SetupDiGetDeviceInterfaceDetail(hDevInfo, DeviceInterfaceData, DeviceInterfaceDetailData, reqlength.getValue(), reqlength, null);
        	    devpath = Native.toString(DeviceInterfaceDetailData.devicePath);
    	    }
            index++;
        } while (true);
        return devpath;
	}
	
}