from __future__ import annotations

"""Data types, enums, and structures used by the Python CosmicSDK binding."""

from ctypes import POINTER, Structure, Union, c_char, c_int, c_size_t, c_uint8, c_uint16, string_at
from dataclasses import dataclass
from enum import IntEnum, IntFlag
from typing import Optional

I3C_MAX_TARGETS = 11
I3C_MAX_CCC_BUFFER_LENGTH = 64


class NotificationType(IntEnum):
    NOTIF_I3C_HJ = 0
    NOTIF_I3C_IBI = 1
    NOTIF_GPIO = 2
    NOTIF_UART = 3
    NOTIF_UNKNOWN = 4


class I2cBus(IntEnum):
    I2C_BUS_A = 0x00
    I2C_BUS_B = 0x01


class I2cPullUp(IntEnum):
    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


class I3cPushPullRate(IntEnum):
    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


class I3cOpenDrainTransferRate(IntEnum):
    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


class I2cTransferRate(IntEnum):
    STANDARD_MODE = 0
    FAST_MODE = 1
    FAST_MODE_PLUS = 2
    HIGH_SPEED_MODE = 3
    ULTRA_FAST_MODE = 4


class I3cDriveStrength(IntEnum):
    STANDARD = 0
    FAST = 1


class I3cTransferDirection(IntEnum):
    WRITE = 0
    READ = 1


class I3cTransferFlags(IntFlag):
    I3C_XFER_NONE = 0
    I3C_XFER_NONSTOP = 1 << 0
    I3C_XFER_START_WITH_7E = 1 << 1


class I3cTargetResetDefByte(IntEnum):
    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


class I3cEvent(IntFlag):
    IBI = 0x01
    CR = 0x02
    HJ = 0x08


class UartBaudRate(IntEnum):
    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


class UartParity(IntEnum):
    UART_PARITY_NONE = 0x00
    UART_PARITY_EVEN = 0x01
    UART_PARITY_ODD = 0x02


class UartDataSize(IntEnum):
    UART_DATA_SIZE_7 = 0x00
    UART_DATA_SIZE_8 = 0x01


class UartStopBit(IntEnum):
    UART_STOP_BIT_1 = 0x00
    UART_STOP_BIT_2 = 0x01


class UartHwHandshake(IntEnum):
    UART_HW_HANDSHAKE_DIS = 0x00
    UART_HW_HANDSHAKE_EN = 0x01


class GpioPinNumber(IntEnum):
    GPIO_1 = 0x00
    GPIO_2 = 0x01
    GPIO_3 = 0x02
    GPIO_4 = 0x03
    GPIO_5 = 0x04
    GPIO_6 = 0x05


class GpioLogicLevel(IntEnum):
    GPIO_LOW = 0x00
    GPIO_HIGH = 0x01


class GpioFunctionality(IntEnum):
    GPIO_DIGITAL_INPUT = 0x00
    GPIO_DIGITAL_OUTPUT = 0x01


class GpioTriggerType(IntEnum):
    GPIO_TRIGGER_RISING_EDGE = 0x01
    GPIO_TRIGGER_FALLING_EDGE = 0x02
    GPIO_TRIGGER_BOTH_EDGES = 0x03


class SpiBitOrder(IntEnum):
    SPI_BIT_ORDER_MSB = 0x00
    SPI_BIT_ORDER_LSB = 0x01


class SpiMode(IntEnum):
    SPI_MODE_0 = 0x00
    SPI_MODE_1 = 0x01
    SPI_MODE_2 = 0x02
    SPI_MODE_3 = 0x03


class SpiDataWidth(IntEnum):
    SPI_DATA_WIDTH_8_BITS = 0x00
    SPI_DATA_WIDTH_16_BITS = 0x01


class SpiChipSelect(IntEnum):
    SPI_CHIP_SELECT_0 = 0x00
    SPI_CHIP_SELECT_1 = 0x01
    SPI_CHIP_SELECT_2 = 0x02
    SPI_CHIP_SELECT_3 = 0x03


class SpiChipSelectPolarity(IntEnum):
    SPI_CS_ACTIVE_LOW = 0x00
    SPI_CS_ACTIVE_HIGH = 0x01


class _CI3cTargetConfigurationBits(Structure):
    _fields_ = [
        ("targetType", c_uint8, 1),
        ("IBIRequest", c_uint8, 1),
        ("CRRequest", c_uint8, 1),
        ("daaUseSETDASA", c_uint8, 1),
        ("daaUseSETAASA", c_uint8, 1),
        ("daaUseENTDAA", c_uint8, 1),
        ("ibiTimestampEnable", c_uint8, 1),
        ("pendingReadCapability", c_uint8, 1),
    ]


