from UniTAP.libs.lib_tsi.tsi_io import PortIO
from .types import HdmiModeTx, FrlMode, _update_error_counters_tx
from UniTAP.libs.lib_tsi.tsi_types import TSI_HDTX_FRL_STATUS_R, TSI_HDTX_CONTROL_W, TSI_HDTX_STATUS_R, \
    TSI_HDTX_SINK_STATUS_R, TSI_HDTX_LANES_ERR_COUNTERS_R, TSI_HDTX_HPD_STATUS_R
from ctypes import c_uint32, c_uint64


class StatusTx:
    """
    Class `StatusTx` describes information about HDMI link status on Source (TX - transmitter) side.
    Contains following info:
    - Set and get HDMI mode `hdmi_mode`.
    - Link Error counters `error_counters`.
    - Video status `video_status`.
    - Channel lock `channel_lock`.
    - HPD status `hpd_status`.
    """
    def __init__(self, port_io: PortIO):
        self.__io = port_io

    def __read_status(self) -> int:
        return self.__io.get(TSI_HDTX_STATUS_R, c_uint32)[1]

    def __frl_status(self) -> FrlMode:
        return FrlMode(self.__io.get(TSI_HDTX_FRL_STATUS_R, c_uint32)[1] & 0xF)

    @property
    def video_status(self) -> bool:
        """
        Returns current video status (video enable or not).

        Returns:
            object of bool type
        """
        return (self.__read_status() & 0x2) != 0

    @property
    def error_counters(self) -> list:
        """
        Returns values of current errors on link.

        Returns:
            object of list type
        """
        return _update_error_counters_tx(self.__io.get(TSI_HDTX_LANES_ERR_COUNTERS_R, c_uint64)[1])

    @property
    def channel_lock(self) -> list:
        """
        Returns channel lock states of current link.

        Returns:
            object of list type
        """
        if self.__frl_status().Mode_Disable or self.hdmi_mode in [HdmiModeTx.HDMI_1_4, HdmiModeTx.HDMI_2_0]:
            return self.__update_channel_lock(self.__io.get(TSI_HDTX_SINK_STATUS_R, c_uint32)[1], self.hdmi_mode)
        else:
            return self.__update_channel_lock(self.__io.get(TSI_HDTX_FRL_STATUS_R, c_uint32)[1], self.hdmi_mode)

    @property
    def hpd_status(self) -> bool:
        """
        Returns True if HDP is enabled, False - if not.

        Returns:
            object of bool type
        """
        return (self.__io.get(TSI_HDTX_HPD_STATUS_R, c_uint32)[1] & 0x1) != 0

    @property
    def hdmi_mode(self) -> HdmiModeTx:
        """
        Returns current HDMI mode.

        Returns:
            object of `HdmiModeTx` type
        """
        return HdmiModeTx((self.__read_status() >> 2) & 0x3)

    @hdmi_mode.setter
    def hdmi_mode(self, hdmi_mode: HdmiModeTx):
        """
        Set new HDMI mode.

        Args:
            hdmi_mode (HdmiModeTx)
        """
        self.__io.set(TSI_HDTX_CONTROL_W, hdmi_mode.value << 2, c_uint32)

    @property
    def available_link_rate(self) -> float:
        """

        Returns available link rate.

        Returns:
            object of float type
        """

        gbps = 1000000000
        frl_status = self.__frl_status()
        if frl_status == FrlMode.Mode_Disable:
            link_rate = 3 * 6 * gbps
        elif frl_status == FrlMode.Mode_3lanes_3gbps:
            link_rate = 3 * 3 * gbps
        elif frl_status == FrlMode.Mode_3lanes_6gbps:
            link_rate = 3 * 6 * gbps
        elif frl_status == FrlMode.Mode_4lanes_6gbps:
            link_rate = 4 * 6 * gbps
        elif frl_status == FrlMode.Mode_4lanes_8gbps:
            link_rate = 4 * 8 * gbps
        elif frl_status == FrlMode.Mode_4lanes_10gbps:
            link_rate = 4 * 10 * gbps
        elif frl_status == FrlMode.Mode_4lanes_12gbps:
            link_rate = 4 * 12 * gbps
        else:
            link_rate = 0

        return link_rate

    @staticmethod
    def __update_channel_lock(value, mode: HdmiModeTx) -> list:
        if mode != HdmiModeTx.HDMI_2_1:
            lane_0 = (value & (1 << 6)) != 0
            lane_1 = (value & (1 << 7)) != 0
            lane_2 = (value & (1 << 8)) != 0
            lane_3 = False

        else:
            lane_0 = (((value >> 25) & 0xF) >> 0) & 0x1 != 0
            lane_1 = (((value >> 25) & 0xF) >> 1) & 0x1 != 0
            lane_2 = (((value >> 25) & 0xF) >> 2) & 0x1 != 0
            lane_3 = (((value >> 25) & 0xF) >> 3) & 0x1 != 0

        return [lane_0, lane_1, lane_2, lane_3]

    def __str__(self):
        return f"HDMI Mode: {self.hdmi_mode.name}\n" \
               f"Channel Lock:\n{self.channel_lock}\n" \
               f"Error Counters:\n{self.error_counters.__str__()}\n"
