from __future__ import annotations

"""Reference Python binding for the CosmicSDK C API.

This module is intentionally a thin, explicit ``ctypes`` integration example.
It is not intended to be a production-grade Python package. The goal is to
show how the CosmicSDK shared library can be consumed from Python while
providing a small amount of Python-friendly structure around the C API.
"""

import ctypes
import os
import sys
from ctypes import CFUNCTYPE, POINTER, byref, c_bool, c_char_p, c_int, c_size_t, c_uint8, c_uint16, c_uint32, c_void_p
from pathlib import Path
from typing import Callable, Optional, Sequence

from .datatypes import *
from .result_codes import SUCCESS


class CosmicPySDK:
    """Thin Python wrapper over the blocking CosmicSDK C API."""

    CosmicSDK_Handle = c_void_p
    _NOTIFICATION_CALLBACK = CFUNCTYPE(None, POINTER(_CNotification))

    def __init__(self, lib_path: Optional[str] = None, timeout_ms: int = 3000, is_blocking: bool = True):
        """Create a Python wrapper around the CosmicSDK shared library."""
        del is_blocking

        self._lib_path = self._resolve_library_path(lib_path)
        self.lib = ctypes.CDLL(str(self._lib_path))
        self._configure_c_api()
        self.driver = self.lib.CosmicSDK_Create(timeout_ms)
        if not self.driver:
            raise RuntimeError("CosmicSDK_Create returned a null handle.")

        self._notification_handler: Optional[Callable[[Notification], None]] = None
        self._notification_callback = None

    def __enter__(self) -> "CosmicPySDK":
        return self

    def __exit__(self, exc_type, exc, tb) -> None:
        self.close()

    def __del__(self) -> None:
        driver = getattr(self, "driver", None)
        lib = getattr(self, "lib", None)
        if driver and lib:
            try:
                lib.CosmicSDK_Destroy(driver)
            except Exception:
                pass
            finally:
                self.driver = None

    @staticmethod
    def _resolve_library_path(lib_path: Optional[str]) -> Path:
        raw_path = lib_path or os.getenv("COSMICSDK_LIB_PATH")
        if not raw_path:
            raise EnvironmentError(
                "COSMICSDK_LIB_PATH is not set. Set it to the directory containing the CosmicSDK "
                "shared library or pass lib_path explicitly."
            )

        candidate = Path(raw_path).expanduser()
        if candidate.is_dir():
            if sys.platform == "win32":
                candidate = candidate / "cosmicsdk.dll"
            elif sys.platform == "darwin":
                candidate = candidate / "libcosmicsdk.dylib"
            else:
                candidate = candidate / "libcosmicsdk.so"

        if not candidate.exists():
            raise FileNotFoundError(f"Could not find the CosmicSDK shared library at '{candidate}'.")
        return candidate

    def _configure_c_api(self) -> None:
        self.lib.CosmicSDK_Create.argtypes = [c_int]
        self.lib.CosmicSDK_Create.restype = self.CosmicSDK_Handle
        self.lib.CosmicSDK_Destroy.argtypes = [self.CosmicSDK_Handle]
        self.lib.CosmicSDK_Destroy.restype = None

        self.lib.CosmicSDK_onNotification.argtypes = [self.CosmicSDK_Handle, self._NOTIFICATION_CALLBACK]
        self.lib.CosmicSDK_onNotification.restype = None
        self.lib.CosmicSDK_notificationAsI3cHotJoin.argtypes = [POINTER(_CNotification), POINTER(_CI3cHotJoinNotification)]
        self.lib.CosmicSDK_notificationAsI3cHotJoin.restype = c_bool
        self.lib.CosmicSDK_notificationAsI3cIbi.argtypes = [POINTER(_CNotification), POINTER(_CI3cIbiNotification)]
        self.lib.CosmicSDK_notificationAsI3cIbi.restype = c_bool
        self.lib.CosmicSDK_notificationAsGpio.argtypes = [POINTER(_CNotification), POINTER(_CGpioInterruptNotification)]
        self.lib.CosmicSDK_notificationAsGpio.restype = c_bool
        self.lib.CosmicSDK_notificationAsUartRx.argtypes = [POINTER(_CNotification), POINTER(_CUartRxNotification)]
        self.lib.CosmicSDK_notificationAsUartRx.restype = c_bool

        self.lib.CosmicSDK_Connect.argtypes = [self.CosmicSDK_Handle, c_char_p]
        self.lib.CosmicSDK_Connect.restype = c_bool
        self.lib.CosmicSDK_ConnectWithSerialNumber.argtypes = [self.CosmicSDK_Handle, c_char_p]
        self.lib.CosmicSDK_ConnectWithSerialNumber.restype = c_bool
        self.lib.CosmicSDK_Disconnect.argtypes = [self.CosmicSDK_Handle]
        self.lib.CosmicSDK_Disconnect.restype = None
        self.lib.CosmicSDK_GetDeviceInfo.argtypes = [self.CosmicSDK_Handle, POINTER(_CDeviceInfo)]
        self.lib.CosmicSDK_GetDeviceInfo.restype = c_uint16
        self.lib.CosmicSDK_EnterBootMode.argtypes = [self.CosmicSDK_Handle]
        self.lib.CosmicSDK_EnterBootMode.restype = c_uint16

        self.lib.CosmicSDK_I2cSetBusVoltage.argtypes = [self.CosmicSDK_Handle, c_uint16, c_bool]
        self.lib.CosmicSDK_I2cSetBusVoltage.restype = c_uint16
        self.lib.CosmicSDK_I2cControllerInit.argtypes = [self.CosmicSDK_Handle, c_uint8, c_uint32, c_uint8]
        self.lib.CosmicSDK_I2cControllerInit.restype = c_uint16
        self.lib.CosmicSDK_I2cSetParameters.argtypes = [self.CosmicSDK_Handle, c_uint8, c_uint32, c_uint8]
        self.lib.CosmicSDK_I2cSetParameters.restype = c_uint16
        self.lib.CosmicSDK_I2cSetPullUpResistors.argtypes = [self.CosmicSDK_Handle, c_uint8, c_uint8]
        self.lib.CosmicSDK_I2cSetPullUpResistors.restype = c_uint16
        self.lib.CosmicSDK_I2cWrite.argtypes = [
            self.CosmicSDK_Handle,
            c_uint8,
            c_uint8,
            POINTER(c_uint8),
            c_uint8,
            POINTER(c_uint8),
            c_uint32,
            c_bool,
        ]
        self.lib.CosmicSDK_I2cWrite.restype = c_uint16
        self.lib.CosmicSDK_I2cRead.argtypes = [
            self.CosmicSDK_Handle,
            c_uint8,
            c_uint8,
            POINTER(c_uint8),
            c_uint8,
            c_uint16,
            POINTER(c_uint8),
            c_bool,
        ]
        self.lib.CosmicSDK_I2cRead.restype = c_uint16
        self.lib.CosmicSDK_I2cScanBus.argtypes = [
            self.CosmicSDK_Handle,
            POINTER(c_uint8),
            c_uint8,
            POINTER(c_uint8),
            POINTER(c_uint8),
            c_uint8,
            c_bool,
        ]
        self.lib.CosmicSDK_I2cScanBus.restype = c_uint16

        self.lib.CosmicSDK_I3cSetBusVoltage.argtypes = [self.CosmicSDK_Handle, c_uint16]
        self.lib.CosmicSDK_I3cSetBusVoltage.restype = c_uint16
        self.lib.CosmicSDK_I3cControllerInit.argtypes = [self.CosmicSDK_Handle, c_int, c_int, c_int, c_int]
        self.lib.CosmicSDK_I3cControllerInit.restype = c_uint16
        self.lib.CosmicSDK_I3cSetParameters.argtypes = [self.CosmicSDK_Handle, c_int, c_int, c_int, c_int]
        self.lib.CosmicSDK_I3cSetParameters.restype = c_uint16
        self.lib.CosmicSDK_I3cInitBus.argtypes = [self.CosmicSDK_Handle, POINTER(_CI3cTargetInfo), c_uint8]
        self.lib.CosmicSDK_I3cInitBus.restype = c_uint16
        self.lib.CosmicSDK_I3cResetBus.argtypes = [self.CosmicSDK_Handle]
        self.lib.CosmicSDK_I3cResetBus.restype = c_uint16
        self.lib.CosmicSDK_I3cGetDeviceTable.argtypes = [
            self.CosmicSDK_Handle,
            POINTER(_CI3cTargetInfo),
            c_uint8,
            POINTER(c_uint8),
        ]
        self.lib.CosmicSDK_I3cGetDeviceTable.restype = c_uint16
        self.lib.CosmicSDK_I3cSetTargetDeviceConfiguration.argtypes = [self.CosmicSDK_Handle, c_uint8, _CI3cTargetConfiguration]
        self.lib.CosmicSDK_I3cSetTargetDeviceConfiguration.restype = c_uint16
        self.lib.CosmicSDK_I3cLegacyI2CWrite.argtypes = [
            self.CosmicSDK_Handle,
            c_uint8,
            POINTER(c_uint8),
            c_uint8,
            POINTER(c_uint8),
            c_uint16,
            c_int,
        ]
        self.lib.CosmicSDK_I3cLegacyI2CWrite.restype = c_uint16
        self.lib.CosmicSDK_I3cLegacyI2CRead.argtypes = [
            self.CosmicSDK_Handle,
            c_uint8,
            POINTER(c_uint8),
            c_uint8,
            c_uint16,
            POINTER(c_uint8),
            c_int,
        ]
        self.lib.CosmicSDK_I3cLegacyI2CRead.restype = c_uint16
        self.lib.CosmicSDK_I3cSdrWrite.argtypes = [
            self.CosmicSDK_Handle,
            c_uint8,
            POINTER(c_uint8),
            c_uint8,
            POINTER(c_uint8),
            c_uint16,
            c_int,
        ]
        self.lib.CosmicSDK_I3cSdrWrite.restype = c_uint16
        self.lib.CosmicSDK_I3cSdrRead.argtypes = [
            self.CosmicSDK_Handle,
            c_uint8,
            POINTER(c_uint8),
            c_uint8,
            c_uint16,
            POINTER(c_uint8),
            POINTER(c_uint16),
            c_int,
        ]
        self.lib.CosmicSDK_I3cSdrRead.restype = c_uint16
        self.lib.CosmicSDK_I3cHdrDdrWrite.argtypes = [self.CosmicSDK_Handle, c_uint8, c_uint8, POINTER(c_uint8), c_uint16]
        self.lib.CosmicSDK_I3cHdrDdrWrite.restype = c_uint16
        self.lib.CosmicSDK_I3cHdrDdrRead.argtypes = [
            self.CosmicSDK_Handle,
            c_uint8,
            c_uint8,
            c_uint16,
            POINTER(c_uint16),
            POINTER(c_uint8),
        ]
        self.lib.CosmicSDK_I3cHdrDdrRead.restype = c_uint16
        self.lib.CosmicSDK_I3cTargetResetPattern.argtypes = [self.CosmicSDK_Handle]
        self.lib.CosmicSDK_I3cTargetResetPattern.restype = c_uint16
        self.lib.CosmicSDK_I3cHdrExitPattern.argtypes = [self.CosmicSDK_Handle]
        self.lib.CosmicSDK_I3cHdrExitPattern.restype = c_uint16

        self.lib.CosmicSDK_I3cCccRSTDAA.argtypes = [self.CosmicSDK_Handle]
        self.lib.CosmicSDK_I3cCccRSTDAA.restype = c_uint16
        self.lib.CosmicSDK_I3cCccENTDAA.argtypes = [self.CosmicSDK_Handle]
        self.lib.CosmicSDK_I3cCccENTDAA.restype = c_uint16
        self.lib.CosmicSDK_I3cCccSETDASA.argtypes = [self.CosmicSDK_Handle, c_uint8, c_uint8]
        self.lib.CosmicSDK_I3cCccSETDASA.restype = c_uint16
        self.lib.CosmicSDK_I3cCccSETAASA.argtypes = [self.CosmicSDK_Handle, POINTER(c_uint8), c_size_t]
        self.lib.CosmicSDK_I3cCccSETAASA.restype = c_uint16
        self.lib.CosmicSDK_I3cCccSETNEWDA.argtypes = [self.CosmicSDK_Handle, c_uint8, c_uint8]
        self.lib.CosmicSDK_I3cCccSETNEWDA.restype = c_uint16
        self.lib.CosmicSDK_I3cCccGETPID.argtypes = [self.CosmicSDK_Handle, c_uint8, POINTER(c_uint8)]
        self.lib.CosmicSDK_I3cCccGETPID.restype = c_uint16
        self.lib.CosmicSDK_I3cCccGETBCR.argtypes = [self.CosmicSDK_Handle, c_uint8, POINTER(c_uint8)]
        self.lib.CosmicSDK_I3cCccGETBCR.restype = c_uint16
        self.lib.CosmicSDK_I3cCccGETDCR.argtypes = [self.CosmicSDK_Handle, c_uint8, POINTER(c_uint8)]
        self.lib.CosmicSDK_I3cCccGETDCR.restype = c_uint16
        self.lib.CosmicSDK_I3cCccGETSTATUS.argtypes = [self.CosmicSDK_Handle, c_uint8, POINTER(c_uint8), POINTER(c_uint8), c_bool, c_uint8]
        self.lib.CosmicSDK_I3cCccGETSTATUS.restype = c_uint16
        self.lib.CosmicSDK_I3cCccGETCAPS.argtypes = [self.CosmicSDK_Handle, c_uint8, POINTER(c_uint8), POINTER(c_uint8), c_bool, c_uint8]
        self.lib.CosmicSDK_I3cCccGETCAPS.restype = c_uint16
        self.lib.CosmicSDK_I3cCccRSTACTBroadcast.argtypes = [self.CosmicSDK_Handle, c_int]
        self.lib.CosmicSDK_I3cCccRSTACTBroadcast.restype = c_uint16
        self.lib.CosmicSDK_I3cCccRSTACTDirect.argtypes = [self.CosmicSDK_Handle, c_uint8, c_int, c_int, POINTER(c_uint8)]
        self.lib.CosmicSDK_I3cCccRSTACTDirect.restype = c_uint16
        self.lib.CosmicSDK_I3cCccENECBroadcast.argtypes = [self.CosmicSDK_Handle, c_uint8]
        self.lib.CosmicSDK_I3cCccENECBroadcast.restype = c_uint16
        self.lib.CosmicSDK_I3cCccENECDirect.argtypes = [self.CosmicSDK_Handle, c_uint8, c_uint8]
        self.lib.CosmicSDK_I3cCccENECDirect.restype = c_uint16
        self.lib.CosmicSDK_I3cCccDISECBroadcast.argtypes = [self.CosmicSDK_Handle, c_uint8]
        self.lib.CosmicSDK_I3cCccDISECBroadcast.restype = c_uint16
        self.lib.CosmicSDK_I3cCccDISECDirect.argtypes = [self.CosmicSDK_Handle, c_uint8, c_uint8]
        self.lib.CosmicSDK_I3cCccDISECDirect.restype = c_uint16
        self.lib.CosmicSDK_I3cCccGETMWL.argtypes = [self.CosmicSDK_Handle, c_uint8, POINTER(c_uint16)]
        self.lib.CosmicSDK_I3cCccGETMWL.restype = c_uint16
        self.lib.CosmicSDK_I3cCccSETMWLBroadcast.argtypes = [self.CosmicSDK_Handle, c_uint16]
        self.lib.CosmicSDK_I3cCccSETMWLBroadcast.restype = c_uint16
        self.lib.CosmicSDK_I3cCccSETMWLDirect.argtypes = [self.CosmicSDK_Handle, c_uint8, c_uint16]
        self.lib.CosmicSDK_I3cCccSETMWLDirect.restype = c_uint16
        self.lib.CosmicSDK_I3cCccGETMRL.argtypes = [self.CosmicSDK_Handle, c_uint8, POINTER(c_uint16), POINTER(c_uint8), POINTER(c_bool)]
        self.lib.CosmicSDK_I3cCccGETMRL.restype = c_uint16
        self.lib.CosmicSDK_I3cCccSETMRLBroadcast.argtypes = [self.CosmicSDK_Handle, c_uint16, c_bool, c_uint8]
        self.lib.CosmicSDK_I3cCccSETMRLBroadcast.restype = c_uint16
        self.lib.CosmicSDK_I3cCccSETMRLDirect.argtypes = [self.CosmicSDK_Handle, c_uint8, c_uint16, c_bool, c_uint8]
        self.lib.CosmicSDK_I3cCccSETMRLDirect.restype = c_uint16

        self.lib.CosmicSDK_UartSetBusVoltage.argtypes = [self.CosmicSDK_Handle, c_uint16, c_bool]
        self.lib.CosmicSDK_UartSetBusVoltage.restype = c_uint16
        self.lib.CosmicSDK_UartInit.argtypes = [self.CosmicSDK_Handle, c_int, c_int, c_int, c_int, c_int]
        self.lib.CosmicSDK_UartInit.restype = c_uint16
        self.lib.CosmicSDK_UartSetParameters.argtypes = [self.CosmicSDK_Handle, c_int, c_int, c_int, c_int, c_int]
        self.lib.CosmicSDK_UartSetParameters.restype = c_uint16
        self.lib.CosmicSDK_UartSendMessage.argtypes = [self.CosmicSDK_Handle, POINTER(c_uint8), c_uint16]
        self.lib.CosmicSDK_UartSendMessage.restype = c_uint16

        self.lib.CosmicSDK_GpioSetVoltage.argtypes = [self.CosmicSDK_Handle, c_uint16, c_bool]
        self.lib.CosmicSDK_GpioSetVoltage.restype = c_uint16
        self.lib.CosmicSDK_GpioConfigurePin.argtypes = [self.CosmicSDK_Handle, c_int, c_int, c_bool, c_int]
        self.lib.CosmicSDK_GpioConfigurePin.restype = c_uint16
        self.lib.CosmicSDK_GpioDigitalWrite.argtypes = [self.CosmicSDK_Handle, c_int, c_int]
        self.lib.CosmicSDK_GpioDigitalWrite.restype = c_uint16
        self.lib.CosmicSDK_GpioDigitalRead.argtypes = [self.CosmicSDK_Handle, c_int, POINTER(c_int)]
        self.lib.CosmicSDK_GpioDigitalRead.restype = c_uint16
        self.lib.CosmicSDK_GpioConfigureInterrupt.argtypes = [self.CosmicSDK_Handle, c_int, c_int]
        self.lib.CosmicSDK_GpioConfigureInterrupt.restype = c_uint16
        self.lib.CosmicSDK_GpioDisableInterrupt.argtypes = [self.CosmicSDK_Handle, c_int]
        self.lib.CosmicSDK_GpioDisableInterrupt.restype = c_uint16

        self.lib.CosmicSDK_SpiSetBusVoltage.argtypes = [self.CosmicSDK_Handle, c_uint16, c_bool]
        self.lib.CosmicSDK_SpiSetBusVoltage.restype = c_uint16
        self.lib.CosmicSDK_SpiControllerInit.argtypes = [self.CosmicSDK_Handle, c_int, c_int, c_int, c_int, c_int, c_uint32]
        self.lib.CosmicSDK_SpiControllerInit.restype = c_uint16
        self.lib.CosmicSDK_SpiSetParameters.argtypes = [self.CosmicSDK_Handle, c_int, c_int, c_int, c_int, c_int, c_uint32]
        self.lib.CosmicSDK_SpiSetParameters.restype = c_uint16
        self.lib.CosmicSDK_SpiTransfer.argtypes = [self.CosmicSDK_Handle, POINTER(c_uint8), c_uint16, POINTER(c_uint8), c_uint16]
        self.lib.CosmicSDK_SpiTransfer.restype = c_uint16

    @staticmethod
    def _bytes_to_list(pointer: POINTER(c_uint8), length: int) -> list[int]:
        if not pointer or length <= 0:
            return []
        return [int(pointer[index]) for index in range(length)]

    @staticmethod
    def _make_byte_array(data: Optional[Sequence[int]]) -> tuple[Optional[ctypes.Array[c_uint8]], int]:
        items = list(data or [])
        if not items:
            return None, 0
        return (c_uint8 * len(items))(*items), len(items)

    @staticmethod
    def _enum_value(value: IntEnum | IntFlag | int) -> int:
        return int(value)

    @staticmethod
    def _encode_text(value: Optional[str]) -> Optional[bytes]:
        if value is None:
            return None
        return value.encode("utf-8")

    def _build_notification(self, notification_ptr: POINTER(_CNotification)) -> Notification:
        raw = notification_ptr.contents
        notification_type = NotificationType(raw.type) if raw.type in set(item.value for item in NotificationType) else NotificationType.NOTIF_UNKNOWN
        payload = self._bytes_to_list(raw.payload, int(raw.payloadLength))
        parsed_hot_join = None
        parsed_ibi = None
        parsed_gpio = None
        parsed_uart = None

        if notification_type == NotificationType.NOTIF_I3C_HJ:
            parsed = _CI3cHotJoinNotification()
            if self.lib.CosmicSDK_notificationAsI3cHotJoin(notification_ptr, byref(parsed)):
                parsed_hot_join = I3cHotJoinNotification(
                    pid=[int(byte) for byte in parsed.pid],
                    bcr=int(parsed.bcr),
                    dcr=int(parsed.dcr),
                    dynamic_address=int(parsed.dynamicAddress),
                )
        elif notification_type == NotificationType.NOTIF_I3C_IBI:
            parsed = _CI3cIbiNotification()
            if self.lib.CosmicSDK_notificationAsI3cIbi(notification_ptr, byref(parsed)):
                parsed_ibi = I3cIbiNotification(
                    address=int(parsed.address),
                    payload=self._bytes_to_list(parsed.payload, int(parsed.payloadLength)),
                )
        elif notification_type == NotificationType.NOTIF_GPIO:
            parsed = _CGpioInterruptNotification()
            if self.lib.CosmicSDK_notificationAsGpio(notification_ptr, byref(parsed)):
                parsed_gpio = GpioInterruptNotification(pin_number=int(parsed.pinNumber))
        elif notification_type == NotificationType.NOTIF_UART:
            parsed = _CUartRxNotification()
            if self.lib.CosmicSDK_notificationAsUartRx(notification_ptr, byref(parsed)):
                parsed_uart = UartRxNotification(data=self._bytes_to_list(parsed.data, int(parsed.dataLength)))

        return Notification(
            type=notification_type,
            raw_payload=payload,
            i3c_hot_join=parsed_hot_join,
            i3c_ibi=parsed_ibi,
            gpio=parsed_gpio,
            uart_rx=parsed_uart,
        )

    def on_notification(self, handler: Optional[Callable[[Notification], None]]) -> None:
        """Register a Python callback for CosmicSDK notifications."""
        self._notification_handler = handler

        def _internal_callback(notification_ptr: POINTER(_CNotification)) -> None:
            if self._notification_handler is None:
                return
            self._notification_handler(self._build_notification(notification_ptr))

        self._notification_callback = self._NOTIFICATION_CALLBACK(_internal_callback)
        self.lib.CosmicSDK_onNotification(self.driver, self._notification_callback)

    set_on_notification = on_notification

    def open(self, device_path: Optional[str] = None) -> bool:
        """Connect to the first matching device or to an explicit device path."""
        return bool(self.lib.CosmicSDK_Connect(self.driver, self._encode_text(device_path)))

    def connect_with_serial_number(self, serial_number: str) -> bool:
        """Connect to a device by serial number."""
        return bool(self.lib.CosmicSDK_ConnectWithSerialNumber(self.driver, self._encode_text(serial_number)))

    def get_device_info(self) -> tuple[int, Optional[DeviceInfo]]:
        """Return the connected device information."""
        device_info = _CDeviceInfo()
        status = int(self.lib.CosmicSDK_GetDeviceInfo(self.driver, byref(device_info)))
        if status != SUCCESS:
            return status, None
        return status, DeviceInfo.from_c(device_info)

    def enter_boot_mode(self) -> tuple[int, None]:
        """Request the adapter to enter bootloader mode."""
        return int(self.lib.CosmicSDK_EnterBootMode(self.driver)), None

    def i2c_set_bus_voltage(self, voltage_mv: int, persist: bool = False) -> tuple[int, None]:
        """Set the I2C voltage domain."""
        return int(self.lib.CosmicSDK_I2cSetBusVoltage(self.driver, voltage_mv, persist)), None

    def i2c_controller_init(self, bus: I2cBus | int, frequency_hz: int, pull_up_resistor: I2cPullUp | int) -> tuple[int, None]:
        """Initialize the I2C controller."""
        return int(self.lib.CosmicSDK_I2cControllerInit(self.driver, int(bus), frequency_hz, int(pull_up_resistor))), None

    def i2c_set_parameters(self, bus: I2cBus | int, frequency_hz: int, pull_up_resistor: I2cPullUp | int) -> tuple[int, None]:
        """Update the active I2C controller parameters."""
        return int(self.lib.CosmicSDK_I2cSetParameters(self.driver, int(bus), frequency_hz, int(pull_up_resistor))), None

    def i2c_set_pull_up_resistors(self, bus: I2cBus | int, resistance: I2cPullUp | int) -> tuple[int, None]:
        """Configure I2C pull-up resistors."""
        return int(self.lib.CosmicSDK_I2cSetPullUpResistors(self.driver, int(bus), int(resistance))), None

    def i2c_write(
        self,
        bus: I2cBus | int,
        address: int,
        subaddress: Optional[Sequence[int]] = None,
        data: Optional[Sequence[int]] = None,
        non_stop: bool = False,
    ) -> tuple[int, None]:
        """Perform an I2C write transaction."""
        subaddress_array, subaddress_length = self._make_byte_array(subaddress)
        data_array, data_length = self._make_byte_array(data)
        status = int(
            self.lib.CosmicSDK_I2cWrite(
                self.driver,
                int(bus),
                address,
                subaddress_array,
                subaddress_length,
                data_array,
                data_length,
                non_stop,
            )
        )
        return status, None

    def i2c_read(
        self,
        bus: I2cBus | int,
        address: int,
        subaddress: Optional[Sequence[int]] = None,
        read_length: int = 0,
        non_stop: bool = False,
    ) -> tuple[int, Optional[list[int]]]:
        """Perform an I2C read transaction."""
        subaddress_array, subaddress_length = self._make_byte_array(subaddress)
        out_buffer = (c_uint8 * read_length)()
        status = int(
            self.lib.CosmicSDK_I2cRead(
                self.driver,
                int(bus),
                address,
                subaddress_array,
                subaddress_length,
                read_length,
                out_buffer,
                non_stop,
            )
        )
        if status != SUCCESS:
            return status, None
        return status, [int(byte) for byte in out_buffer]

    def i2c_scan_bus(
        self,
        bus: I2cBus | int,
        buffer_length: int,
        include_10bit_addresses: bool = True,
    ) -> tuple[int, Optional[I2cScanResult]]:
        """Scan an I2C bus and return the discovered 7-bit and 10-bit addresses."""
        device_addresses = (c_uint8 * buffer_length)()
        count_7bit = c_uint8()
        count_10bit = c_uint8()
        status = int(
            self.lib.CosmicSDK_I2cScanBus(
                self.driver,
                device_addresses,
                buffer_length,
                byref(count_7bit),
                byref(count_10bit),
                int(bus),
                include_10bit_addresses,
            )
        )
        if status != SUCCESS:
            return status, None

        raw = [int(device_addresses[index]) for index in range(buffer_length)]
        seven_bit = raw[: count_7bit.value]
        ten_bit = []
        offset = count_7bit.value
        for index in range(count_10bit.value):
            ten_bit.append((raw[offset + (2 * index)] << 8) | raw[offset + (2 * index) + 1])
        return status, I2cScanResult(addresses_7bit=seven_bit, addresses_10bit=ten_bit)

    def i3c_set_bus_voltage(self, voltage_mv: int) -> tuple[int, None]:
        """Set the I3C voltage domain."""
        return int(self.lib.CosmicSDK_I3cSetBusVoltage(self.driver, voltage_mv)), None

    def i3c_controller_init(
        self,
        push_pull_frequency: I3cPushPullRate | int,
        open_drain_frequency: I3cOpenDrainTransferRate | int,
        i2c_open_drain_frequency: I2cTransferRate | int,
        drive_strength: I3cDriveStrength | int,
    ) -> tuple[int, None]:
        """Initialize the I3C controller."""
        status = int(
            self.lib.CosmicSDK_I3cControllerInit(
                self.driver,
                self._enum_value(push_pull_frequency),
                self._enum_value(open_drain_frequency),
                self._enum_value(i2c_open_drain_frequency),
                self._enum_value(drive_strength),
            )
        )
        return status, None

    def i3c_set_parameters(
        self,
        push_pull_frequency: I3cPushPullRate | int,
        open_drain_frequency: I3cOpenDrainTransferRate | int,
        i2c_open_drain_frequency: I2cTransferRate | int,
        drive_strength: I3cDriveStrength | int,
    ) -> tuple[int, None]:
        """Update I3C controller parameters."""
        status = int(
            self.lib.CosmicSDK_I3cSetParameters(
                self.driver,
                self._enum_value(push_pull_frequency),
                self._enum_value(open_drain_frequency),
                self._enum_value(i2c_open_drain_frequency),
                self._enum_value(drive_strength),
            )
        )
        return status, None

    def i3c_init_bus(self, targets: Optional[Sequence[I3cTargetInfo]] = None) -> tuple[int, None]:
        """Initialize the I3C bus, optionally with a predefined target table."""
        target_list = list(targets or [])
        if target_list:
            c_targets = (_CI3cTargetInfo * len(target_list))(*(target.to_c() for target in target_list))
            status = int(self.lib.CosmicSDK_I3cInitBus(self.driver, c_targets, len(target_list)))
        else:
            status = int(self.lib.CosmicSDK_I3cInitBus(self.driver, None, 0))
        return status, None

    def i3c_reset_bus(self) -> tuple[int, None]:
        """Reset the I3C bus and clear the target table."""
        return int(self.lib.CosmicSDK_I3cResetBus(self.driver)), None

    def i3c_get_device_table(self, max_targets: int = I3C_MAX_TARGETS) -> tuple[int, Optional[list[I3cTargetInfo]]]:
        """Return the current I3C target table stored in the adapter."""
        c_targets = (_CI3cTargetInfo * max_targets)()
        count = c_uint8()
        status = int(self.lib.CosmicSDK_I3cGetDeviceTable(self.driver, c_targets, max_targets, byref(count)))
        if status != SUCCESS:
            return status, None
        return status, [I3cTargetInfo.from_c(c_targets[index]) for index in range(count.value)]

    def i3c_set_target_device_configuration(
        self,
        address: int,
        configuration: I3cTargetConfiguration,
    ) -> tuple[int, None]:
        """Update the configuration stored for a target in the adapter device table."""
        return int(self.lib.CosmicSDK_I3cSetTargetDeviceConfiguration(self.driver, address, configuration.to_c())), None

    def i3c_legacy_i2c_write(
        self,
        address: int,
        subaddress: Optional[Sequence[int]] = None,
        data: Optional[Sequence[int]] = None,
        flags: I3cTransferFlags | int = I3cTransferFlags.I3C_XFER_NONE,
    ) -> tuple[int, None]:
        """Issue a legacy-I2C write transaction over the I3C bus."""
        subaddress_array, subaddress_length = self._make_byte_array(subaddress)
        data_array, data_length = self._make_byte_array(data)
        status = int(
            self.lib.CosmicSDK_I3cLegacyI2CWrite(
                self.driver,
                address,
                subaddress_array,
                subaddress_length,
                data_array,
                data_length,
                self._enum_value(flags),
            )
        )
        return status, None

    def i3c_legacy_i2c_read(
        self,
        address: int,
        subaddress: Optional[Sequence[int]] = None,
        read_length: int = 0,
        flags: I3cTransferFlags | int = I3cTransferFlags.I3C_XFER_NONE,
    ) -> tuple[int, Optional[list[int]]]:
        """Issue a legacy-I2C read transaction over the I3C bus."""
        subaddress_array, subaddress_length = self._make_byte_array(subaddress)
        out_buffer = (c_uint8 * read_length)()
        status = int(
            self.lib.CosmicSDK_I3cLegacyI2CRead(
                self.driver,
                address,
                subaddress_array,
                subaddress_length,
                read_length,
                out_buffer,
                self._enum_value(flags),
            )
        )
        if status != SUCCESS:
            return status, None
        return status, [int(byte) for byte in out_buffer]

    def i3c_sdr_write(
        self,
        address: int,
        subaddress: Optional[Sequence[int]] = None,
        data: Optional[Sequence[int]] = None,
        flags: I3cTransferFlags | int = I3cTransferFlags.I3C_XFER_NONE,
    ) -> tuple[int, None]:
        """Perform an I3C SDR write transfer."""
        subaddress_array, subaddress_length = self._make_byte_array(subaddress)
        data_array, data_length = self._make_byte_array(data)
        status = int(
            self.lib.CosmicSDK_I3cSdrWrite(
                self.driver,
                address,
                subaddress_array,
                subaddress_length,
                data_array,
                data_length,
                self._enum_value(flags),
            )
        )
        return status, None

    def i3c_sdr_read(
        self,
        address: int,
        subaddress: Optional[Sequence[int]] = None,
        read_length: int = 0,
        flags: I3cTransferFlags | int = I3cTransferFlags.I3C_XFER_NONE,
    ) -> tuple[int, Optional[list[int]]]:
        """Perform an I3C SDR read transfer."""
        subaddress_array, subaddress_length = self._make_byte_array(subaddress)
        out_buffer = (c_uint8 * read_length)()
        out_length = c_uint16()
        status = int(
            self.lib.CosmicSDK_I3cSdrRead(
                self.driver,
                address,
                subaddress_array,
                subaddress_length,
                read_length,
                out_buffer,
                byref(out_length),
                self._enum_value(flags),
            )
        )
        if status != SUCCESS:
            return status, None
        return status, [int(out_buffer[index]) for index in range(out_length.value)]

    def i3c_hdr_ddr_write(self, address: int, command: int, data: Optional[Sequence[int]] = None) -> tuple[int, None]:
        """Perform an I3C HDR-DDR write transfer."""
        data_array, data_length = self._make_byte_array(data)
        return int(self.lib.CosmicSDK_I3cHdrDdrWrite(self.driver, address, command, data_array, data_length)), None

    def i3c_hdr_ddr_read(self, address: int, command: int, read_length: int) -> tuple[int, Optional[list[int]]]:
        """Perform an I3C HDR-DDR read transfer."""
        out_buffer = (c_uint8 * read_length)()
        out_length = c_uint16()
        status = int(self.lib.CosmicSDK_I3cHdrDdrRead(self.driver, address, command, read_length, byref(out_length), out_buffer))
        if status != SUCCESS:
            return status, None
        return status, [int(out_buffer[index]) for index in range(out_length.value)]

    def i3c_target_reset_pattern(self) -> tuple[int, None]:
        """Emit the I3C target-reset pattern."""
        return int(self.lib.CosmicSDK_I3cTargetResetPattern(self.driver)), None

    def i3c_hdr_exit_pattern(self) -> tuple[int, None]:
        """Emit the I3C HDR-exit pattern."""
        return int(self.lib.CosmicSDK_I3cHdrExitPattern(self.driver)), None

    def i3c_ccc_rstdaa(self) -> tuple[int, None]:
        """Send the broadcast RSTDAA CCC."""
        return int(self.lib.CosmicSDK_I3cCccRSTDAA(self.driver)), None

    def i3c_ccc_entdaa(self) -> tuple[int, None]:
        """Send the broadcast ENTDAA CCC."""
        return int(self.lib.CosmicSDK_I3cCccENTDAA(self.driver)), None

    def i3c_ccc_setdasa(self, static_address: int, dynamic_address: int) -> tuple[int, None]:
        """Send the direct SETDASA CCC."""
        return int(self.lib.CosmicSDK_I3cCccSETDASA(self.driver, static_address, dynamic_address)), None

    def i3c_ccc_setaasa(self, static_addresses: Sequence[int]) -> tuple[int, None]:
        """Send the broadcast SETAASA CCC."""
        addresses_array, count = self._make_byte_array(static_addresses)
        return int(self.lib.CosmicSDK_I3cCccSETAASA(self.driver, addresses_array, count)), None

    def i3c_ccc_setnewda(self, old_dynamic_address: int, new_dynamic_address: int) -> tuple[int, None]:
        """Send the direct SETNEWDA CCC."""
        return int(self.lib.CosmicSDK_I3cCccSETNEWDA(self.driver, old_dynamic_address, new_dynamic_address)), None

    def i3c_ccc_getpid(self, address: int) -> tuple[int, Optional[list[int]]]:
        """Read the PID from an I3C target."""
        pid = (c_uint8 * 6)()
        status = int(self.lib.CosmicSDK_I3cCccGETPID(self.driver, address, pid))
        if status != SUCCESS:
            return status, None
        return status, [int(byte) for byte in pid]

    def i3c_ccc_getbcr(self, address: int) -> tuple[int, Optional[int]]:
        """Read the BCR from an I3C target."""
        value = c_uint8()
        status = int(self.lib.CosmicSDK_I3cCccGETBCR(self.driver, address, byref(value)))
        if status != SUCCESS:
            return status, None
        return status, int(value.value)

    def i3c_ccc_getdcr(self, address: int) -> tuple[int, Optional[int]]:
        """Read the DCR from an I3C target."""
        value = c_uint8()
        status = int(self.lib.CosmicSDK_I3cCccGETDCR(self.driver, address, byref(value)))
        if status != SUCCESS:
            return status, None
        return status, int(value.value)

    def i3c_ccc_getstatus(
        self,
        target_address: int,
        def_byte: Optional[int] = None,
        buffer_length: int = I3C_MAX_CCC_BUFFER_LENGTH,
    ) -> tuple[int, Optional[list[int]]]:
        """Read GETSTATUS data from an I3C target."""
        buffer = (c_uint8 * buffer_length)()
        length = c_uint8()
        status = int(
            self.lib.CosmicSDK_I3cCccGETSTATUS(
                self.driver,
                target_address,
                buffer,
                byref(length),
                def_byte is not None,
                0 if def_byte is None else def_byte,
            )
        )
        if status != SUCCESS:
            return status, None
        return status, [int(buffer[index]) for index in range(length.value)]

    def i3c_ccc_getcaps(
        self,
        target_address: int,
        def_byte: Optional[int] = None,
        buffer_length: int = I3C_MAX_CCC_BUFFER_LENGTH,
    ) -> tuple[int, Optional[list[int]]]:
        """Read GETCAPS data from an I3C target."""
        buffer = (c_uint8 * buffer_length)()
        length = c_uint8()
        status = int(
            self.lib.CosmicSDK_I3cCccGETCAPS(
                self.driver,
                target_address,
                buffer,
                byref(length),
                def_byte is not None,
                0 if def_byte is None else def_byte,
            )
        )
        if status != SUCCESS:
            return status, None
        return status, [int(buffer[index]) for index in range(length.value)]

    def i3c_ccc_rstact_broadcast(self, defining_byte: I3cTargetResetDefByte | int) -> tuple[int, None]:
        """Send the broadcast RSTACT CCC."""
        return int(self.lib.CosmicSDK_I3cCccRSTACTBroadcast(self.driver, self._enum_value(defining_byte))), None

    def i3c_ccc_rstact_direct(
        self,
        target_address: int,
        defining_byte: I3cTargetResetDefByte | int,
        direction: I3cTransferDirection | int,
    ) -> tuple[int, Optional[int]]:
        """Send the direct RSTACT CCC in read or write mode."""
        direction_value = self._enum_value(direction)
        out_value = c_uint8()
        pointer = byref(out_value) if direction_value == I3cTransferDirection.READ else None
        status = int(
            self.lib.CosmicSDK_I3cCccRSTACTDirect(
                self.driver,
                target_address,
                self._enum_value(defining_byte),
                direction_value,
                pointer,
            )
        )
        if status != SUCCESS:
            return status, None
        return status, int(out_value.value) if direction_value == I3cTransferDirection.READ else None

    def i3c_ccc_enec_broadcast(self, events: I3cEvent | int) -> tuple[int, None]:
        """Send the broadcast ENEC CCC."""
        return int(self.lib.CosmicSDK_I3cCccENECBroadcast(self.driver, int(events))), None

    def i3c_ccc_enec_direct(self, address: int, events: I3cEvent | int) -> tuple[int, None]:
        """Send the direct ENEC CCC."""
        return int(self.lib.CosmicSDK_I3cCccENECDirect(self.driver, address, int(events))), None

    def i3c_ccc_disec_broadcast(self, events: I3cEvent | int) -> tuple[int, None]:
        """Send the broadcast DISEC CCC."""
        return int(self.lib.CosmicSDK_I3cCccDISECBroadcast(self.driver, int(events))), None

    def i3c_ccc_disec_direct(self, address: int, events: I3cEvent | int) -> tuple[int, None]:
        """Send the direct DISEC CCC."""
        return int(self.lib.CosmicSDK_I3cCccDISECDirect(self.driver, address, int(events))), None

    def i3c_ccc_getmwl(self, address: int) -> tuple[int, Optional[int]]:
        """Read the maximum write length from an I3C target."""
        value = c_uint16()
        status = int(self.lib.CosmicSDK_I3cCccGETMWL(self.driver, address, byref(value)))
        if status != SUCCESS:
            return status, None
        return status, int(value.value)

    def i3c_ccc_setmwl_broadcast(self, mwl: int) -> tuple[int, None]:
        """Send the broadcast SETMWL CCC."""
        return int(self.lib.CosmicSDK_I3cCccSETMWLBroadcast(self.driver, mwl)), None

    def i3c_ccc_setmwl_direct(self, address: int, mwl: int) -> tuple[int, None]:
        """Send the direct SETMWL CCC."""
        return int(self.lib.CosmicSDK_I3cCccSETMWLDirect(self.driver, address, mwl)), None

    def i3c_ccc_getmrl(self, address: int) -> tuple[int, Optional[I3cGetMrlResult]]:
        """Read the maximum read length from an I3C target."""
        max_read_length = c_uint16()
        ibi_length = c_uint8()
        ibi_length_returned = c_bool()
        status = int(
            self.lib.CosmicSDK_I3cCccGETMRL(
                self.driver,
                address,
                byref(max_read_length),
                byref(ibi_length),
                byref(ibi_length_returned),
            )
        )
        if status != SUCCESS:
            return status, None
        return status, I3cGetMrlResult(
            max_read_length=int(max_read_length.value),
            ibi_length=int(ibi_length.value) if bool(ibi_length_returned.value) else None,
        )

    def i3c_ccc_setmrl_broadcast(self, mrl: int, ibi_length: Optional[int] = None) -> tuple[int, None]:
        """Send the broadcast SETMRL CCC."""
        status = int(
            self.lib.CosmicSDK_I3cCccSETMRLBroadcast(
                self.driver,
                mrl,
                ibi_length is not None,
                0 if ibi_length is None else ibi_length,
            )
        )
        return status, None

    def i3c_ccc_setmrl_direct(self, address: int, mrl: int, ibi_length: Optional[int] = None) -> tuple[int, None]:
        """Send the direct SETMRL CCC."""
        status = int(
            self.lib.CosmicSDK_I3cCccSETMRLDirect(
                self.driver,
                address,
                mrl,
                ibi_length is not None,
                0 if ibi_length is None else ibi_length,
            )
        )
        return status, None

    def uart_set_bus_voltage(self, voltage_mv: int, persist: bool = False) -> tuple[int, None]:
        """Set the UART voltage domain."""
        return int(self.lib.CosmicSDK_UartSetBusVoltage(self.driver, voltage_mv, persist)), None

    def uart_init(
        self,
        baudrate: UartBaudRate | int,
        hardware_handshake: UartHwHandshake | int,
        parity_mode: UartParity | int,
        data_size: UartDataSize | int,
        stop_bit: UartStopBit | int,
    ) -> tuple[int, None]:
        """Initialize the UART peripheral."""
        status = int(
            self.lib.CosmicSDK_UartInit(
                self.driver,
                self._enum_value(baudrate),
                self._enum_value(hardware_handshake),
                self._enum_value(parity_mode),
                self._enum_value(data_size),
                self._enum_value(stop_bit),
            )
        )
        return status, None

    def uart_set_parameters(
        self,
        baudrate: UartBaudRate | int,
        hardware_handshake: UartHwHandshake | int,
        parity_mode: UartParity | int,
        data_size: UartDataSize | int,
        stop_bit: UartStopBit | int,
    ) -> tuple[int, None]:
        """Update UART parameters."""
        status = int(
            self.lib.CosmicSDK_UartSetParameters(
                self.driver,
                self._enum_value(baudrate),
                self._enum_value(hardware_handshake),
                self._enum_value(parity_mode),
                self._enum_value(data_size),
                self._enum_value(stop_bit),
            )
        )
        return status, None

    def uart_send_message(self, data: Sequence[int]) -> tuple[int, None]:
        """Transmit a UART message."""
        data_array, data_length = self._make_byte_array(data)
        return int(self.lib.CosmicSDK_UartSendMessage(self.driver, data_array, data_length)), None

    def gpio_set_voltage(self, voltage_mv: int, persist: bool = False) -> tuple[int, None]:
        """Set the GPIO voltage domain."""
        return int(self.lib.CosmicSDK_GpioSetVoltage(self.driver, voltage_mv, persist)), None

    def gpio_configure_pin(
        self,
        pin_number: GpioPinNumber | int,
        functionality: GpioFunctionality | int,
        initial_output_logic_level: Optional[GpioLogicLevel | int] = None,
    ) -> tuple[int, None]:
        """Configure a GPIO pin."""
        set_initial = initial_output_logic_level is not None
        logic_level = GpioLogicLevel.GPIO_LOW if initial_output_logic_level is None else initial_output_logic_level
        status = int(
            self.lib.CosmicSDK_GpioConfigurePin(
                self.driver,
                self._enum_value(pin_number),
                self._enum_value(functionality),
                set_initial,
                self._enum_value(logic_level),
            )
        )
        return status, None

    def gpio_digital_write(self, pin_number: GpioPinNumber | int, logic_level: GpioLogicLevel | int) -> tuple[int, None]:
        """Write a digital level to a GPIO output."""
        return int(self.lib.CosmicSDK_GpioDigitalWrite(self.driver, self._enum_value(pin_number), self._enum_value(logic_level))), None

    def gpio_digital_read(self, pin_number: GpioPinNumber | int) -> tuple[int, Optional[GpioLogicLevel]]:
        """Read a digital level from a GPIO input."""
        logic_level = c_int()
        status = int(self.lib.CosmicSDK_GpioDigitalRead(self.driver, self._enum_value(pin_number), byref(logic_level)))
        if status != SUCCESS:
            return status, None
        return status, GpioLogicLevel(logic_level.value)

    def gpio_configure_interrupt(self, pin_number: GpioPinNumber | int, trigger: GpioTriggerType | int) -> tuple[int, None]:
        """Enable interrupts for a GPIO pin."""
        return int(self.lib.CosmicSDK_GpioConfigureInterrupt(self.driver, self._enum_value(pin_number), self._enum_value(trigger))), None

    def gpio_disable_interrupt(self, pin_number: GpioPinNumber | int) -> tuple[int, None]:
        """Disable interrupts for a GPIO pin."""
        return int(self.lib.CosmicSDK_GpioDisableInterrupt(self.driver, self._enum_value(pin_number))), None

    def spi_set_bus_voltage(self, voltage_mv: int, persist: bool = False) -> tuple[int, None]:
        """Set the SPI voltage domain."""
        return int(self.lib.CosmicSDK_SpiSetBusVoltage(self.driver, voltage_mv, persist)), None

    def spi_controller_init(
        self,
        bit_order: SpiBitOrder | int,
        mode: SpiMode | int,
        data_width: SpiDataWidth | int,
        chip_select: SpiChipSelect | int,
        chip_select_polarity: SpiChipSelectPolarity | int,
        frequency_hz: int,
    ) -> tuple[int, None]:
        """Initialize the SPI controller."""
        status = int(
            self.lib.CosmicSDK_SpiControllerInit(
                self.driver,
                self._enum_value(bit_order),
                self._enum_value(mode),
                self._enum_value(data_width),
                self._enum_value(chip_select),
                self._enum_value(chip_select_polarity),
                frequency_hz,
            )
        )
        return status, None

    def spi_set_parameters(
        self,
        bit_order: SpiBitOrder | int,
        mode: SpiMode | int,
        data_width: SpiDataWidth | int,
        chip_select: SpiChipSelect | int,
        chip_select_polarity: SpiChipSelectPolarity | int,
        frequency_hz: int,
    ) -> tuple[int, None]:
        """Update SPI controller parameters."""
        status = int(
            self.lib.CosmicSDK_SpiSetParameters(
                self.driver,
                self._enum_value(bit_order),
                self._enum_value(mode),
                self._enum_value(data_width),
                self._enum_value(chip_select),
                self._enum_value(chip_select_polarity),
                frequency_hz,
            )
        )
        return status, None

    def spi_transfer(self, data: Optional[Sequence[int]], transfer_length: int) -> tuple[int, Optional[list[int]]]:
        """Perform an SPI transfer."""
        data_array, data_length = self._make_byte_array(data)
        out_buffer = (c_uint8 * transfer_length)()
        status = int(self.lib.CosmicSDK_SpiTransfer(self.driver, data_array, data_length, out_buffer, transfer_length))
        if status != SUCCESS:
            return status, None
        return status, [int(byte) for byte in out_buffer]

    def close(self) -> None:
        """Disconnect and destroy the wrapper handle."""
        driver = getattr(self, "driver", None)
        if not driver:
            return
        self.lib.CosmicSDK_Disconnect(driver)
        self.lib.CosmicSDK_Destroy(driver)
        self.driver = None

    disconnect = close


__all__ = ["CosmicPySDK"]
