using System.Runtime.InteropServices;

namespace CosmicDriver;

public static class CosmicConstants
{
    public const int I3C_MAX_TARGETS = 11;
    public const int I3C_MAX_CCC_BUFFER_LENGTH = 64;
}

public enum NotificationType
{
    NOTIF_I3C_HJ = 0,
    NOTIF_I3C_IBI = 1,
    NOTIF_GPIO = 2,
    NOTIF_UART = 3,
    NOTIF_UNKNOWN = 4,
}

public enum I2cBus : byte
{
    I2C_BUS_A = 0x00,
    I2C_BUS_B = 0x01,
}

public enum I2cPullUp : byte
{
    I2C_PULLUP_150Ohm = 0x00,
    I2C_PULLUP_220Ohm = 0x01,
    I2C_PULLUP_330Ohm = 0x02,
    I2C_PULLUP_470Ohm = 0x03,
    I2C_PULLUP_680Ohm = 0x04,
    I2C_PULLUP_1kOhm = 0x05,
    I2C_PULLUP_1_5kOhm = 0x06,
    I2C_PULLUP_2_2kOhm = 0x07,
    I2C_PULLUP_3_3kOhm = 0x08,
    I2C_PULLUP_4kOhm = 0x09,
    I2C_PULLUP_4_7kOhm = 0x0A,
    I2C_PULLUP_10kOhm = 0x0B,
    I2C_PULLUP_DISABLE = 0x0C,
}

public enum I3cPushPullRate
{
    PUSH_PULL_2_5_MHz_DC_10 = 0,
    PUSH_PULL_2_5_MHz_DC_15 = 1,
    PUSH_PULL_2_5_MHz_DC_20 = 2,
    PUSH_PULL_2_5_MHz_DC_25 = 3,
    PUSH_PULL_3_125_MHz_DC_12_5 = 4,
    PUSH_PULL_3_125_MHz_DC_18_75 = 5,
    PUSH_PULL_3_125_MHz_DC_25 = 6,
    PUSH_PULL_3_125_MHz_DC_31_25 = 7,
    PUSH_PULL_5_MHz_DC_20 = 8,
    PUSH_PULL_5_MHz_DC_30 = 9,
    PUSH_PULL_5_MHz_DC_40 = 10,
    PUSH_PULL_5_MHz_DC_50 = 11,
    PUSH_PULL_6_25_MHz_DC_25 = 12,
    PUSH_PULL_6_25_MHz_DC_37_5 = 13,
    PUSH_PULL_6_25_MHz_DC_50 = 14,
    PUSH_PULL_7_5_MHz_DC_30 = 15,
    PUSH_PULL_7_5_MHz_DC_45 = 16,
    PUSH_PULL_10_MHz_DC_40 = 17,
    PUSH_PULL_12_5_MHz_DC_50 = 18,
}

public enum I3cOpenDrainTransferRate
{
    OPEN_DRAIN_100_KHz = 0,
    OPEN_DRAIN_400_KHz = 1,
    OPEN_DRAIN_1_MHz = 2,
    OPEN_DRAIN_2_MHz = 3,
    OPEN_DRAIN_4_17_MHz = 4,
}

public enum I2cTransferRate
{
    STANDARD_MODE = 0,
    FAST_MODE = 1,
    FAST_MODE_PLUS = 2,
    HIGH_SPEED_MODE = 3,
    ULTRA_FAST_MODE = 4,
}

public enum I3cDriveStrength
{
    STANDARD = 0,
    FAST = 1,
}

public enum I3cTransferDirection
{
    WRITE = 0,
    READ = 1,
}

[Flags]
public enum I3cTransferFlags
{
    I3C_XFER_NONE = 0,
    I3C_XFER_NONSTOP = 1 << 0,
    I3C_XFER_START_WITH_7E = 1 << 1,
}

public enum I3cTargetResetDefByte
{
    NO_RESET = 0x00,
    RESET_I3C_PERIPHERAL = 0x01,
    RESET_WHOLE_TARGET = 0x02,
    RESET_DEBUG_NETWORK = 0x03,
    VIRTUAL_TARGET_DETECT = 0x04,
    RETURN_TIME_RESET_PERIPHERAL = 0x81,
    RETURN_TIME_RESET_WHOLE_TARGET = 0x82,
    RETURN_TIME_DEBUG_NETWORK_RESET = 0x83,
    RETURN_VIRTUAL_TARGET_INDICATION = 0x84,
}

[Flags]
public enum I3cEvent
{
    IBI = 0x01,
    CR = 0x02,
    HJ = 0x08,
}

