package com.binho.cosmicsdk;

import com.sun.jna.Callback;
import com.sun.jna.IntegerType;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Locale;

interface NativeMethods extends Library {
    NativeMethods INSTANCE = load();

    static NativeMethods load() {
        String rawPath = System.getenv("COSMICSDK_LIB_PATH");
        if (rawPath == null || rawPath.isBlank()) {
            return Native.load("cosmicsdk", NativeMethods.class);
        }

        Path candidate = Path.of(rawPath);
        if (Files.isDirectory(candidate)) {
            String os = System.getProperty("os.name", "").toLowerCase(Locale.ROOT);
            if (os.contains("win")) {
                candidate = candidate.resolve("cosmicsdk.dll");
            } else if (os.contains("mac")) {
                candidate = candidate.resolve("libcosmicsdk.dylib");
            } else {
                candidate = candidate.resolve("libcosmicsdk.so");
            }
        }

        return Native.load(candidate.toAbsolutePath().toString(), NativeMethods.class);
    }

    interface NotificationCallback extends Callback {
        void invoke(Pointer notificationPtr);
    }

    class SizeT extends IntegerType {
        public SizeT() {
            this(0);
        }

        public SizeT(long value) {
            super(Native.SIZE_T_SIZE, value, true);
        }
    }

    class NativeDeviceInfo extends Structure {
        public Pointer manufacturer;
        public SizeT manufacturerLength;
        public Pointer productName;
        public SizeT productNameLength;
        public Pointer serialNumber;
        public SizeT serialNumberLength;
        public Pointer fwVersion;
        public SizeT fwVersionLength;
        public Pointer hwVersion;
        public SizeT hwVersionLength;

        @Override
        protected List<String> getFieldOrder() {
            return List.of(
                    "manufacturer", "manufacturerLength",
                    "productName", "productNameLength",
                    "serialNumber", "serialNumberLength",
                    "fwVersion", "fwVersionLength",
                    "hwVersion", "hwVersionLength"
            );
        }
    }

    class NativeNotification extends Structure {
        public int type;
        public Pointer payload;
        public SizeT payloadLength;

        public NativeNotification() {
        }

        public NativeNotification(Pointer p) {
            super(p);
            read();
        }

        @Override
        protected List<String> getFieldOrder() {
            return List.of("type", "payload", "payloadLength");
        }
    }

    class NativeI3cHotJoinNotification extends Structure {
        public byte[] pid = new byte[6];
        public byte bcr;
        public byte dcr;
        public byte dynamicAddress;

        @Override
        protected List<String> getFieldOrder() {
            return List.of("pid", "bcr", "dcr", "dynamicAddress");
        }
    }

    class NativeI3cIbiNotification extends Structure {
        public byte address;
        public Pointer payload;
        public SizeT payloadLength;

        @Override
        protected List<String> getFieldOrder() {
            return List.of("address", "payload", "payloadLength");
        }
    }

    class NativeUartRxNotification extends Structure {
        public Pointer data;
        public SizeT dataLength;

        @Override
        protected List<String> getFieldOrder() {
            return List.of("data", "dataLength");
        }
    }

    class NativeGpioInterruptNotification extends Structure {
        public byte pinNumber;

        @Override
        protected List<String> getFieldOrder() {
            return List.of("pinNumber");
        }
    }

    class NativeI3cTargetInfo extends Structure {
        public byte staticAddress;
        public byte dynamicAddress;
        public byte[] pid = new byte[6];
        public byte bcr;
        public byte dcr;
        public short mwl;
        public short mrl;
        public byte maxIbiPayloadLength;
        public byte configuration;

        @Override
        protected List<String> getFieldOrder() {
            return List.of(
                    "staticAddress",
                    "dynamicAddress",
                    "pid",
                    "bcr",
                    "dcr",
                    "mwl",
                    "mrl",
                    "maxIbiPayloadLength",
                    "configuration"
            );
        }
    }

    Pointer CosmicSDK_Create(int timeoutMs);

    void CosmicSDK_Destroy(Pointer handle);

    void CosmicSDK_onNotification(Pointer handle, NotificationCallback callback);

