from UniTAP.libs.lib_tsi.tsi_io import PortIO
from UniTAP.libs.lib_tsi.tsi import *
from .pr_types import *
from .pr_private_types import *


class PanelReplayConfig:
    """
    Class `PanelReplayConfig` contains information about Panel Replay Configuration.
    - Set configuration object `PrSettings`, function `set`.
    - Get configuration object `PrSettings`, function `get`.
    """

    def __init__(self, port_io: PortIO, pg_caps):
        self.__io = port_io
        self.__y_granularity_list = [e for e in YGranularity]

        self.__mode_list = []
        if pg_caps.flags.panel_replay:
            self.__mode_list.append(PRMode.PR)
        if pg_caps.flags.psr:
            self.__mode_list.append(PRMode.PSR1)
            self.__mode_list.append(PRMode.PSR2)

    @property
    def pr_caps(self):
        return self.__read_caps()

    def set(self, config: PrSettings):
        """
        Write new configuration.

        Args:
            config (PrSettings).
        """
        caps = self.pr_caps

        self.__io.set(TSI_PG_STREAM_SELECT, 0)
        pr_config, _ = self.__read_config()

        pr_config.early_transport = config.flags.early_transport if caps.pr_flags().early_transport else 0
        pr_config.crc_in_vsc_sdp = config.flags.crc_in_vsc_sdp if caps.pr_flags().crc_sel_update else 0
        pr_config.hpd_irq_as_sdp = config.flags.hpd_irq
        pr_config.hpd_irq_vsc_sdp = config.flags.hpd_irq_vsc_sdp
        pr_config.hpd_irq_rfb = config.flags.hpd_irq_rfb
        pr_config.hpd_irq_crc = config.flags.hpd_irq_crc
        pr_config.self_refresh = config.flags.refresh_rate_unlock
        pr_config.ext_y_granularity = config.flags.ext_y_gran
        pr_config.main_link_on = config.flags.main_link_remain_on

        if config.flags.y_granularity in self.get_available_y_granularity_values():
            y_gran_value = (caps.pr_flags().y_gran_value & (1 << config.flags.y_granularity.value)) > 0
            pr_config.y_gran_value = 1 << config.flags.y_granularity.value if y_gran_value else 0

        if config.flags.mode in self.__mode_list:
            pr_config.mode = config.flags.mode.value

        data = [pr_config.value]
        for item in config.regions:
            region = RegionConfig(0)
            region.value = item
            data.extend(region.value)
        self.__io.set(TSI_PG_PR_CFG, data, data_count=len(data))

    def get(self) -> PrSettings:
        """
        Returns current Panel Replay Configuration.

        Returns:
            object of `PrSettings` type
        """
        pr_config, pr_regions = self.__read_config()
        pr_settings = PrSettings()
        pr_settings.flags = Flags(early_transport=pr_config.early_transport == 1,
                                  crc_in_vsc_sdp=pr_config.crc_in_vsc_sdp == 1,
                                  pr_mode=PRMode(pr_config.mode),
                                  hpd_irq=pr_config.hpd_irq_as_sdp == 1,
                                  hpd_irq_vsc_sdp=pr_config.hpd_irq_vsc_sdp == 1,
                                  hpd_irq_rfb=pr_config.hpd_irq_rfb == 1,
                                  hpd_irq_crc=pr_config.hpd_irq_crc == 1,
                                  refresh_rate_unlock=pr_config.self_refresh == 1,
                                  ext_y_gran=pr_config.ext_y_granularity == 1,
                                  main_link_remain_on=pr_config.main_link_on == 1)

        value = pr_config.y_gran_value
        bit_number = 0

        while value:
            if value & 0x1:
                pr_settings.y_granularity = YGranularity(bit_number)
                break
            bit_number += 1
            value >>= 1

        pr_settings.regions = pr_regions
        return pr_settings

    def get_available_y_granularity_values(self) -> List[YGranularity]:
        """
        Get available values for Y Granularity.

        Returns:
            list of `YGranularity` type
        """
        caps = self.pr_caps
        gran_list = []

        for i, enum_value in enumerate(YGranularity):
            if caps.pr_flags().y_gran_value >> i & 0x1:
                gran_list.append(enum_value)

        return gran_list

    def __read_config(self):
        caps = self.pr_caps

        self.__io.set(TSI_PG_STREAM_SELECT, 0)
        result = self.__io.get(TSI_PG_PR_CFG, c_uint32, caps.calculate_size())[1]
        if not isinstance(result, list):
            result = [0]
        pr_config = PrConfig(result[0])
        pr_regions = []
        for i in range(1, len(result) - 1, 2):
            pr_regions.append(RegionConfig(result[i] & 0xFFFF,
                                           result[i] >> 16 & 0xFFFF,
                                           result[i + 1] & 0xFFFF,
                                           result[i + 1] >> 16 & 0xFFFF))
        return pr_config, pr_regions

    def __read_caps(self) -> PRCaps:
        self.__io.set(TSI_PG_STREAM_SELECT, 0)
        return self.__io.get(TSI_PG_PR_CAPS_R, PRCaps)[1]
