from enum import IntEnum
from .hdmi_utils import *
from .private_types import HdmiVideoModeInfo


class FrlMode(IntEnum):
    """
    Class `FrlMode` contains all possible variants of FRL modes.
    """
    Mode_Unknown = -1
    Mode_Disable = 0
    Mode_3lanes_3gbps = 1
    Mode_3lanes_6gbps = 2
    Mode_4lanes_6gbps = 3
    Mode_4lanes_8gbps = 4
    Mode_4lanes_10gbps = 5
    Mode_4lanes_12gbps = 6


class LtpPattern(IntEnum):
    """
    Class `LtpPattern` contains all possible variants of LTP pattern types.
    """
    Unknown = -1
    NoLinkPattern = 0
    All_1s = 1
    All_0s = 2
    NyquistClockPattern = 3
    SourceTxFFECompliance = 4
    LFSR0 = 5
    LFSR1 = 6
    LFSR2 = 7
    LFSR3 = 8


class ScramblerState(IntEnum):
    """
    Class `ScramblerState` contains all possible variants of scrambler states.
    """
    Unknown = -1
    Disable = 0
    Enable = 1


class LinkMode(IntEnum):
    """
    Class `LinkMode` contains all possible variants of Link modes.
    """
    Unknown = -1
    HDMI = 0
    DVI = 1


class ClockRate(IntEnum):
    """
    Class `ClockRate` contains all possible variants of clock rate.
    """
    Unknown = -1
    Rate3G = 0
    Rate6G = 1


class HdmiModeTx(IntEnum):
    """
    Class `HdmiModeTx` contains all possible variants of HDMI modes on TX side.
    """
    Unknown = -1
    HDMI_2_0 = 0
    HDMI_1_4 = 1
    HDMI_2_1 = 2


class HdmiModeRx(IntEnum):
    """
    Class `HdmiModeRx` contains all possible variants of HDMI modes on RX side.
    """
    Unknown = -1
    HDMI_1_4 = 0
    HDMI_2_0 = 1
    HDMI_2_1 = 2


class LtpLanesPattern:
    """
    Class `LtpLanesPattern` describes LTP pattern values on all lanes.
    - Lane 0 `lane0`.
    - Lane 1 `lane1`.
    - Lane 2 `lane2`.
    - Lane 3 `lane3`.
    """
    def __init__(self):
        self.__lane0 = LtpPattern.Unknown
        self.__lane1 = LtpPattern.Unknown
        self.__lane2 = LtpPattern.Unknown
        self.__lane3 = LtpPattern.Unknown

    @property
    def lane0(self) -> LtpPattern:
        """
        Returns `LtpPattern` value of line 0.

        Returns:
            object of `LtpPattern` type
        """
        return self.__lane0

    @lane0.setter
    def lane0(self, lane0: LtpPattern):
        """
        Set new value `LtpPattern` to lane 0.

        Args:
            lane0 (LtpPattern)
        """
        self.__lane0 = lane0

    @property
    def lane1(self) -> LtpPattern:
        """
        Returns `LtpPattern` value of line 1.

        Returns:
            object of `LtpPattern` type
        """
        return self.__lane1

    @lane1.setter
    def lane1(self, lane1: LtpPattern):
        """
        Set new value `LtpPattern` to lane 1.

        Args:
            lane1 (LtpPattern)
        """
        self.__lane1 = lane1

    @property
    def lane2(self) -> LtpPattern:
        """
        Returns `LtpPattern` value of line 2.

        Returns:
            object of `LtpPattern` type
        """
        return self.__lane2

    @lane2.setter
    def lane2(self, lane2: LtpPattern):
        """
        Set new value `LtpPattern` to lane 1.

        Args:
            lane2 (LtpPattern)
        """
        self.__lane2 = lane2

    @property
    def lane3(self) -> LtpPattern:
        """
        Returns `LtpPattern` value of line 3.

        Returns:
            object of `LtpPattern` type
        """
        return self.__lane3

    @lane3.setter
    def lane3(self, lane3: LtpPattern):
        """
        Set new value `LtpPattern` to lane 3.

        Args:
            lane3 (LtpPattern)
        """
        self.__lane3 = lane3

    def value(self) -> int:
        """
        Returns combines value of all lines.

        Returns:
            object of int type
        """
        return self.lane0.value | (self.lane1.value << 4) | (self.lane2.value << 8) | (self.lane3.value << 12)

    def __str__(self):
        return f"Lane 0: {self.lane0}\n" \
               f"Lane 1: {self.lane1}\n" \
               f"Lane 2: {self.lane2}\n" \
               f"Lane 3: {self.lane3}\n"


