from UniTAP.dev.modules.dut_tests.test_group_params_types import Param


class VideoModeStandard:
    """
    Class `VideoModeStandard` describes available supported timings standard.
    - CTA `cta` (enable/disable).
    - DMT `dmt` (enable/disable).
    - CVT `cvt` (enable/disable).
    - CVT RB1 `cvt_rb1` (enable/disable).
    - CVT RB2 `cvt_rb2` (enable/disable).
    - OVT `ovt` (enable/disable).
    - Set all standards `set_all`.
    """
    def __init__(self, value: Param):
        self.__value = value

    @property
    def cta(self) -> bool:
        """
        Set and get CTA flag support.

        Returns:
            object of bool type
        """
        return self.__value._get_by_bit_number(0)

    @property
    def dmt(self) -> bool:
        """
        Set and get DMT flag support.

        Returns:
            object of bool type
        """
        return self.__value._get_by_bit_number(1)

    @property
    def cvt(self) -> bool:
        """
        Set and get CVT flag support.

        Returns:
            object of bool type
        """
        return self.__value._get_by_bit_number(2)

    @property
    def cvt_rb1(self) -> bool:
        """
        Set and get RB1 flag support.

        Returns:
            object of bool type
        """
        return self.__value._get_by_bit_number(3)

    @property
    def cvt_rb2(self) -> bool:
        """
        Set and get RB2 flag support.

        Returns:
            object of bool type
        """
        return self.__value._get_by_bit_number(4)

    @property
    def ovt(self) -> bool:
        """
        Set and get OVT flag support.

        Returns:
            object of bool type
        """
        return self.__value._get_by_bit_number(5)

    @cta.setter
    def cta(self, value: bool):
        self.__value._set_by_bit_number(value, 1, 0)

    @dmt.setter
    def dmt(self, value: bool):
        self.__value._set_by_bit_number(value, 1, 1)

    @cvt.setter
    def cvt(self, value: bool):
        self.__value._set_by_bit_number(value, 1, 2)

    @cvt_rb1.setter
    def cvt_rb1(self, value: bool):
        self.__value._set_by_bit_number(value, 1, 3)

    @cvt_rb2.setter
    def cvt_rb2(self, value: bool):
        self.__value._set_by_bit_number(value, 1, 4)

    @ovt.setter
    def ovt(self, value: bool):
        self.__value._set_by_bit_number(value, 1, 5)

    def set_all(self):
        self.cta = True
        self.dmt = True
        self.cvt = True
        self.cvt_rb1 = True
        self.cvt_rb2 = True
        self.ovt = True

    def clear_all(self):
        self.cta = False
        self.dmt = False
        self.cvt = False
        self.cvt_rb1 = False
        self.cvt_rb2 = False
        self.ovt = False


