using System.Reflection;
using System.Runtime.InteropServices;

namespace CosmicDriver;

internal static class NativeMethods
{
    private const string LibraryName = "cosmicsdk";

    static NativeMethods()
    {
        NativeLibrary.SetDllImportResolver(typeof(NativeMethods).Assembly, Resolve);
    }

    private static nint Resolve(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
    {
        if (!string.Equals(libraryName, LibraryName, StringComparison.OrdinalIgnoreCase))
        {
            return nint.Zero;
        }

        string? rawPath = Environment.GetEnvironmentVariable("COSMICSDK_LIB_PATH");
        if (string.IsNullOrWhiteSpace(rawPath))
        {
            return nint.Zero;
        }

        string candidate = rawPath;
        if (Directory.Exists(rawPath))
        {
            candidate = OperatingSystem.IsWindows()
                ? Path.Combine(rawPath, "cosmicsdk.dll")
                : OperatingSystem.IsMacOS()
                    ? Path.Combine(rawPath, "libcosmicsdk.dylib")
                    : Path.Combine(rawPath, "libcosmicsdk.so");
        }

        return File.Exists(candidate) ? NativeLibrary.Load(candidate) : nint.Zero;
    }

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    internal delegate void NotificationCallback(nint notificationPtr);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_Create", CallingConvention = CallingConvention.Cdecl)]
    internal static extern nint CosmicSDK_Create(int timeoutMs);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_Destroy", CallingConvention = CallingConvention.Cdecl)]
    internal static extern void CosmicSDK_Destroy(nint handle);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_onNotification", CallingConvention = CallingConvention.Cdecl)]
    internal static extern void CosmicSDK_onNotification(nint handle, NotificationCallback callback);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_notificationAsI3cHotJoin", CallingConvention = CallingConvention.Cdecl)]
    [return: MarshalAs(UnmanagedType.I1)]
    internal static extern bool CosmicSDK_notificationAsI3cHotJoin(nint notificationPtr, ref NativeI3cHotJoinNotification outValue);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_notificationAsI3cIbi", CallingConvention = CallingConvention.Cdecl)]
    [return: MarshalAs(UnmanagedType.I1)]
    internal static extern bool CosmicSDK_notificationAsI3cIbi(nint notificationPtr, ref NativeI3cIbiNotification outValue);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_notificationAsGpio", CallingConvention = CallingConvention.Cdecl)]
    [return: MarshalAs(UnmanagedType.I1)]
    internal static extern bool CosmicSDK_notificationAsGpio(nint notificationPtr, ref NativeGpioInterruptNotification outValue);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_notificationAsUartRx", CallingConvention = CallingConvention.Cdecl)]
    [return: MarshalAs(UnmanagedType.I1)]
    internal static extern bool CosmicSDK_notificationAsUartRx(nint notificationPtr, ref NativeUartRxNotification outValue);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_Connect", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    [return: MarshalAs(UnmanagedType.I1)]
    internal static extern bool CosmicSDK_Connect(nint handle, string? devicePath);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_ConnectWithSerialNumber", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    [return: MarshalAs(UnmanagedType.I1)]
    internal static extern bool CosmicSDK_ConnectWithSerialNumber(nint handle, string serialNumber);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_Disconnect", CallingConvention = CallingConvention.Cdecl)]
    internal static extern void CosmicSDK_Disconnect(nint handle);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_GetDeviceInfo", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_GetDeviceInfo(nint handle, ref NativeDeviceInfo info);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_EnterBootMode", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_EnterBootMode(nint handle);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I2cSetBusVoltage", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I2cSetBusVoltage(nint handle, ushort voltageMv, [MarshalAs(UnmanagedType.I1)] bool persist);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I2cControllerInit", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I2cControllerInit(nint handle, byte bus, uint frequencyHz, byte pullUp);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I2cSetParameters", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I2cSetParameters(nint handle, byte bus, uint frequencyHz, byte pullUp);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I2cSetPullUpResistors", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I2cSetPullUpResistors(nint handle, byte bus, byte value);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I2cWrite", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I2cWrite(nint handle, byte bus, byte address, byte[]? subAddress, byte subAddressLength, byte[]? inData, uint dataLength, [MarshalAs(UnmanagedType.I1)] bool nonStop);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I2cRead", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I2cRead(nint handle, byte bus, byte address, byte[]? subAddress, byte subAddressLength, ushort readLength, [Out] byte[] outData, [MarshalAs(UnmanagedType.I1)] bool nonStop);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I2cScanBus", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I2cScanBus(nint handle, [Out] byte[] deviceAddresses, byte deviceAddressesLength, out byte count7Bit, out byte count10Bit, byte bus, [MarshalAs(UnmanagedType.I1)] bool include10Bit);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cSetBusVoltage", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cSetBusVoltage(nint handle, ushort voltageMv);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cControllerInit", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cControllerInit(nint handle, int pushPullRate, int openDrainRate, int i2cRate, int driveStrength);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cSetParameters", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cSetParameters(nint handle, int pushPullRate, int openDrainRate, int i2cRate, int driveStrength);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cInitBus", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cInitBus(nint handle, NativeI3cTargetInfo[]? targets, byte count);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cResetBus", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cResetBus(nint handle);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cGetDeviceTable", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cGetDeviceTable(nint handle, [Out] NativeI3cTargetInfo[] targets, byte maxCount, out byte foundCount);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cSetTargetDeviceConfiguration", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cSetTargetDeviceConfiguration(nint handle, byte targetAddress, byte configuration);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cLegacyI2CWrite", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cLegacyI2CWrite(nint handle, byte address, byte[]? subAddress, byte subAddressLength, byte[]? inData, ushort dataLength, int flags);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cLegacyI2CRead", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cLegacyI2CRead(nint handle, byte address, byte[]? subAddress, byte subAddressLength, ushort readLength, [Out] byte[] outData, int flags);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cSdrWrite", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cSdrWrite(nint handle, byte address, byte[]? subAddress, byte subAddressLength, byte[]? inData, ushort dataLength, int flags);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cSdrRead", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cSdrRead(nint handle, byte address, byte[]? subAddress, byte subAddressLength, ushort readLength, [Out] byte[] outData, out ushort outLength, int flags);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cHdrDdrWrite", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cHdrDdrWrite(nint handle, byte address, byte command, byte[]? inData, ushort dataLength);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cHdrDdrRead", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cHdrDdrRead(nint handle, byte address, byte command, ushort readLength, out ushort outLength, [Out] byte[] outData);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cTargetResetPattern", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cTargetResetPattern(nint handle);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cHdrExitPattern", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cHdrExitPattern(nint handle);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cCccRSTDAA", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cCccRSTDAA(nint handle);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cCccENTDAA", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cCccENTDAA(nint handle);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cCccSETDASA", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cCccSETDASA(nint handle, byte staticAddress, byte dynamicAddress);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cCccSETAASA", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cCccSETAASA(nint handle, byte[]? staticAddresses, nuint count);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cCccSETNEWDA", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cCccSETNEWDA(nint handle, byte oldAddress, byte newAddress);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cCccGETPID", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cCccGETPID(nint handle, byte address, [Out] byte[] pidOut);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cCccGETBCR", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cCccGETBCR(nint handle, byte address, out byte bcr);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cCccGETDCR", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cCccGETDCR(nint handle, byte address, out byte dcr);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cCccGETSTATUS", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cCccGETSTATUS(nint handle, byte targetAddress, [Out] byte[] buffer, out byte outLength, [MarshalAs(UnmanagedType.I1)] bool hasDefByte, byte defByte);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cCccGETCAPS", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cCccGETCAPS(nint handle, byte targetAddress, [Out] byte[] buffer, out byte outLength, [MarshalAs(UnmanagedType.I1)] bool hasDefByte, byte defByte);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cCccRSTACTBroadcast", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cCccRSTACTBroadcast(nint handle, int definingByte);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cCccRSTACTDirect", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cCccRSTACTDirect(nint handle, byte targetAddress, int definingByte, int direction, out byte valueOut);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cCccENECBroadcast", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cCccENECBroadcast(nint handle, byte events);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cCccENECDirect", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cCccENECDirect(nint handle, byte address, byte events);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cCccDISECBroadcast", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cCccDISECBroadcast(nint handle, byte events);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cCccDISECDirect", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cCccDISECDirect(nint handle, byte address, byte events);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cCccGETMWL", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cCccGETMWL(nint handle, byte address, out ushort mwl);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cCccSETMWLBroadcast", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cCccSETMWLBroadcast(nint handle, ushort mwl);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cCccSETMWLDirect", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cCccSETMWLDirect(nint handle, byte address, ushort mwl);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cCccGETMRL", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cCccGETMRL(nint handle, byte address, out ushort mrl, out byte ibiLength, [MarshalAs(UnmanagedType.I1)] out bool ibiLengthReturned);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cCccSETMRLBroadcast", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cCccSETMRLBroadcast(nint handle, ushort mrl, [MarshalAs(UnmanagedType.I1)] bool hasIbiLength, byte ibiLength);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_I3cCccSETMRLDirect", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_I3cCccSETMRLDirect(nint handle, byte address, ushort mrl, [MarshalAs(UnmanagedType.I1)] bool hasIbiLength, byte ibiLength);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_UartSetBusVoltage", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_UartSetBusVoltage(nint handle, ushort voltageMv, [MarshalAs(UnmanagedType.I1)] bool persist);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_UartInit", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_UartInit(nint handle, int baudrate, int hwHandshake, int parity, int dataSize, int stopBit);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_UartSetParameters", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_UartSetParameters(nint handle, int baudrate, int hwHandshake, int parity, int dataSize, int stopBit);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_UartSendMessage", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_UartSendMessage(nint handle, byte[] inData, ushort dataLength);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_GpioSetVoltage", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_GpioSetVoltage(nint handle, ushort voltageMv, [MarshalAs(UnmanagedType.I1)] bool persist);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_GpioConfigurePin", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_GpioConfigurePin(nint handle, int pinNumber, int functionality, [MarshalAs(UnmanagedType.I1)] bool setInitial, int initialLevel);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_GpioDigitalWrite", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_GpioDigitalWrite(nint handle, int pinNumber, int logicLevel);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_GpioDigitalRead", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_GpioDigitalRead(nint handle, int pinNumber, out int logicLevel);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_GpioConfigureInterrupt", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_GpioConfigureInterrupt(nint handle, int pinNumber, int trigger);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_GpioDisableInterrupt", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_GpioDisableInterrupt(nint handle, int pinNumber);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_SpiSetBusVoltage", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_SpiSetBusVoltage(nint handle, ushort voltageMv, [MarshalAs(UnmanagedType.I1)] bool persist);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_SpiControllerInit", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_SpiControllerInit(nint handle, int bitOrder, int mode, int dataWidth, int chipSelect, int chipSelectPolarity, uint frequencyHz);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_SpiSetParameters", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_SpiSetParameters(nint handle, int bitOrder, int mode, int dataWidth, int chipSelect, int chipSelectPolarity, uint frequencyHz);

    [DllImport(LibraryName, EntryPoint = "CosmicSDK_SpiTransfer", CallingConvention = CallingConvention.Cdecl)]
    internal static extern ushort CosmicSDK_SpiTransfer(nint handle, byte[]? inData, ushort inDataLength, [Out] byte[] outData, ushort transferLength);
}
