from .event_utils import set_bit_in_vector, search_in_list
from .private_event_types import AdditionalSDPEventFilter, AdditionalLinkPatternEventFilter, \
    AdditionalInfoFrameEventFilter, AdditionalVBIDEventFilter, AdditionalMSAEventFilter, AdditionalLSEEventFilter
from UniTAP.libs.lib_tsi.tsi_types import *
import warnings


class EventFileFormat(IntEnum):
    """
    Describe all supported file formats for saving events:
    - BIN.
    - TXT (Support will be added later).
    """
    UNKNOWN = -1
    BIN = 0
    TXT = 1
    HTML = 2
    CSV = 3


class EventSDP(IntEnum):
    """
    Describe all supported SDP packets types:
    """
    AudioTimeStamp = 0x1
    AudioStream = 0x2
    Extension = 0x4
    AudioCopyManagement = 0x5
    ISRC = 0x6
    VSC = 0x7
    CG0 = 0x8
    CG1 = 0x9
    CG2 = 0xA
    CG3 = 0xB
    CG4 = 0xC
    CG5 = 0xD
    CG6 = 0xE
    CG7 = 0xF
    PictureParamSet = 0x10
    VSC_EXT_VESA = 0x20
    VSC_EXT_CTA = 0x21
    Adaptive_Sync = 0x22
    VS = 0x81
    AVI = 0x82
    SPD = 0x83
    Audio = 0x84
    MPEG = 0x85
    NTSC_VBI = 0x86
    DRM = 0x87


class EventLinkPattern(IntEnum):
    """
    Describe all supported Link Pattern packets types:
    """
    TPS1Begin = 0
    TPS1End = 1
    TPS2Begin = 2
    TPS2End = 3
    TPS3Begin = 4
    TPS3End = 5
    TPS4Begin = 6
    TPS4End = 7
    IdleBegin = 10
    IdleEnd = 11
    ActiveBegin = 12
    ActiveEnd = 13
    SleepBegin = 14
    SleepEnd = 15
    StandbyBegin = 16
    StandbyEnd = 17
    EIEOSBegin = 18
    EIEOSEnd = 19
    CustomBegin = 20
    CustomEnd = 21
    Begin25201 = 22
    End25201 = 23
    Begin25202 = 24
    End25202 = 25
    PRBS7Begin = 26
    PRBS7End = 27
    PRBS31Begin = 28
    PRBS31End = 29


class EventVBID(IntEnum):
    """
    Describe all supported VBID packets types:
    """
    SetVBlank = 0x1
    ClearVBlank = 0x2
    AnyVBlank = 0x3
    SetFieldID = 0x4
    CleatFieldID = 0x8
    AnyFieldID = 0xC
    SetInterface = 0x10
    CleatInterface = 0x20
    AnyInterface = 0x30
    SetNoVideo = 0x40
    CleatNoVideo = 0x80
    AnyNoVideo = 0xC0
    SetNoAudio = 0x100
    CleatNoAudio = 0x200
    AnyNoAudio = 0x300
    SetHDCPSYNC = 0x400
    CleatHDCPSYNC = 0x800
    AnyHDCPSYNC = 0xC00
    SetCompressed = 0x1000
    CleatCompressed = 0x2000
    AnyCompressed = 0x3000
    SetReserved = 0x4000
    CleatReserved = 0x8000
    AnyReserved = 0xC000
    MVID = 0x10000
    MAUD = 0x20000


class EventMSA(IntEnum):
    """
    Describe all supported MSA packets types:
    """
    MVID = 0x1
    NVID = 0x2
    HTOTAL = 0x4
    VTOTAL = 0x8
    HSTART = 0x10
    VSTART = 0x20
    HSP = 0x40
    HSW = 0x80
    VSP = 0x100
    VSW = 0x200
    HWIDTH = 0x400
    VHEIGHT = 0x800
    MISC0 = 0x1000
    MISC1 = 0x2000


class EventInfoFrame(IntEnum):
    """
    Describe all supported Info Frame packets types:
    """
    VS = 0x81
    AVI = 0x82
    SPD = 0x83
    Audio = 0x84
    MPEG = 0x85
    NTSC_VBI = 0x86
    DRM = 0x87