class ColorimetryModes:
    """
    Class `ColorimetryModes` describes available supported colorimetry.
    - RGB `rgb` (enable/disable, set bcp).
    - YCbCr 444 `ycbcr444` (enable/disable, set bcp).
    - YCbCr 422 `ycbcr422` (enable/disable, set bcp).
    - YCbCr Simple 422 `ycbcr_simple422` (enable/disable, set bcp).
    - YCbCr 420 `ycbcr420` (enable/disable, set bcp).
    """

    __COLORIMETRY_VALUES = {8: 1, 10: 3, 12: 7, 16: 15}
    __COLORIMETRY_VALUES_MAP = {0: 0, 1: 8, 3: 10, 7: 12, 15: 16}
    __AVAILABLE_BPC_VALUES = [0, 8, 10, 12, 16]
    __STR_AVAILABLE_BPC_VALUES = [f" {_str}" for _str in __AVAILABLE_BPC_VALUES]

    def __init__(self, value: Param):
        self.__value = value

    @property
    def rgb(self) -> int:
        """
        Set and get RGB bpc value.

        Returns:
            object of int type
        """
        return self.__COLORIMETRY_VALUES_MAP.get((self.__value.default_value >> 12) & 0xF)

    @property
    def ycbcr444(self) -> int:
        """
        Set and get YCbCr 444 bpc value.

        Returns:
            object of int type
        """
        return self.__COLORIMETRY_VALUES_MAP.get((self.__value.default_value >> 16) & 0xF)

    @property
    def ycbcr422(self) -> int:
        """
        Set and get YCbCr 422 bpc value.

        Returns:
            object of int type
        """
        return self.__COLORIMETRY_VALUES_MAP.get((self.__value.default_value >> 20) & 0xF)

    @property
    def ycbcr_simple422(self) -> int:
        """
        Set and get YCbCr Simple 422 bpc value.

        Returns:
            object of bool type
        """
        return self.__COLORIMETRY_VALUES_MAP.get((self.__value.default_value >> 24) & 0xF)

    @property
    def ycbcr420(self) -> int:
        """
        Set and get YCbCr 420 bpc value.

        Returns:
            object of bool type
        """
        return self.__COLORIMETRY_VALUES_MAP.get((self.__value.default_value >> 28) & 0xF)

    @rgb.setter
    def rgb(self, value: int):
        if value not in self.__AVAILABLE_BPC_VALUES:
            raise ValueError(f'Incorrect input bpc value {value}. '
                             f'Value must be from list:{self.__STR_AVAILABLE_BPC_VALUES}')

        new_value = self.__value.default_value
        new_value &= ~(0xF << 12)
        if value == 0:
            self.__value.default_value = new_value
        else:
            new_value |= (self.__COLORIMETRY_VALUES.get(value) << 12)
            self.__value.default_value = new_value

    @ycbcr444.setter
    def ycbcr444(self, value: int):
        if value not in self.__AVAILABLE_BPC_VALUES:
            raise ValueError(f'Incorrect input bpc value {value}. '
                             f'Value must be from list:{self.__STR_AVAILABLE_BPC_VALUES}')

        new_value = self.__value.default_value
        new_value &= ~(0xF << 16)
        if value == 0:
            self.__value.default_value = new_value
        else:
            new_value |= (self.__COLORIMETRY_VALUES.get(value) << 16)
            self.__value.default_value = new_value

    @ycbcr422.setter
    def ycbcr422(self, value: int):
        if value not in self.__AVAILABLE_BPC_VALUES:
            raise ValueError(f'Incorrect input bpc value {value}. '
                             f'Value must be from list:{self.__STR_AVAILABLE_BPC_VALUES}')

        new_value = self.__value.default_value
        new_value &= ~(0xF << 20)
        if value == 0:
            self.__value.default_value = new_value
        else:
            new_value |= (self.__COLORIMETRY_VALUES.get(value) << 20)
            self.__value.default_value = new_value

    @ycbcr_simple422.setter
    def ycbcr_simple422(self, value: int):
        if value not in self.__AVAILABLE_BPC_VALUES:
            raise ValueError(f'Incorrect input bpc value {value}. '
                             f'Value must be from list:{self.__STR_AVAILABLE_BPC_VALUES}')

        new_value = self.__value.default_value
        new_value &= ~(0xF << 24)
        if value == 0:
            self.__value.default_value = new_value
        else:
            new_value |= (self.__COLORIMETRY_VALUES.get(value) << 24)
            self.__value.default_value = new_value

    @ycbcr420.setter
    def ycbcr420(self, value: int):
        if value not in self.__AVAILABLE_BPC_VALUES:
            raise ValueError(f'Incorrect input bpc value {value}. '
                             f'Value must be from list:{self.__STR_AVAILABLE_BPC_VALUES}')

        new_value = self.__value.default_value
        new_value &= ~(0xF << 28)
        if value == 0:
            self.__value.default_value = new_value
        else:
            new_value |= (self.__COLORIMETRY_VALUES.get(value) << 28)
            self.__value.default_value = new_value

    def set_all(self, bpc: int):
        if bpc not in self.__AVAILABLE_BPC_VALUES:
            raise ValueError(f'Incorrect input bpc value {bpc}. '
                             f'Value must be from list:{self.__STR_AVAILABLE_BPC_VALUES}')
        self.rgb = bpc
        self.ycbcr444 = bpc
        self.ycbcr422 = bpc
        self.ycbcr_simple422 = bpc
        self.ycbcr420 = bpc

    def clear_all(self):
        self.rgb = 0
        self.ycbcr444 = 0
        self.ycbcr422 = 0
        self.ycbcr_simple422 = 0
        self.ycbcr420 = 0


class VideoModeInfo:

    def __init__(self, json_obj):
        self.__value = Param(json_obj)
        self.__standard = VideoModeStandard(self.__value)
        self.__colorimetry = ColorimetryModes(self.__value)

    @property
    def standard(self) -> VideoModeStandard:
        return self.__standard

    @property
    def colorimetry(self) -> ColorimetryModes:
        return self.__colorimetry
