using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace Win_API
{
   public static class NativeMethods
   {
      // The following GitHub link was very helpful in understanding how some of the WinApi calls work together, as well as
      // for certain const values like DIGCF_PRESENT. https://github.com/mikeobrien/HidLibrary/tree/dc9026067dd0c511574c8ce726682ece4f45939e

      //https://docs.microsoft.com/en-us/windows-hardware/drivers/install/setupapi

      //https://docs.microsoft.com/en-us/windows-hardware/drivers/hid/introduction-to-hid-concepts


      public const uint GENERIC_READ = 0x80000000;
      public const uint GENERIC_WRITE = 0x40000000;
      public const int FILE_FLAG_OVERLAPPED = 0x40000000;
      public const int FILE_ATTRIBUTE_NORMAL = 0x128;

      public const short DIGCF_PRESENT = 0x2;
      public const short DIGCF_DEVICEINTERFACE = 0x10;
      public const int DIGCF_ALLCLASSES = 0x4;

      public static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);

      /// <summary>
      /// Zero prevents other processes from opening a file or device if they request delete, read, or write access.
      /// </summary>
      public enum FileShareMode
      {
         Zero = 0x00000000,
         FILE_SHARE_DELETE = 0x00000004,
         FILE_SHARE_READ = 0x00000001,
         FILE_SHARE_WRITE = 0x00000002
      }

      public struct SECURITY_ATTRIBUTES
      {
         public int nLength;
         public IntPtr lpSecurityDescriptor;
         public bool bInheritHandle;
      }
      [DllImport("kernel32.dll")]
      public static extern bool CancelIo(IntPtr hFile);

      [DllImport("kernel32.dll")]
      public static extern IntPtr CreateFileA(
         string lpFileName,
         uint dwDesiredAccess,
         int dwShareMode,
         ref SECURITY_ATTRIBUTES lpSecurityAttributes,
         int dwCreationDisposition,
         int dwFlagsAndAttributes,
         IntPtr hTemplateFile);

      [DllImport("kernel32.dll")]
      public static extern bool ReadFile(
         IntPtr hFile,
         IntPtr lpBuffer,
         uint nNumberOfBytesToRead,
         out uint lpNumberOfBytesRead,
         [In] ref System.Threading.NativeOverlapped lpOverlapped);

      [DllImport("kernel32.dll")]
      public static extern bool WriteFile(
         IntPtr hFile,
         byte[] lpBuffer,
         uint nNumberOfBytesToWrite,
         out uint lpNumberOfBytesWritten,
         [In] ref System.Threading.NativeOverlapped lpOverlapped);

      [DllImport("kernel32.dll")]
      public static extern bool CloseHandle(IntPtr hHandle);

      /// <summary>
      /// The caller of this function must delete the returned device information set when it is no longer needed by calling SetupDiDestroyDeviceInfoList.
      /// </summary>
      /// <param name="guid"></param>
      /// <param name="hwndParent"></param>
      /// <returns></returns>
      [DllImport("Setupapi.dll")]
      public static extern IntPtr SetupDiCreateDeviceInfoList(Guid Guid, IntPtr HwndParent);

      [DllImport("setupapi.dll")]
      public static extern bool SetupDiCreateDeviceInfo(IntPtr DeviceInfoSet, string DeviceName, Guid ClassGuid, string DeviceDescription, uint CreationFlags, IntPtr DeviceInfoData);

      [DllImport("setupapi.dll")]
      public static extern bool SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet, uint MemberIndex, ref SP_DEVINFO_DATA DeviceInfoData);

      [DllImport("setupapi.dll")]
      public static extern bool SetupDiEnumDeviceInterfaces(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, Guid InterfaceClassGuid, uint MemberIndex, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData);

      [DllImport("setupapi.dll")]
      public static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, IntPtr DeviceInterfaceDetailData, uint DeviceInterfaceDetailDataSize, ref uint RequiredSize, IntPtr DeviceInfoData);

      [DllImport("setupapi.dll")]
      public static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, ref SP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData, uint DeviceInterfaceDetailDataSize, ref uint RequiredSize, IntPtr DeviceInfoData);

      [DllImport("setupapi.dll")]
      public static extern bool SetupDiGetClassDevs(Guid ClassGuid, IntPtr Enumerator, IntPtr hwndParent, uint Flags);

      [DllImport("setupapi.dll")]
      public static extern bool SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);

      /// <summary>
      /// HidD_GetAttributes returns TRUE if succeeds; otherwise, it returns FALSE.
      /// </summary>
      /// <param name="HidDeviceObject"></param>
      /// <param name="Attributes"></param>
      /// <returns></returns>
      [DllImport("hid.dll")]
      public static extern bool HidD_GetAttributes(IntPtr HidDeviceObject, HIDD_ATTRIBUTES Attributes);
      /// <summary>
      /// [out] Pointer to a caller-allocated GUID buffer that the routine uses to return the device interface GUID for HIDClass devices.
      /// </summary>
      /// <param name="HidGuid"></param>
      [DllImport("hid.dll")]
      public static extern void HidD_GetHidGuid(out Guid HidGuid);

      [DllImport("hid.dll")]
      public static extern bool HidD_GetIndexString(IntPtr HidDeviceObject, ulong StringIndex, out string Buffer, ulong BufferLength);
      /// <summary>
      /// The maximum possible number of characters in an embedded string is device specific. For USB devices, the maximum string length is 126 wide characters (not including the terminating NULL character).
      /// If the supplied buffer is not <= 4093 bytes(2^12 – 3) the call may fail(depending on the underlying protocol, HID/Bluetooth/SPI) with error code ERROR_GEN_FAILURE(0x0000001f).
      /// </summary>
      /// <param name="HidDeviceObject"></param>
      /// <param name="Buffer"></param>
      /// <param name="BufferLength"></param>
      /// <returns>bool</returns>
      [DllImport("hid.dll")]
      public static extern bool HidD_GetManufacturerString(IntPtr HidDeviceObject, out string Buffer, ulong BufferLength);

      /// <summary>
      /// HidD_GetPhysicalDescriptor returns TRUE if it succeeds; otherwise, it returns FALSE. Use GetLastError to get extended error information.
      /// </summary>
      /// <param name="HidDeviceObject"></param>
      /// <param name="Buffer"></param>
      /// <param name="BufferLength"></param>
      /// <returns></returns>
      [DllImport("hid.dll")]
      public static extern bool HidD_GetPhysicalDescriptor(IntPtr HidDeviceObject, out string Buffer, [In] ulong BufferLength);

      [DllImport("hid.dll")]
      public static extern bool HidD_GetPreparsedData(IntPtr HidDeviceObject, out HID_COLLECTION_INFORMATION PreparsedData);

      /// <summary>
      /// The supplied buffer must be <= 4093 bytes (2^12 – 3).
      /// </summary>
      /// <param name="HidDeviceObject"></param>
      /// <param name="Buffer"></param>
      /// <param name="BufferLength"></param>
      /// <returns></returns>
      [DllImport("hid.dll")]
      public static extern bool HidD_GetProductString(IntPtr HidDeviceObject, out string Buffer, ulong BufferLength);

      [DllImport("hid.dll")]
      public static extern bool HidD_GetSerialNumberString(IntPtr HidDeviceObject, out string Buffer, ulong BufferLength);

      [DllImport("hid.dll")]
      public static extern bool HidD_GetNumInputBuffers(IntPtr HidDeviceObject, out ulong NumberBuffers);
      [DllImport("hid.dll")]
      public static extern bool HidD_SetNumInputBuffers(IntPtr HidDeviceObject, ulong NumberBuffers);

      /// <summary>
      /// A caller of HidD_GetAttributes, uses this structure to obtain a device's vendor information.
      /// Before using a HIDD_ATTRIBUTES structure with HIDClass support routines, the caller must set the Size member.
      /// </summary>
      public struct HIDD_ATTRIBUTES
      {
         public ulong Size;
         public ushort VendorID;
         public ushort ProductID;
         public ushort VersionNumber;
      }

      public struct HID_COLLECTION_INFORMATION
      {
         public ulong DescriptorSize;
         public bool Polled;
         public char[] Reserved1;
         public ushort VendorID;
         public ushort ProductID;
         public ushort VersionNumber;
      }

      [StructLayout(LayoutKind.Sequential)]
      public struct SP_DEVICE_INTERFACE_DATA
      {
         public uint cbSize;
         public Guid InterfaceClassGuid;
         public uint Flags;
         public IntPtr Reserved;
      }

      [StructLayout(LayoutKind.Sequential)]
      public struct SP_DEVINFO_DATA
      {
         public uint cbSize;
         public Guid ClassGuid;
         public uint DevInst;
         public IntPtr Reserved;
      }

      [StructLayout(LayoutKind.Sequential)]
      public struct SP_DEVICE_INTERFACE_DETAIL_DATA
      {
         public uint cbSize;
         [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
         public string DevicePath;
      }





      public static SECURITY_ATTRIBUTES InitWithDefaultAttributes()
      {
         SECURITY_ATTRIBUTES attributes = new SECURITY_ATTRIBUTES
         {
            bInheritHandle = false,
            lpSecurityDescriptor = IntPtr.Zero
         };
         attributes.nLength = Marshal.SizeOf(attributes);
         return attributes;
      }
      public static IntPtr CreateNonManagedBuffer(byte[] buffer)
      {
         return Marshal.AllocHGlobal(buffer.Length);
      }

      public static byte[] CopyUnManagedBufferToManagedBuffer(IntPtr unManagedBuffer, int bytesRead)
      {
         var buffer = new byte[] { };
         Marshal.Copy(unManagedBuffer, buffer, 0, bytesRead);
         return buffer;
      }
      public static void DisposeNonManagedObject(IntPtr nonManagedObj)
      {
         Marshal.FreeHGlobal(nonManagedObj);
      }
   }

}