class EventData:
    """
    Class `EventData` describe one event. Contains following information:
    - `source`
    - `type`
    - `brief` info
    - `info`
    - `time`
    - `duration`
    - `data`
    """

    def __init__(self):
        self.__source = ""
        self.__type = ""
        self.__brief = ""
        self.__info = ""
        self.__time = 0
        self.__duration = 0
        self.__data = bytearray()

    @property
    def source(self):
        return self.__source

    @property
    def type(self):
        return self.__type

    @property
    def brief(self):
        return self.__brief

    @property
    def info(self):
        return self.__info

    @property
    def time(self):
        return self.__time

    @property
    def duration(self):
        return self.__duration

    @property
    def data(self):
        return self.__data

    @source.setter
    def source(self, source: str):
        self.__source = source

    @type.setter
    def type(self, _type: str):
        self.__type = _type

    @brief.setter
    def brief(self, brief: str):
        self.__brief = brief

    @info.setter
    def info(self, info: str):
        self.__info = info

    @time.setter
    def time(self, time: int):
        if time <= 0:
            raise ValueError(f"Time must be more than 0.")
        self.__time = time

    @duration.setter
    def duration(self, duration: int):
        if duration <= 0:
            raise ValueError(f"Duration must be more than 0.")
        self.__duration = duration

    @data.setter
    def data(self, data: bytearray):
        if len(data) <= 0:
            raise ValueError(f"Data length must be more than 0.")
        self.__data = data


class EventLCE:
    """
    Describe settings for LCE USB-C events:
    - `v_bus`
    - `iv_bus`
    - `vcc`
    - `vsbu`
    - `i_vconn`
    """

    def __init__(self, v_bus: int, iv_bus: int, vcc: int, vsbu: int, i_vconn: int):
        self.__v_bus = v_bus
        self.__iv_bus = iv_bus
        self.__vcc = vcc
        self.__vsbu = vsbu
        self.__i_vconn = i_vconn

    @property
    def v_bus(self):
        return self.__v_bus

    @property
    def iv_bus(self):
        return self.__iv_bus

    @property
    def vcc(self):
        return self.__vcc

    @property
    def vsbu(self):
        return self.__vsbu

    @property
    def i_vconn(self):
        return self.__i_vconn

    @v_bus.setter
    def v_bus(self, v_bus: int):
        if v_bus <= 0:
            raise ValueError(f"V bus must be more than 0.")
        self.__v_bus = v_bus

    @iv_bus.setter
    def iv_bus(self, iv_bus: int):
        if iv_bus <= 0:
            raise ValueError(f"I V bus must be more than 0.")
        self.__iv_bus = iv_bus

    @vcc.setter
    def vcc(self, vcc: int):
        if vcc <= 0:
            raise ValueError(f"VCC must be more than 0.")
        self.__vcc = vcc

    @vsbu.setter
    def vsbu(self, vsbu: int):
        if vsbu <= 0:
            raise ValueError(f"V sbu must be more than 0.")
        self.__vsbu = vsbu

    @i_vconn.setter
    def i_vconn(self, i_vconn: int):
        if i_vconn <= 0:
            raise ValueError(f"I vconn must be more than 0.")
        self.i_vconn = i_vconn

    def values(self) -> list:
        """
        Returns all values how list.

        Returns:
            object of list type
        """
        return [self.v_bus, self.iv_bus, self.vcc, self.vsbu, self.i_vconn]


def _set_bit_in_link_pattern(enable, data, bit_no: EventLinkPattern):
    _list = list(EventLinkPattern)
    _ind = _list.index(bit_no)
    if 0 <= _ind <= 7:
        if enable:
            data[0] |= (1 << bit_no.value)
        else:
            data[0] &= ~(1 << bit_no.value)
    elif 8 <= _ind <= 17:
        if enable:
            data[1] |= (1 << (bit_no.value - 10))
        else:
            data[1] &= ~(1 << (bit_no.value - 10))
    else:
        if enable:
            data[2] |= (1 << (bit_no.value - 20))
        else:
            data[2] &= ~(1 << (bit_no.value - 20))


class EventFilter:
    """
    Base class of all filters.
    """

    def __init__(self, hw_caps=None):
        self.__config = 0
        self._control_ci = 0
        self._hw_caps = hw_caps
        self.__additional_filter = []

    @property
    def config(self) -> int:
        """
        Returns current configuration value of filter.

        Returns:
            object of int type
        """
        return self.__config

    @property
    def additional_filter(self) -> list:
        """
        Returns current additional filters for main filter.

        Returns:
            object of list type
        """
        return self.__additional_filter

    @config.setter
    def config(self, config: int):
        """
        Set configuration value of filter.

        Args:
            config (int)
        """
        if config < 0:
            raise ValueError(f"Config value must be more than 0.")
        self.__config = config

    @additional_filter.setter
    def additional_filter(self, additional_filter: list):
        """
        Set additional filters of main filter.

        Args:
            additional_filter (list)
        """
        self.__additional_filter = additional_filter

    def clear(self):
        """
        Clear all config.
        """
        self.config = 0

        for item in self.additional_filter:
            if isinstance(item.value, int):
                item.value = 0
            elif isinstance(item.value, list):
                item.value = [0] * len(item.value)


