import ctypes
import threading
from pathlib import Path

from pyautd3.native_methods.autd3capi_driver import Duration, ResultLink

from pyautd3_link_ethercrab.native_methods.autd3_link_ethercrab import Status


class EtherCrabOption(ctypes.Structure):
    _fields_ = [
        ("ifname", ctypes.c_char_p),
        ("dc_configuration_sync0_period", Duration),
        ("state_check_period", Duration),
        ("sync_tolerance", Duration),
        ("sync_timeout", Duration),
    ]

    def __eq__(self, other: object) -> bool:
        return isinstance(other, EtherCrabOption) and self._fields_ == other._fields_  # pragma: no cover

    def __hash__(self) -> int:
        return super().__hash__()  # pragma: no cover


class Singleton(type):
    _instances = {}  # type: ignore[var-annotated]
    _lock = threading.Lock()

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            with cls._lock:
                if cls not in cls._instances:  # pragma: no cover
                    cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]


class NativeMethods(metaclass=Singleton):
    def init_dll(self, bin_location: Path, bin_prefix: str, bin_ext: str) -> None:
        self.dll = ctypes.CDLL(str(bin_location / f"{bin_prefix}autd3_capi_link_ethercrab{bin_ext}"))

        self.dll.AUTDLinkEtherCrab.argtypes = [ctypes.c_void_p, ctypes.c_void_p, EtherCrabOption]
        self.dll.AUTDLinkEtherCrab.restype = ResultLink

        self.dll.AUTDLinkEtherCrabIsDefault.argtypes = [EtherCrabOption]
        self.dll.AUTDLinkEtherCrabIsDefault.restype = ctypes.c_bool

        self.dll.AUTDLinkEtherCrabStatusGetMsg.argtypes = [ctypes.c_uint8, ctypes.c_char_p]
        self.dll.AUTDLinkEtherCrabStatusGetMsg.restype = ctypes.c_uint32

    def link_ether_crab(self, err_handler: ctypes.c_void_p, err_context: ctypes.c_void_p, option: EtherCrabOption) -> ResultLink:
        return self.dll.AUTDLinkEtherCrab(err_handler, err_context, option)

    def link_ether_crab_is_default(self, option: EtherCrabOption) -> ctypes.c_bool:
        return self.dll.AUTDLinkEtherCrabIsDefault(option)

    def link_ether_crab_status_get_msg(self, src: Status, dst: bytes) -> ctypes.c_uint32:
        return self.dll.AUTDLinkEtherCrabStatusGetMsg(src, dst)
