import platform
import warnings

from UniTAP.libs.lib_tsi.tsi_types import TSI_OPF_CALLBACK_STRUCT, TSI_OPF_RETURN_CODE_ABORT
from UniTAP.libs.lib_tsi.tsi_io import DeviceIO
from UniTAP.utils import tsi_logging as logging
from .handlers import OpfHandlerBase, OpfHandlerDefault

if platform.system() == 'Windows':
    from ctypes import c_int, POINTER, c_void_p, WINFUNCTYPE

    OPF_CALLBACK_TYPE = WINFUNCTYPE(c_int, POINTER(TSI_OPF_CALLBACK_STRUCT), c_void_p)
else:
    from ctypes import c_int, POINTER, c_void_p, CFUNCTYPE

    OPF_CALLBACK_TYPE = CFUNCTYPE(c_int, POINTER(TSI_OPF_CALLBACK_STRUCT), c_void_p)


class OperatorFeedbackHandler:
    """
    Class `OperatorFeedbackHandler` helps to do required actions during DUT tests. Contains object of `OpfHandlerBase`,
    that can be overridden.
    - `handler` - set and get OPF Handler. `OpfHandlerBase` does OPF number 19 and 103,
    `OpfHandlerInternal` does all OPF, `OpfHandlerDefault` does overridden OPF by user.
    """

    def __init__(self, device_io: DeviceIO):
        global OPF_CALLBACK_TYPE
        self.__io = device_io
        self.__tsi_callback = OPF_CALLBACK_TYPE(self.__get_callback())
        self.__io.set_opf_callback(self.__tsi_callback)

        self.__handler = OpfHandlerDefault()

    def __get_callback(self):
        def ofp_impl(_struct: POINTER(TSI_OPF_CALLBACK_STRUCT), context_ptr: c_void_p):
            opf_struct: TSI_OPF_CALLBACK_STRUCT = _struct.contents
            logging.debug(f"Received OPF dialog with ID: {opf_struct.id()}, Session: {opf_struct.session_id()}.")

            try:
                return self.handler.handle(opf_struct.id(),
                                             opf_struct.write_opf_result,
                                             opf_struct.title(),
                                             opf_struct.request1(),
                                             opf_struct.request2(),
                                             opf_struct.parameters())
            except BaseException as e:
                warnings.warn(f"OPF Dialog {opf_struct.id()} was aborted due an exception. Exception: {e}")
                return opf_struct.write_opf_result(TSI_OPF_RETURN_CODE_ABORT)

        return ofp_impl

    @property
    def handler(self) -> OpfHandlerBase:
        """

        Return current OPF handler. Can be overriden in `set` method.

        Returns:
            object of `OpfHandlerBase` type
        """
        return self.__handler

    @handler.setter
    def handler(self, value):
        self.__handler = value