class EventFilterDpRx(EventFilter):
    """
    Class `EventFilterDpRx` allows setting filter for DPRX available events: `config_hpd_events`, `config_aux_events`,
    `config_sdp_events`, `config_link_pattern_events`, `config_vb_id_events`, `config_msa_events`,
    `config_aux_bw_events`.
    Inherited from class `EventFilter`.
    """

    def __init__(self, hw_caps):
        super().__init__(hw_caps)
        self._control_ci = TSI_DPRX_LOG_CONTROL
        self.additional_filter = [AdditionalSDPEventFilter()]
        if hw_caps.link_pat_log:
            self.additional_filter.append(AdditionalLinkPatternEventFilter())
        if hw_caps.vbid_hw_log:
            self.additional_filter.append(AdditionalVBIDEventFilter())
        if self._hw_caps.msa_hw_log:
            self.additional_filter.append(AdditionalMSAEventFilter())

    def config_hpd_events(self, enable: bool):
        """
        Configure HDP events.

        Args:
            enable (bool) - enable/disable HDP events
        """
        if enable:
            self.config |= TSI_DPRX_LOG_CTRL_VALUE_HPD
        else:
            self.config &= ~TSI_DPRX_LOG_CTRL_VALUE_HPD

    def config_aux_events(self, enable: bool):
        """
        Configure AUX events.

        Args:
            enable (bool) - enable/disable AUX events
        """
        if enable:
            self.config |= TSI_DPRX_LOG_CTRL_VALUE_AUX
        else:
            self.config &= ~TSI_DPRX_LOG_CTRL_VALUE_AUX

    def config_sdp_events(self, enable: bool, *args: EventSDP):
        """
        Configure SDP events.

        Args:
            enable (bool) - enable/disable SDP events
            *args (`EventSDP`) - SDP packet types
        """
        if enable:
            self.config |= TSI_DPRX_LOG_CTRL_VALUE_SDP
        else:
            self.config &= ~TSI_DPRX_LOG_CTRL_VALUE_SDP

        if len(args) > 0:
            for item in args:
                if isinstance(item, EventSDP):
                    set_bit_in_vector(enable, self.additional_filter[0].value, item.value)

    def config_link_pattern_events(self, enable: bool, *args: EventLinkPattern):
        """
        Configure Link Pattern events.

        Args:
            enable (bool) - enable/disable Link Pattern events
            *args (`EventLinkPattern`) - Link Pattern packet types
        """
        if self._hw_caps is not None and self._hw_caps.link_pat_log:
            if enable:
                self.config |= TSI_DPRX_LOG_CTRL_VALUE_LINK_PAT
            else:
                self.config &= ~TSI_DPRX_LOG_CTRL_VALUE_LINK_PAT

            if len(args) > 0:
                for item in args:
                    if isinstance(item, EventLinkPattern):
                        _set_bit_in_link_pattern(enable,
                                                 self.additional_filter[search_in_list(AdditionalLinkPatternEventFilter,
                                                                                       self.additional_filter)].value,
                                                 item)
        else:
            warnings.warn("HW does not support Link Pattern Logger")

    def config_vb_id_events(self, enable: bool, *args: EventVBID):
        """
        Configure VB ID events.

        Args:
            enable (bool) - enable/disable VB ID events
            *args (`EventVBID`) - VB ID packet types
        """
        if self._hw_caps is not None and self._hw_caps.vbid_hw_log:
            if enable:
                self.config |= TSI_DPRX_LOG_CTRL_VALUE_VBID_HW
            else:
                self.config &= ~TSI_DPRX_LOG_CTRL_VALUE_VBID_HW

            if len(args) > 0:
                for item in args:
                    if isinstance(item, EventVBID):
                        if enable:
                            self.additional_filter[search_in_list(AdditionalVBIDEventFilter,
                                                                  self.additional_filter)].value |= item.value
                        else:
                            self.additional_filter[search_in_list(AdditionalVBIDEventFilter,
                                                                  self.additional_filter)].value &= ~item.value
        else:
            warnings.warn("HW does not support Vbid Hw Logger")

    def config_msa_events(self, enable: bool, *args: EventMSA):
        """
        Configure MSA events.

        Args:
            enable (bool) - enable/disable MSA events
            *args (`EventMSA`) - MSA packet types
        """
        if self._hw_caps is not None and self._hw_caps.msa_hw_log:
            if enable:
                self.config |= TSI_DPRX_LOG_CTRL_VALUE_MSA_HW
            else:
                self.config &= ~TSI_DPRX_LOG_CTRL_VALUE_MSA_HW

            if len(args) > 0:
                for item in args:
                    if isinstance(item, EventMSA):
                        if enable:
                            self.additional_filter[search_in_list(AdditionalMSAEventFilter,
                                                                  self.additional_filter)].value |= item.value
                        else:
                            self.additional_filter[search_in_list(AdditionalMSAEventFilter,
                                                                  self.additional_filter)].value &= ~item.value
        else:
            warnings.warn("HW does not support Msa Hw Logger")

    def config_aux_bw_events(self, enable: bool):
        """
        Configure AUX BW events.

        Args:
            enable (bool) - enable/disable AUX BW events
        """
        if self._hw_caps is not None and self._hw_caps.aux_bw_log:
            if enable:
                self.config |= TSI_DPRX_LOG_CTRL_VALUE_AUX_BW
            else:
                self.config &= ~TSI_DPRX_LOG_CTRL_VALUE_AUX_BW
        else:
            warnings.warn("HW does not support Aux Bw Logger")


