from typing import Optional, Union, Type
from UniTAP.libs.lib_tsi import PortIO
from UniTAP.libs.lib_tsi.tsi_private_types import *
from .link_tx_types import *
from .private_link_tx_types import *
from .link_status_common import *


class LinkDisplayPortForceConfig:
    """
    Class `LinkDisplayPortForceConfig` allows settings link configuration on Source (TX - transmitter) side.
    - Set configuration `set`.
    - Get current configuration on link `get`.
    """
    def __init__(self, port_io: PortIO, caps: DPTXHWCaps):
        self.__io = port_io
        self.__caps = caps

    def set(self, config: DisplayPortLinkConfig):
        """
        Write new configuration on DP link.

        Args:
            config (`DisplayPortLinkConfig`).
        """
        if isinstance(config, LinkConfig.Force8b10b):
            # TODO
            pass
        elif isinstance(config, LinkConfig.Force128b132b):
            self.__force_set_bit_rate(config.bit_rate)
            self.__force_set_lane_count(config.lane_count)
            self.__force_set_pattern(config.pattern)
        else:
            raise TypeError("Incorrect LinkConfig Force format!")

    def get(self, config_type: Type[DisplayPortLinkConfig]) -> DisplayPortLinkConfig:
        """
        Returns current DP source link configuration.

        Args:
            config_type Type[DisplayPortLinkConfig].

        Returns:
            object of `DisplayPortLinkConfig` type
        """
        if issubclass(config_type, LinkConfig.Force8b10b):
            # TODO
            return LinkConfig.Force8b10b()
        elif issubclass(config_type, LinkConfig.Force128b132b):
            link_status = LinkConfig.Force128b132b()
            link_status.lane_count = self.__force_get_lane_count()
            link_status.bit_rate = self.__force_get_bit_rate()
            link_status.pattern = self.__force_get_pattern()
            return link_status
        else:
            raise TypeError("Incorrect type!")

    def __force_set_bit_rate(self, bit_rate: Optional[Union[float, int]]):
        if bit_rate is None:
            return
        elif (bit_rate == 10.0 and not self.__caps.support_10gbps) or \
             (bit_rate == 13.5 and not self.__caps.support_13_5gbps) or \
             (bit_rate == 20.0 and not self.__caps.support_20gbps):
            raise ValueError(f"Following bit rate {bit_rate} is not supported on device.")
        self.__io.set(TSI_DP2TX_OUT_BR, DP21_LinkRateRev.get(bit_rate), c_uint32)

    def __force_get_bit_rate(self) -> float:
        return DP21_LinkRate.get(self.__io.get(TSI_DP2TX_OUT_BR)[1])

    def __force_set_lane_count(self, lane_count: Optional[int]):
        if lane_count is None:
            return
        if lane_count not in [1, 2, 4]:
            raise ValueError(f"Incorrect lane count number {lane_count}. Must be from list: 1, 2, 4")
        self.__io.set(TSI_DP2TX_OUT_LC, lane_count, c_uint32)

    def __force_get_lane_count(self) -> int:
        return self.__io.get(TSI_DP2TX_OUT_LC)[1]

    def __force_set_pattern(self, pattern: Optional[DP128b132bLinkPattern]):
        if pattern is None:
            return
        self.__io.set(TSI_DP2TX_OUT_TEST_PATTERN, pattern.value, c_uint32)

    def __force_get_pattern(self) -> DP128b132bLinkPattern:
        return DP128b132bLinkPattern(self.__io.get(TSI_DP2TX_OUT_TEST_PATTERN, c_uint32)[1])