class _CI3cTargetConfiguration(Union):
    _fields_ = [
        ("byte", c_uint8),
        ("bits", _CI3cTargetConfigurationBits),
    ]


class _CI3cBcrBits(Structure):
    _fields_ = [
        ("maxDataSpeedLimit", c_uint8, 1),
        ("ibiRequestCapable", c_uint8, 1),
        ("ibiPayload", c_uint8, 1),
        ("offlineCapable", c_uint8, 1),
        ("virtualTargetSupport", c_uint8, 1),
        ("advancedCapabilities", c_uint8, 1),
        ("deviceRole", c_uint8, 2),
    ]


class _CI3cBcr(Union):
    _fields_ = [
        ("byte", c_uint8),
        ("bits", _CI3cBcrBits),
    ]


class _CI3cTargetInfo(Structure):
    _fields_ = [
        ("staticAddress", c_uint8),
        ("dynamicAddress", c_uint8),
        ("pid", c_uint8 * 6),
        ("bcr", _CI3cBcr),
        ("dcr", c_uint8),
        ("mwl", c_uint16),
        ("mrl", c_uint16),
        ("maxIbiPayloadLength", c_uint8),
        ("configuration", _CI3cTargetConfiguration),
    ]


class _CNotification(Structure):
    _fields_ = [
        ("type", c_int),
        ("payload", POINTER(c_uint8)),
        ("payloadLength", c_size_t),
    ]


class _CI3cHotJoinNotification(Structure):
    _fields_ = [
        ("pid", c_uint8 * 6),
        ("bcr", c_uint8),
        ("dcr", c_uint8),
        ("dynamicAddress", c_uint8),
    ]


class _CI3cIbiNotification(Structure):
    _fields_ = [
        ("address", c_uint8),
        ("payload", POINTER(c_uint8)),
        ("payloadLength", c_size_t),
    ]


class _CUartRxNotification(Structure):
    _fields_ = [
        ("data", POINTER(c_uint8)),
        ("dataLength", c_size_t),
    ]


class _CGpioInterruptNotification(Structure):
    _fields_ = [
        ("pinNumber", c_uint8),
    ]


class _CDeviceInfo(Structure):
    _fields_ = [
        ("manufacturer", POINTER(c_char)),
        ("manufacturerLength", c_size_t),
        ("productName", POINTER(c_char)),
        ("productNameLength", c_size_t),
        ("serialNumber", POINTER(c_char)),
        ("serialNumberLength", c_size_t),
        ("fwVersion", POINTER(c_char)),
        ("fwVersionLength", c_size_t),
        ("hwVersion", POINTER(c_char)),
        ("hwVersionLength", c_size_t),
    ]


@dataclass(frozen=True)
class DeviceInfo:
    manufacturer: str
    product_name: str
    serial_number: str
    fw_version: str
    hw_version: str

    @classmethod
    def from_c(cls, value: _CDeviceInfo) -> "DeviceInfo":
        return cls(
            manufacturer=string_at(value.manufacturer, value.manufacturerLength).decode("utf-8", "ignore"),
            product_name=string_at(value.productName, value.productNameLength).decode("utf-8", "ignore"),
            serial_number=string_at(value.serialNumber, value.serialNumberLength).decode("utf-8", "ignore"),
            fw_version=string_at(value.fwVersion, value.fwVersionLength).decode("utf-8", "ignore"),
            hw_version=string_at(value.hwVersion, value.hwVersionLength).decode("utf-8", "ignore"),
        )


@dataclass(frozen=True)
class I3cTargetConfiguration:
    target_type: int = 0
    ibi_request: bool = False
    cr_request: bool = False
    daa_use_setdasa: bool = False
    daa_use_setaasa: bool = False
    daa_use_entdaa: bool = False
    ibi_timestamp_enable: bool = False
    pending_read_capability: bool = False

    def to_c(self) -> _CI3cTargetConfiguration:
        value = _CI3cTargetConfiguration()
        value.bits.targetType = self.target_type & 0x01
        value.bits.IBIRequest = int(self.ibi_request)
        value.bits.CRRequest = int(self.cr_request)
        value.bits.daaUseSETDASA = int(self.daa_use_setdasa)
        value.bits.daaUseSETAASA = int(self.daa_use_setaasa)
        value.bits.daaUseENTDAA = int(self.daa_use_entdaa)
        value.bits.ibiTimestampEnable = int(self.ibi_timestamp_enable)
        value.bits.pendingReadCapability = int(self.pending_read_capability)
        return value

    @classmethod
    def from_c(cls, value: _CI3cTargetConfiguration) -> "I3cTargetConfiguration":
        return cls(
            target_type=int(value.bits.targetType),
            ibi_request=bool(value.bits.IBIRequest),
            cr_request=bool(value.bits.CRRequest),
            daa_use_setdasa=bool(value.bits.daaUseSETDASA),
            daa_use_setaasa=bool(value.bits.daaUseSETAASA),
            daa_use_entdaa=bool(value.bits.daaUseENTDAA),
            ibi_timestamp_enable=bool(value.bits.ibiTimestampEnable),
            pending_read_capability=bool(value.bits.pendingReadCapability),
        )