class EventFilterDpTx(EventFilter):
    """
    Class `EventFilterDpTx` allows setting filter for DPTX available events: `config_hpd_events`, `config_aux_events`.
    Inherited from class `EventFilter`.
    """

    def __init__(self, hw_caps):
        super().__init__(hw_caps)
        self._control_ci = TSI_DPTX_LOG_CONTROL

    def config_hpd_events(self, enable: bool):
        """
        Configure HDP events.

        Args:
            enable (bool) - enable/disable HDP events
        """
        if enable:
            self.config |= TSI_DPTX_LOG_CONTROL_VALUE_HPD
        else:
            self.config &= ~TSI_DPTX_LOG_CONTROL_VALUE_HPD

    def config_aux_events(self, enable: bool):
        """
        Configure AUX events.

        Args:
            enable (bool) - enable/disable AUX events
        """
        if enable:
            self.config |= TSI_DPTX_LOG_CONTROL_VALUE_AUX
        else:
            self.config &= ~TSI_DPTX_LOG_CONTROL_VALUE_AUX


class EventFilterHdRx(EventFilter):
    """
    Class `EventFilterHdRx` allows setting filter for HDRX available events: `config_hpd_events`,
    `config_packets_events`, `config_i2c_events`, `config_cec_events`.
    Inherited from class `EventFilter`.
    """

    def __init__(self, hw_caps):
        super().__init__(hw_caps)
        self.additional_filter = [AdditionalInfoFrameEventFilter()]
        self._control_ci = TSI_HDRX_LOG_CONTROL

    def config_hpd_events(self, enable: bool):
        """
        Configure HPD events.

        Args:
            enable (bool) - enable/disable HPD events
        """
        if enable:
            self.config |= TSI_HDRX_LOG_CTRL_VALUE_HPD
        else:
            self.config &= ~TSI_HDRX_LOG_CTRL_VALUE_HPD

    def config_packets_events(self, enable: bool, *args: EventInfoFrame):
        """
        Configure InfoFrame events.

        Args:
            enable (bool) - enable/disable InfoFrame events
            *args (`EventInfoFrame`) - InfoFrame packet types
        """
        if enable:
            self.config |= TSI_HDRX_LOG_CTRL_VALUE_INFO
        else:
            self.config &= ~TSI_HDRX_LOG_CTRL_VALUE_INFO

        if len(args) > 0:
            for item in args:
                if isinstance(item, EventInfoFrame):
                    set_bit_in_vector(enable, self.additional_filter[0].value, item.value)

    def config_i2c_events(self, enable: bool):
        """
        Configure I2C events.

        Args:
            enable (bool) - enable/disable I2C events
        """
        if enable:
            self.config |= TSI_HDRX_LOG_CTRL_VALUE_I2C
        else:
            self.config &= ~TSI_HDRX_LOG_CTRL_VALUE_I2C

    def config_cec_events(self, enable: bool):
        """
        Configure CEC events.

        Args:
            enable (bool) - enable/disable CEC events
        """
        if enable:
            self.config |= TSI_HDRX_LOG_CTRL_VALUE_CEC
        else:
            self.config &= ~TSI_HDRX_LOG_CTRL_VALUE_CEC