public enum UartBaudRate
{
    UART_BAUD_600 = 0x00,
    UART_BAUD_1200 = 0x01,
    UART_BAUD_2400 = 0x02,
    UART_BAUD_4800 = 0x03,
    UART_BAUD_9600 = 0x04,
    UART_BAUD_14400 = 0x05,
    UART_BAUD_19200 = 0x06,
    UART_BAUD_38400 = 0x07,
    UART_BAUD_56000 = 0x08,
    UART_BAUD_57600 = 0x09,
    UART_BAUD_115200 = 0x0A,
}

public enum UartParity
{
    UART_PARITY_NONE = 0x00,
    UART_PARITY_EVEN = 0x01,
    UART_PARITY_ODD = 0x02,
}

public enum UartDataSize
{
    UART_DATA_SIZE_7 = 0x00,
    UART_DATA_SIZE_8 = 0x01,
}

public enum UartStopBit
{
    UART_STOP_BIT_1 = 0x00,
    UART_STOP_BIT_2 = 0x01,
}

public enum UartHwHandshake
{
    UART_HW_HANDSHAKE_DIS = 0x00,
    UART_HW_HANDSHAKE_EN = 0x01,
}

public enum GpioPinNumber
{
    GPIO_1 = 0x00,
    GPIO_2 = 0x01,
    GPIO_3 = 0x02,
    GPIO_4 = 0x03,
    GPIO_5 = 0x04,
    GPIO_6 = 0x05,
}

public enum GpioLogicLevel
{
    GPIO_LOW = 0x00,
    GPIO_HIGH = 0x01,
}

public enum GpioFunctionality
{
    GPIO_DIGITAL_INPUT = 0x00,
    GPIO_DIGITAL_OUTPUT = 0x01,
}

public enum GpioTriggerType
{
    GPIO_TRIGGER_RISING_EDGE = 0x01,
    GPIO_TRIGGER_FALLING_EDGE = 0x02,
    GPIO_TRIGGER_BOTH_EDGES = 0x03,
}

public enum SpiBitOrder
{
    SPI_BIT_ORDER_MSB = 0x00,
    SPI_BIT_ORDER_LSB = 0x01,
}

public enum SpiMode
{
    SPI_MODE_0 = 0x00,
    SPI_MODE_1 = 0x01,
    SPI_MODE_2 = 0x02,
    SPI_MODE_3 = 0x03,
}

public enum SpiDataWidth
{
    SPI_DATA_WIDTH_8_BITS = 0x00,
    SPI_DATA_WIDTH_16_BITS = 0x01,
}

public enum SpiChipSelect
{
    SPI_CHIP_SELECT_0 = 0x00,
    SPI_CHIP_SELECT_1 = 0x01,
    SPI_CHIP_SELECT_2 = 0x02,
    SPI_CHIP_SELECT_3 = 0x03,
}

public enum SpiChipSelectPolarity
{
    SPI_CS_ACTIVE_LOW = 0x00,
    SPI_CS_ACTIVE_HIGH = 0x01,
}

public sealed record DeviceInfo(
    string Manufacturer,
    string ProductName,
    string SerialNumber,
    string FwVersion,
    string HwVersion
);

public sealed record I3cTargetConfiguration(
    int TargetType = 0,
    bool IbiRequest = false,
    bool CrRequest = false,
    bool DaaUseSetdasa = false,
    bool DaaUseSetaasa = false,
    bool DaaUseEntdaa = false,
    bool IbiTimestampEnable = false,
    bool PendingReadCapability = false
)
{
    public byte ToByte()
    {
        byte value = 0;
        value |= (byte)(TargetType & 0x01);
        value |= (byte)(IbiRequest ? 1 << 1 : 0);
        value |= (byte)(CrRequest ? 1 << 2 : 0);
        value |= (byte)(DaaUseSetdasa ? 1 << 3 : 0);
        value |= (byte)(DaaUseSetaasa ? 1 << 4 : 0);
        value |= (byte)(DaaUseEntdaa ? 1 << 5 : 0);
        value |= (byte)(IbiTimestampEnable ? 1 << 6 : 0);
        value |= (byte)(PendingReadCapability ? 1 << 7 : 0);
        return value;
    }

    public static I3cTargetConfiguration FromByte(byte value) => new(
        TargetType: value & 0x01,
        IbiRequest: (value & (1 << 1)) != 0,
        CrRequest: (value & (1 << 2)) != 0,
        DaaUseSetdasa: (value & (1 << 3)) != 0,
        DaaUseSetaasa: (value & (1 << 4)) != 0,
        DaaUseEntdaa: (value & (1 << 5)) != 0,
        IbiTimestampEnable: (value & (1 << 6)) != 0,
        PendingReadCapability: (value & (1 << 7)) != 0
    );
}