    byte CosmicSDK_notificationAsI3cHotJoin(Pointer notificationPtr, NativeI3cHotJoinNotification outValue);

    byte CosmicSDK_notificationAsI3cIbi(Pointer notificationPtr, NativeI3cIbiNotification outValue);

    byte CosmicSDK_notificationAsGpio(Pointer notificationPtr, NativeGpioInterruptNotification outValue);

    byte CosmicSDK_notificationAsUartRx(Pointer notificationPtr, NativeUartRxNotification outValue);

    byte CosmicSDK_Connect(Pointer handle, String devicePath);

    byte CosmicSDK_ConnectWithSerialNumber(Pointer handle, String serialNumber);

    void CosmicSDK_Disconnect(Pointer handle);

    short CosmicSDK_GetDeviceInfo(Pointer handle, NativeDeviceInfo outInfo);

    short CosmicSDK_EnterBootMode(Pointer handle);

    short CosmicSDK_I2cSetBusVoltage(Pointer handle, short voltageMv, byte persist);

    short CosmicSDK_I2cControllerInit(Pointer handle, byte bus, int frequencyHz, byte pullUp);

    short CosmicSDK_I2cSetParameters(Pointer handle, byte bus, int frequencyHz, byte pullUp);

    short CosmicSDK_I2cSetPullUpResistors(Pointer handle, byte bus, byte value);

    short CosmicSDK_I2cWrite(Pointer handle, byte bus, byte address, Pointer subAddress, byte subAddressLength, Pointer inData, int dataLength, byte nonStop);

    short CosmicSDK_I2cRead(Pointer handle, byte bus, byte address, Pointer subAddress, byte subAddressLength, short readLength, Pointer outData, byte nonStop);

    short CosmicSDK_I2cScanBus(Pointer handle, Pointer deviceAddresses, byte deviceAddressesLength, byte[] count7Bit, byte[] count10Bit, byte bus, byte include10Bit);

    short CosmicSDK_I3cSetBusVoltage(Pointer handle, short voltageMv);

    short CosmicSDK_I3cControllerInit(Pointer handle, int pushPullRate, int openDrainRate, int i2cRate, int driveStrength);

    short CosmicSDK_I3cSetParameters(Pointer handle, int pushPullRate, int openDrainRate, int i2cRate, int driveStrength);

    short CosmicSDK_I3cInitBus(Pointer handle, NativeI3cTargetInfo[] targets, byte count);

    short CosmicSDK_I3cResetBus(Pointer handle);

    short CosmicSDK_I3cGetDeviceTable(Pointer handle, NativeI3cTargetInfo[] targets, byte maxCount, byte[] foundCount);

    short CosmicSDK_I3cSetTargetDeviceConfiguration(Pointer handle, byte targetAddress, byte configuration);

    short CosmicSDK_I3cLegacyI2CWrite(Pointer handle, byte address, Pointer subAddress, byte subAddressLength, Pointer inData, short dataLength, int flags);

    short CosmicSDK_I3cLegacyI2CRead(Pointer handle, byte address, Pointer subAddress, byte subAddressLength, short readLength, Pointer outData, int flags);

    short CosmicSDK_I3cSdrWrite(Pointer handle, byte address, Pointer subAddress, byte subAddressLength, Pointer inData, short dataLength, int flags);

    short CosmicSDK_I3cSdrRead(Pointer handle, byte address, Pointer subAddress, byte subAddressLength, short readLength, Pointer outData, short[] outLength, int flags);

    short CosmicSDK_I3cHdrDdrWrite(Pointer handle, byte address, byte command, Pointer inData, short dataLength);

    short CosmicSDK_I3cHdrDdrRead(Pointer handle, byte address, byte command, short readLength, short[] outLength, Pointer outData);

    short CosmicSDK_I3cTargetResetPattern(Pointer handle);

    short CosmicSDK_I3cHdrExitPattern(Pointer handle);

    short CosmicSDK_I3cCccRSTDAA(Pointer handle);

    short CosmicSDK_I3cCccENTDAA(Pointer handle);