class FrlCaps:
    """
    Class `FrlCaps` describes FRL capabilities.
    - FRL start `frl_start`.
    - FLT no timeout `flt_no_timeout`.
    - FLT ready `flt_ready`
    - FRL max `frl_max`.
    - Check patterns state `check_patterns`.
    """
    def __init__(self):
        self.__frl_start = False
        self.__flt_no_timeout = False
        self.__flt_ready = False
        self.__frl_max = False
        self.__check_patterns = False

    @property
    def frl_start(self) -> bool:
        """
        Returns state of FRL start.

        Returns:
            object of bool type
        """
        return self.__frl_start

    @frl_start.setter
    def frl_start(self, frl_start: bool):
        """
        Set new state to FRL start.

        Args:
            frl_start (bool)
        """
        self.__frl_start = frl_start

    @property
    def flt_no_timeout(self) -> bool:
        """
        Returns state of FLT no timeout.

        Returns:
            object of bool type
        """
        return self.__flt_no_timeout

    @flt_no_timeout.setter
    def flt_no_timeout(self, flt_no_timeout: bool):
        """
        Set new state to FLT no timeout.

        Args:
            flt_no_timeout (bool)
        """
        self.__flt_no_timeout = flt_no_timeout

    @property
    def flt_ready(self) -> bool:
        """
        Returns state of FLT ready.

        Returns:
            object of bool type
        """
        return self.__flt_ready

    @flt_ready.setter
    def flt_ready(self, flt_ready: bool):
        """
        Set new state to FLT ready.

        Args:
            flt_ready (bool)
        """
        self.__flt_ready = flt_ready

    @property
    def frl_max(self) -> bool:
        """
        Returns state of FLT max.

        Returns:
            object of bool type
        """
        return self.__frl_max

    @frl_max.setter
    def frl_max(self, frl_max: bool):
        """
        Set new state to FLT max.

        Args:
            frl_max (bool)
        """
        self.__frl_max = frl_max

    @property
    def check_patterns(self) -> bool:
        """
        Returns state of check patterns.

        Returns:
            object of bool type
        """
        return self.__check_patterns

    @check_patterns.setter
    def check_patterns(self, check_patterns: bool):
        """
        Set new state to check patterns.

        Args:
            check_patterns (bool)
        """
        self.__check_patterns = check_patterns

    def value(self) -> int:
        """
        Returns combined value of all flags.

        Returns:
            object of int type
        """
        return (self.frl_start << 4) | (self.flt_ready << 5) | (self.flt_no_timeout << 6) | (self.frl_max << 7) | \
               (self.check_patterns << 8)

    def __str__(self):
        return f"FRL Start: {self.__frl_start}\n" \
               f"FRL No Timeout: {self.__flt_no_timeout}\n" \
               f"FRL Max: {self.__frl_max}\n" \
               f"Check patterns: {self.__check_patterns}\n"


def _hdmi_vm_info_to_video_mode(hdmi_vm_info: HdmiVideoModeInfo):
    video_mode = VideoMode()

    video_mode.timing.hactive = hdmi_vm_info.h_active
    video_mode.timing.vactive = hdmi_vm_info.v_active
    video_mode.timing.htotal = hdmi_vm_info.h_total
    video_mode.timing.vtotal = hdmi_vm_info.v_total
    video_mode.timing.hstart = hdmi_vm_info.h_start
    video_mode.timing.vstart = hdmi_vm_info.v_start
    video_mode.timing.hswidth = hdmi_vm_info.h_sync_width
    video_mode.timing.vswidth = hdmi_vm_info.v_sync_width

    video_mode.color_info.colorimetry = get_vm_colorimetry(hdmi_vm_info.colorimetry)
    video_mode.color_info.color_format = get_vm_color_format(hdmi_vm_info.color_mode)
    video_mode.color_info.bpc = hdmi_vm_info.bpc
    video_mode.color_info.dynamic_range = ColorInfo.DynamicRange.DR_VESA

    video_mode.timing.frame_rate = hdmi_vm_info.frame_rate

    return video_mode


def _update_frl_values(frl_caps: FrlCaps, value: int):
    frl_caps.frl_start = bool(value >> 4 & 0x1)
    frl_caps.flt_ready = bool(value >> 5 & 0x1)
    frl_caps.flt_no_timeout = bool(value >> 6 & 0x1)
    frl_caps.frl_max = bool(value >> 7 & 0x1)
    frl_caps.check_patterns = bool(value >> 8 & 0x1)


def _update_ltp_pattern_values(pattern: LtpLanesPattern, value: int):
    pattern.lane0 = LtpPattern(value & 0xF)
    pattern.lane1 = LtpPattern((value >> 4) & 0xF)
    pattern.lane2 = LtpPattern((value >> 8) & 0xF)
    pattern.lane3 = LtpPattern((value >> 12) & 0xF)


def _update_error_counters_rx(value: int) -> list:
    lane0 = value & 0x7FFF if (value >> 15) & 0x1 else 0
    lane1 = ((value >> 16) & 0xFFFF) if (value >> 31) & 0x1 else 0
    lane2 = ((value >> 32) & 0xFFFF) if (value >> 47) & 0x1 else 0
    lane3 = ((value >> 48) & 0xFFFF) if (value >> 63) & 0x1 else 0
    return [lane0, lane1, lane2, lane3]


def _update_error_counters_tx(value: int) -> list:
    return [value & 0xFFFF, (value >> 16) & 0xFFFF, (value >> 32) & 0xFFFF, (value >> 48) & 0xFFFF]