public sealed record I3cBcr(
    byte Byte,
    bool MaxDataSpeedLimit,
    bool IbiRequestCapable,
    bool IbiPayload,
    bool OfflineCapable,
    bool VirtualTargetSupport,
    bool AdvancedCapabilities,
    int DeviceRole
)
{
    public static I3cBcr FromByte(byte value) => new(
        Byte: value,
        MaxDataSpeedLimit: (value & (1 << 0)) != 0,
        IbiRequestCapable: (value & (1 << 1)) != 0,
        IbiPayload: (value & (1 << 2)) != 0,
        OfflineCapable: (value & (1 << 3)) != 0,
        VirtualTargetSupport: (value & (1 << 4)) != 0,
        AdvancedCapabilities: (value & (1 << 5)) != 0,
        DeviceRole: (value >> 6) & 0x03
    );
}

public sealed record I3cTargetInfo(
    byte StaticAddress,
    byte DynamicAddress,
    byte[] Pid,
    I3cBcr Bcr,
    byte Dcr,
    ushort Mwl,
    ushort Mrl,
    byte MaxIbiPayloadLength,
    I3cTargetConfiguration Configuration
)
{
    internal NativeI3cTargetInfo ToNative() => new()
    {
        StaticAddress = StaticAddress,
        DynamicAddress = DynamicAddress,
        Pid = (Pid.Length >= 6 ? Pid[..6] : new byte[6]).ToArray(),
        Bcr = Bcr.Byte,
        Dcr = Dcr,
        Mwl = Mwl,
        Mrl = Mrl,
        MaxIbiPayloadLength = MaxIbiPayloadLength,
        Configuration = Configuration.ToByte(),
    };

    internal static I3cTargetInfo FromNative(NativeI3cTargetInfo value) => new(
        StaticAddress: value.StaticAddress,
        DynamicAddress: value.DynamicAddress,
        Pid: value.Pid.ToArray(),
        Bcr: I3cBcr.FromByte(value.Bcr),
        Dcr: value.Dcr,
        Mwl: value.Mwl,
        Mrl: value.Mrl,
        MaxIbiPayloadLength: value.MaxIbiPayloadLength,
        Configuration: I3cTargetConfiguration.FromByte(value.Configuration)
    );
}

public sealed record I3cHotJoinNotification(byte[] Pid, byte Bcr, byte Dcr, byte DynamicAddress);
public sealed record I3cIbiNotification(byte Address, byte[] Payload);
public sealed record GpioInterruptNotification(byte PinNumber);
public sealed record UartRxNotification(byte[] Data);

public sealed record Notification(
    NotificationType Type,
    byte[] RawPayload,
    I3cHotJoinNotification? I3cHotJoin = null,
    I3cIbiNotification? I3cIbi = null,
    GpioInterruptNotification? Gpio = null,
    UartRxNotification? UartRx = null
);

public sealed record I2cScanResult(byte[] Addresses7Bit, ushort[] Addresses10Bit);
public sealed record I3cGetMrlResult(ushort MaxReadLength, byte? IbiLength);

[StructLayout(LayoutKind.Sequential)]
internal struct NativeDeviceInfo
{
    public nint Manufacturer;
    public nuint ManufacturerLength;
    public nint ProductName;
    public nuint ProductNameLength;
    public nint SerialNumber;
    public nuint SerialNumberLength;
    public nint FwVersion;
    public nuint FwVersionLength;
    public nint HwVersion;
    public nuint HwVersionLength;
}

[StructLayout(LayoutKind.Sequential)]
internal struct NativeNotification
{
    public int Type;
    public nint Payload;
    public nuint PayloadLength;
}

[StructLayout(LayoutKind.Sequential)]
internal struct NativeI3cHotJoinNotification
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    public byte[] Pid;
    public byte Bcr;
    public byte Dcr;
    public byte DynamicAddress;
}

[StructLayout(LayoutKind.Sequential)]
internal struct NativeI3cIbiNotification
{
    public byte Address;
    public nint Payload;
    public nuint PayloadLength;
}

[StructLayout(LayoutKind.Sequential)]
internal struct NativeUartRxNotification
{
    public nint Data;
    public nuint DataLength;
}

[StructLayout(LayoutKind.Sequential)]
internal struct NativeGpioInterruptNotification
{
    public byte PinNumber;
}

[StructLayout(LayoutKind.Sequential)]
internal struct NativeI3cTargetInfo
{
    public byte StaticAddress;
    public byte DynamicAddress;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    public byte[] Pid;
    public byte Bcr;
    public byte Dcr;
    public ushort Mwl;
    public ushort Mrl;
    public byte MaxIbiPayloadLength;
    public byte Configuration;
}