    short CosmicSDK_I3cCccSETDASA(Pointer handle, byte staticAddress, byte dynamicAddress);

    short CosmicSDK_I3cCccSETAASA(Pointer handle, Pointer staticAddresses, SizeT count);

    short CosmicSDK_I3cCccSETNEWDA(Pointer handle, byte oldAddress, byte newAddress);

    short CosmicSDK_I3cCccGETPID(Pointer handle, byte address, Pointer pidOut);

    short CosmicSDK_I3cCccGETBCR(Pointer handle, byte address, byte[] bcrOut);

    short CosmicSDK_I3cCccGETDCR(Pointer handle, byte address, byte[] dcrOut);

    short CosmicSDK_I3cCccGETSTATUS(Pointer handle, byte targetAddress, Pointer buffer, byte[] outLength, byte hasDefByte, byte defByte);

    short CosmicSDK_I3cCccGETCAPS(Pointer handle, byte targetAddress, Pointer buffer, byte[] outLength, byte hasDefByte, byte defByte);

    short CosmicSDK_I3cCccRSTACTBroadcast(Pointer handle, int definingByte);

    short CosmicSDK_I3cCccRSTACTDirect(Pointer handle, byte targetAddress, int definingByte, int direction, byte[] valueOut);

    short CosmicSDK_I3cCccENECBroadcast(Pointer handle, byte events);

    short CosmicSDK_I3cCccENECDirect(Pointer handle, byte address, byte events);

    short CosmicSDK_I3cCccDISECBroadcast(Pointer handle, byte events);

    short CosmicSDK_I3cCccDISECDirect(Pointer handle, byte address, byte events);

    short CosmicSDK_I3cCccGETMWL(Pointer handle, byte address, short[] mwlOut);

    short CosmicSDK_I3cCccSETMWLBroadcast(Pointer handle, short mwl);

    short CosmicSDK_I3cCccSETMWLDirect(Pointer handle, byte address, short mwl);

    short CosmicSDK_I3cCccGETMRL(Pointer handle, byte address, short[] mrlOut, byte[] ibiLengthOut, byte[] ibiLengthReturnedOut);

    short CosmicSDK_I3cCccSETMRLBroadcast(Pointer handle, short mrl, byte hasIbiLength, byte ibiLength);

    short CosmicSDK_I3cCccSETMRLDirect(Pointer handle, byte address, short mrl, byte hasIbiLength, byte ibiLength);

    short CosmicSDK_UartSetBusVoltage(Pointer handle, short voltageMv, byte persist);

    short CosmicSDK_UartInit(Pointer handle, int baudrate, int hwHandshake, int parity, int dataSize, int stopBit);

    short CosmicSDK_UartSetParameters(Pointer handle, int baudrate, int hwHandshake, int parity, int dataSize, int stopBit);

    short CosmicSDK_UartSendMessage(Pointer handle, Pointer inData, short dataLength);

    short CosmicSDK_GpioSetVoltage(Pointer handle, short voltageMv, byte persist);

    short CosmicSDK_GpioConfigurePin(Pointer handle, int pinNumber, int functionality, byte setInitial, int initialLevel);

    short CosmicSDK_GpioDigitalWrite(Pointer handle, int pinNumber, int logicLevel);

    short CosmicSDK_GpioDigitalRead(Pointer handle, int pinNumber, int[] logicLevelOut);

    short CosmicSDK_GpioConfigureInterrupt(Pointer handle, int pinNumber, int trigger);

    short CosmicSDK_GpioDisableInterrupt(Pointer handle, int pinNumber);

    short CosmicSDK_SpiSetBusVoltage(Pointer handle, short voltageMv, byte persist);

    short CosmicSDK_SpiControllerInit(Pointer handle, int bitOrder, int mode, int dataWidth, int chipSelect, int chipSelectPolarity, int frequencyHz);

    short CosmicSDK_SpiSetParameters(Pointer handle, int bitOrder, int mode, int dataWidth, int chipSelect, int chipSelectPolarity, int frequencyHz);

    short CosmicSDK_SpiTransfer(Pointer handle, Pointer inData, short inDataLength, Pointer outData, short transferLength);
}