@dataclass(frozen=True)
class I3cBcr:
    byte: int
    max_data_speed_limit: bool
    ibi_request_capable: bool
    ibi_payload: bool
    offline_capable: bool
    virtual_target_support: bool
    advanced_capabilities: bool
    device_role: int

    @classmethod
    def from_byte(cls, byte: int) -> "I3cBcr":
        value = _CI3cBcr()
        value.byte = byte
        return cls.from_c(value)

    @classmethod
    def from_c(cls, value: _CI3cBcr) -> "I3cBcr":
        return cls(
            byte=int(value.byte),
            max_data_speed_limit=bool(value.bits.maxDataSpeedLimit),
            ibi_request_capable=bool(value.bits.ibiRequestCapable),
            ibi_payload=bool(value.bits.ibiPayload),
            offline_capable=bool(value.bits.offlineCapable),
            virtual_target_support=bool(value.bits.virtualTargetSupport),
            advanced_capabilities=bool(value.bits.advancedCapabilities),
            device_role=int(value.bits.deviceRole),
        )


@dataclass(frozen=True)
class I3cTargetInfo:
    static_address: int
    dynamic_address: int
    pid: list[int]
    bcr: I3cBcr
    dcr: int
    mwl: int
    mrl: int
    max_ibi_payload_length: int
    configuration: I3cTargetConfiguration

    def to_c(self) -> _CI3cTargetInfo:
        value = _CI3cTargetInfo()
        value.staticAddress = self.static_address
        value.dynamicAddress = self.dynamic_address
        value.pid[:] = self.pid[:6]
        value.bcr.byte = self.bcr.byte
        value.dcr = self.dcr
        value.mwl = self.mwl
        value.mrl = self.mrl
        value.maxIbiPayloadLength = self.max_ibi_payload_length
        value.configuration = self.configuration.to_c()
        return value

    @classmethod
    def from_c(cls, value: _CI3cTargetInfo) -> "I3cTargetInfo":
        return cls(
            static_address=int(value.staticAddress),
            dynamic_address=int(value.dynamicAddress),
            pid=[int(byte) for byte in value.pid],
            bcr=I3cBcr.from_c(value.bcr),
            dcr=int(value.dcr),
            mwl=int(value.mwl),
            mrl=int(value.mrl),
            max_ibi_payload_length=int(value.maxIbiPayloadLength),
            configuration=I3cTargetConfiguration.from_c(value.configuration),
        )


@dataclass(frozen=True)
class I3cHotJoinNotification:
    pid: list[int]
    bcr: int
    dcr: int
    dynamic_address: int


@dataclass(frozen=True)
class I3cIbiNotification:
    address: int
    payload: list[int]


@dataclass(frozen=True)
class GpioInterruptNotification:
    pin_number: int


@dataclass(frozen=True)
class UartRxNotification:
    data: list[int]


@dataclass(frozen=True)
class Notification:
    type: NotificationType
    raw_payload: list[int]
    i3c_hot_join: Optional[I3cHotJoinNotification] = None
    i3c_ibi: Optional[I3cIbiNotification] = None
    gpio: Optional[GpioInterruptNotification] = None
    uart_rx: Optional[UartRxNotification] = None


@dataclass(frozen=True)
class I2cScanResult:
    addresses_7bit: list[int]
    addresses_10bit: list[int]


@dataclass(frozen=True)
class I3cGetMrlResult:
    max_read_length: int
    ibi_length: Optional[int]

_EXCLUDED_EXPORTS = {
    "POINTER",
    "Structure",
    "Union",
    "c_char",
    "c_int",
    "c_size_t",
    "c_uint8",
    "c_uint16",
    "string_at",
    "dataclass",
    "IntEnum",
    "IntFlag",
    "Optional",
}
__all__ = [name for name in globals() if not name.startswith("__") and name not in _EXCLUDED_EXPORTS]