class EventFilterHdTx(EventFilter):
    """
    Class `EventFilterHdTx` allows setting filter for HDTX available events: `config_hpd_events`, `config_i2c_events`,
    `config_cec_events`.
    Inherited from class `EventFilter`.
    """

    def __init__(self, hw_caps):
        super().__init__(hw_caps)
        self._control_ci = TSI_HDTX_LOG_CONTROL

    def config_hpd_events(self, enable: bool):
        """
        Configure HPD events.

        Args:
            enable (bool) - enable/disable HPD events
        """
        if enable:
            self.config |= TSI_HDTX_LOG_CTRL_VALUE_HPD
        else:
            self.config &= ~TSI_HDTX_LOG_CTRL_VALUE_HPD

    def config_i2c_events(self, enable: bool):
        """
        Configure I2C events.

        Args:
            enable (bool) - enable/disable I2C events
        """
        if enable:
            self.config |= TSI_HDTX_LOG_CTRL_VALUE_I2C
        else:
            self.config &= ~TSI_HDTX_LOG_CTRL_VALUE_I2C

    def config_cec_events(self, enable: bool):
        """
        Configure CEC events.

        Args:
            enable (bool) - enable/disable CEC events
        """
        if enable:
            self.config |= TSI_HDTX_LOG_CTRL_VALUE_CEC
        else:
            self.config &= ~TSI_HDTX_LOG_CTRL_VALUE_CEC


class EventFilterUsbc(EventFilter):
    """
    Class `EventFilterUsbc` allows setting filter for USB-C available events: `config_pd_events`,
    `config_voltage_events`, `config_usbc_events`, `config_port_state_events`.
    Inherited from class `EventFilter`.
    """

    def __init__(self, hw_caps):
        super().__init__(hw_caps)
        self._control_ci = TSI_PDC_LOG_CONTROL
        if hw_caps is not None and hw_caps.voltage:
            self.additional_filter.append(AdditionalLSEEventFilter())

    def config_pd_events(self, enable: bool):
        """
        Configure PD events.

        Args:
            enable (bool) - enable/disable PD events
        """
        if enable:
            self.config |= TSI_PDC_LOG_CTRL_VALUE_PD
        else:
            self.config &= ~TSI_PDC_LOG_CTRL_VALUE_PD

    def config_voltage_events(self, enable: bool, value: EventLCE):
        """
        Configure LCE (voltage) events.

        Args:
            enable (bool) - enable/disable LCE events
            value (`EventLCE`) - LCE packet types
        """
        if self._hw_caps is not None and self._hw_caps.voltage:
            if enable:
                self.config |= TSI_PDC_LOG_CTRL_VALUE_USBC_VOLTAGE
            else:
                self.config &= ~TSI_PDC_LOG_CTRL_VALUE_USBC_VOLTAGE

            for i in enumerate(value.values()):
                self.additional_filter[search_in_list(AdditionalLSEEventFilter,
                                                      self.additional_filter)].value[i[0]] = i[1]
        else:
            warnings.warn("HW does not support 'LCE voltage' events")

    def config_usbc_events(self, enable: bool):
        """
        Configure USB-C events.

        Args:
            enable (bool) - enable/disable USB-C events
        """
        if self._hw_caps is not None and self._hw_caps.events:
            if enable:
                self.config |= TSI_PDC_LOG_CTRL_VALUE_USBC_EVENT
            else:
                self.config &= ~TSI_PDC_LOG_CTRL_VALUE_USBC_EVENT
        else:
            warnings.warn("HW does not support 'USB-C' events")

    def config_port_state_events(self, enable: bool):
        """
        Configure USB-C state events.

        Args:
            enable (bool) - enable/disable USB-C state events
        """
        if self._hw_caps is not None and self._hw_caps.states:
            if enable:
                self.config |= TSI_PDC_LOG_CTRL_VALUE_USBC_STATE
            else:
                self.config &= ~TSI_PDC_LOG_CTRL_VALUE_USBC_STATE
        else:
            warnings.warn("HW does not support 'USB-C Port State' events")
