from UniTAP.dev.modules.dut_tests.dut_default_params.dp_1_4_source_general_tab import DutCapsFlags,\
    update_default_value, Param, IntEnum, TestAutomationFlags


class MaxLinkBwPolicy(IntEnum):
    """
    Describes available values for maximum link bandwidth policy.
    """
    Link1LRBR = 262
    Link1LHBR = 266
    Link2LRBR = 518
    Link2LHBR = 522


class TestAutomationFlagsDP21(TestAutomationFlags):
    """
    Class `TestAutomationFlagsDP21` allows configuring test automation flags:
    - Set and get video operator input flag `video_operator_input`.
    - Set and get DSC VIC flag `dsc_vis_val`.

    Also has all the `TestAutomationFlags` functionality.
    """

    @property
    def video_operator_input(self) -> bool:
        """
        Set and get video operator input flag.
        If flag == true -> If Source DUT does not generate the requested format then GUI request will be made to
        operator to generate the requested format from the EDID (request and waits for test operator input).
        If flag == false -> If Source DUT does not generate the requested Video Format, then no GUI request will be
        made, and test will fail if parameters does not match (request and waits for test operator input).

        Usually use for manual testing.

        Returns:
            object of bool type
        """
        return self._param._get_by_bitmask(5, bool)

    @video_operator_input.setter
    def video_operator_input(self, video_operator_input: bool):
        self._param._set_by_bitmask(int(video_operator_input), 5)

    @property
    def dsc_vis_val(self) -> bool:
        """
        Set and get DSC visual validation flag.
        If flag == true -> will check timing, color depth, color space and video CRC.
        If flag == false -> will check timing, color depth, color space without checking video CRC.

        Usually use for manual checking.

        Returns:
            object of bool type
        """
        return self._param._get_by_bitmask(6, bool)

    @dsc_vis_val.setter
    def dsc_vis_val(self, dsc_vis_val: bool):
        self._param._set_by_bitmask(int(dsc_vis_val), 6)


class LinkRateDp21(Param):
    """
    Class `LinkRateDp21` describes support DP 2.1 rates. Allows getting and setting values.
    - Support 10 Gbps `support_10Gbps`.
    - Support 13.5 Gbps `support_13_5Gbps`.
    - Support 20 Gbps `support_20Gbps`.
    """
    def __init__(self, json_obj):
        super().__init__(json_obj)

    @property
    def support_10Gbps(self) -> bool:
        """
        Set and get 10 Gbps flag support.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(0, bool)


    @support_10Gbps.setter
    def support_10Gbps(self, support_10Gbps: bool):
        self._set_by_bitmask(support_10Gbps, 0)

    @property
    def support_20Gbps(self) -> bool:
        """
        Set and get 20 Gbps flag support.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(1, bool)

    @support_20Gbps.setter
    def support_20Gbps(self, support_20Gbps: bool):
        self._set_by_bitmask(support_20Gbps, 1)

    @property
    def support_13_5Gbps(self) -> bool:
        """
        Set and get 13.5 Gbps flag support.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(2, bool)

    @support_13_5Gbps.setter
    def support_13_5Gbps(self, support_13_5Gbps: bool):
        self._set_by_bitmask(support_13_5Gbps, 2)


class DutCapsDp21Flags(Param):
    """
    Class `DutCapsDp21Flags` inherited of class`DutCapsFlags` which defines the DUT capabilities as flags and allows
    setting:
    - Maximum link bandwidth policy supported flag `max_link_bandwidth_policy_supported`.

    Also has all the `DutCapsFlags` functionality.
    """
    def __init__(self, json_obj):
        super().__init__(json_obj)

    @property
    def voltage_swing_supported(self) -> bool:
        """
        Set and get Voltage swing level flag support.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(0, bool)

    @voltage_swing_supported.setter
    def voltage_swing_supported(self, voltage_swing_supported: bool):
        self._set_by_bitmask(voltage_swing_supported, 0)

    @property
    def pre_emphasis_supported(self) -> bool:
        """
        Set and get Pre-emphasis level flag support.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(1, bool)

    @pre_emphasis_supported.setter
    def pre_emphasis_supported(self, pre_emphasis_supported: bool):
        self._set_by_bitmask(pre_emphasis_supported, 1)

    @property
    def fixed_timing_dut_supported(self) -> bool:
        """
        Set and get Fixed timing DUT flag support.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(2, bool)

    @fixed_timing_dut_supported.setter
    def fixed_timing_dut_supported(self, fixed_timing_dut_supported: bool):
        self._set_by_bitmask(fixed_timing_dut_supported, 2)

    @property
    def spread_spectrum_supported(self) -> bool:
        """
        Set and get Spread Spectrum flag support.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(3, bool)

    @spread_spectrum_supported.setter
    def spread_spectrum_supported(self, spread_spectrum_supported: bool):
        self._set_by_bitmask(spread_spectrum_supported, 3)

    @property
    def change_vf_without_lt_supported(self) -> bool:
        """
        Set and get Video format change without LT flag support.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(4, bool)

    @change_vf_without_lt_supported.setter
    def change_vf_without_lt_supported(self, change_vf_without_lt_supported: bool):
        self._set_by_bitmask(change_vf_without_lt_supported, 4)

    @property
    def e_ddc_protocol_supported(self) -> bool:
        """
        Set and get E-DDC protocol flag support.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(5, bool)

    @e_ddc_protocol_supported.setter
    def e_ddc_protocol_supported(self, e_ddc_protocol_supported: bool):
        self._set_by_bitmask(e_ddc_protocol_supported, 5)

    @property
    def audio_transmission_supported(self) -> bool:
        """
        Set and get Audio Info Frame for 2 channel audio transmission flag support.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(6, bool)

    @audio_transmission_supported.setter
    def audio_transmission_supported(self, audio_transmission_supported: bool):
        self._set_by_bitmask(audio_transmission_supported, 6)

    @property
    def dut_is_type_c_device(self) -> bool:
        """
        Set and get Define that DUT is Type-C device flag support.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(7, bool)

    @dut_is_type_c_device.setter
    def dut_is_type_c_device(self, dut_is_type_c_device: bool):
        self._set_by_bitmask(dut_is_type_c_device, 7)

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

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(8, bool)

    @fec_supported.setter
    def fec_supported(self, fec_supported: bool):
        self._set_by_bitmask(fec_supported, 8)

    @property
    def fec_disable_sequence_supported(self) -> bool:
        """
        Set and get FEC disable sequence flag support.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(9, bool)

    @fec_disable_sequence_supported.setter
    def fec_disable_sequence_supported(self, fec_disable_sequence_supported: bool):
        self._set_by_bitmask(fec_disable_sequence_supported, 9)

    @property
    def audio_without_video_supported(self) -> bool:
        """
        Set and get Audio without Video flag support.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(10, bool)

    @audio_without_video_supported.setter
    def audio_without_video_supported(self, audio_without_video_supported: bool):
        self._set_by_bitmask(audio_without_video_supported, 10)

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

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(11, bool)

    @dsc_supported.setter
    def dsc_supported(self, dsc_supported: bool):
        self._set_by_bitmask(dsc_supported, 11)

    @property
    def dsc_block_prediction_supported(self) -> bool:
        """
        Set and get DSC block prediction flag support.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(12, bool)

    @dsc_block_prediction_supported.setter
    def dsc_block_prediction_supported(self, dsc_block_prediction_supported: bool):
        self._set_by_bitmask(dsc_block_prediction_supported, 12)

    @property
    def max_link_bandwidth_policy_supported(self):
        """
        Set and get Maximum link bandwidth policy flag support.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(13, bool)

    @max_link_bandwidth_policy_supported.setter
    def max_link_bandwidth_policy_supported(self, max_link_bandwidth_policy_supported: bool):
        self._set_by_bitmask(max_link_bandwidth_policy_supported, 13)

    @property
    def use_3tap_conversion(self):
        """
        Set and get 3TAP conversion flag using.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(14, bool)

    @use_3tap_conversion.setter
    def use_3tap_conversion(self, use_3tap_filter: bool):
        self._set_by_bitmask(use_3tap_filter, 14)

    @property
    def usb4_tunnel_presented(self):
        """
        Set and get USB4 tunnel presented flag.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(15, bool)

    @usb4_tunnel_presented.setter
    def usb4_tunnel_presented(self, usb4_tunnel_presented: bool):
        self._set_by_bitmask(usb4_tunnel_presented, 15)

    @property
    def native_display_id_read(self) -> bool:
        """
        Set and get DUT supports native Display ID read flag support.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(16, bool)

    @native_display_id_read.setter
    def native_display_id_read(self, native_display_id_read: bool):
        self._set_by_bitmask(native_display_id_read, 16)

    @property
    def display_id_vii_supported(self) -> bool:
        """
        Set and get DisplayID Type VII Detailed Timing Descriptor flag support.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(17, bool)

    @display_id_vii_supported.setter
    def display_id_vii_supported(self, display_id_vii_supported: bool):
        self._set_by_bitmask(display_id_vii_supported, 17)

    @property
    def display_id_viii_supported(self) -> bool:
        """
        Set and get DisplayID Type VIII Detailed Timing Descriptor flag support.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(18, bool)

    @display_id_viii_supported.setter
    def display_id_viii_supported(self, display_id_viii_supported: bool):
        self._set_by_bitmask(display_id_viii_supported, 18)


    @property
    def display_id_ix_supported(self) -> bool:
        """
        Set and get DisplayID Type IX Detailed Timing Descriptor flag support.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(19, bool)

    @display_id_ix_supported.setter
    def display_id_ix_supported(self, display_id_ix_supported: bool):
        self._set_by_bitmask(display_id_ix_supported, 19)

    @property
    def display_id_x_supported(self) -> bool:
        """
        Set and get DisplayID Type X Detailed Timing Descriptor flag support.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(20, bool)

    @display_id_x_supported.setter
    def display_id_x_supported(self, display_id_x_supported: bool):
        self._set_by_bitmask(display_id_x_supported, 20)

    @property
    def display_id_tiled_display_topology(self) -> bool:
        """
        Set and get 2x1 tiled display and DisplayID Tiled Display Topology data block flag support.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(21, bool)

    @display_id_tiled_display_topology.setter
    def display_id_tiled_display_topology(self, display_id_tiled_display_topology: bool):
        self._set_by_bitmask(display_id_tiled_display_topology, 21)

    @property
    def display_id_tiled_stereo_display(self) -> bool:
        """
        Set and get Field sequential stereo and DisplayID Tiled Stereo Display Interface data block flag support.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(22, bool)

    @display_id_tiled_stereo_display.setter
    def display_id_tiled_stereo_display(self, display_id_tiled_stereo_display: bool):
        self._set_by_bitmask(display_id_tiled_stereo_display, 22)

    @property
    def stacked_frame_stereo_supported(self) -> bool:
        """
        Set and get Stacked frame stereo and DisplayID Tiled Stereo Display Interface data flag support.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(23, bool)

    @stacked_frame_stereo_supported.setter
    def stacked_frame_stereo_supported(self, stacked_frame_stereo_supported: bool):
        self._set_by_bitmask(stacked_frame_stereo_supported, 23)

    @property
    def dynamic_refresh_rate_support(self) -> bool:
        """
        Set and get Dynamic Refresh Rate with VBlank stretch with MSA_TIMING_PAR_IGNORED flag supported.

        Returns:
            object of bool type
        """
        return self._get_by_bitmask(24, bool)

    @dynamic_refresh_rate_support.setter
    def dynamic_refresh_rate_support(self, dynamic_refresh_rate_support: bool):
        self._set_by_bitmask(dynamic_refresh_rate_support, 24)


class DutCapsDp21:
    """
    Class `DutCapsDp21` defines the DUT capabilities and allows setting:
    - Defines the maximum number of lanes supported by the DUT `max_lanes`.
    - Maximum link rate supported by the DUT `max_link_rate`.
    - Dut capabilities flags `dut_caps_flags` type `DutCapsFlags`.
    """
    def __init__(self, json_obj):
        self.__max_lanes = Param(json_obj["TSI_DP20_SRCCTS_MAX_LANES"])
        self.__max_link_rate = Param(json_obj["TSI_DP20_SRCCTS_MAX_LINK_RATE"])
        self.__dut_caps_flags = DutCapsDp21Flags(json_obj["TSI_DP20_SRCCTS_DUT_CAPS"])

    @property
    def max_lanes(self) -> int:
        """
        Set and get number of maximum lanes.

        Returns:
            object of int type
        """
        return self.__max_lanes.default_value

    @max_lanes.setter
    def max_lanes(self, max_lanes: int):
        if max_lanes not in [1, 2, 4]:
            raise ValueError(f"Max lane count must be from list: 1, 2, 4. Current value: {max_lanes}")
        self.__max_lanes.default_value = max_lanes

    @property
    def max_link_rate(self):
        """
        Set and get number of maximum link rate.

        Returns:
            object of int type
        """
        return self.__max_link_rate

    @max_link_rate.setter
    def max_link_rate(self, max_link_rate: float):
        if max_link_rate not in [1.62, 2.7, 5.4, 8.1]:
            raise ValueError(f"Max link rate must be from list: 1.62, 2.7, 5.4, 8.1. Current value: {max_link_rate}")
        self.__max_link_rate.default_value = round(max_link_rate / 0.27)

    @property
    def dut_caps_flags(self) -> DutCapsDp21Flags:
        """
        Set and get DUT capabilities flags.

        Returns:
            object of `DutCapsFlags` type
        """
        return self.__dut_caps_flags

    @dut_caps_flags.setter
    def dut_caps_flags(self, dut_caps_flags: DutCapsDp21Flags):
        self.__dut_caps_flags = dut_caps_flags


class DebugOptions:

    def __init__(self, json_obj):
        self.__param = Param(json_obj["TSI_DP20_SRCCTS_DUT_DEBUG_CONF"])

    @property
    def continue_on_fail(self) -> bool:
        """
        Set and get continue on fail flag.

        Returns:
            object of bool type
        """
        return self.__param._get_by_bitmask(0, bool)

    @continue_on_fail.setter
    def continue_on_fail(self, continue_on_fail: bool):
        self.__param.default_value = update_default_value(value=continue_on_fail,
                                                          default_value=self.__param.default_value,
                                                          mask=self.__param.bit_field_list[0].mask)

    @property
    def force_visual_check(self) -> bool:
        """
        Set and get Force manual visual check flag.

        Returns:
            object of bool type
        """
        return self.__param._get_by_bitmask(1, bool)

    @force_visual_check.setter
    def force_visual_check(self, force_visual_check: bool):
        self.__param.default_value = update_default_value(value=force_visual_check,
                                                          default_value=self.__param.default_value,
                                                          mask=self.__param.bit_field_list[1].mask)


class GeneralSourceDUTDp21SettingTab:
    """
    Class `GeneralSourceDUTDp21SettingTab` inherited of class`GeneralSourceDUTDp14SettingTab` allows working with
    parameters from General source part.
    - Set and get timeout `timeout`.
    - Set and get `hpd_pulse_duration`. Describes duration of long HPD pulses generated, in milliseconds.
    - Set and get DUT capabilities `dut_caps` type `DutCapsDp21`.
    - Set and get DUT link rates `dut_link_rates` type `LinkRateDp21`.
    - Set and get LTTPR device count `lttpr_device_count`.
    - Set and get maximum link bandwidth policy `max_link_bw_policy` type `MaxLinkBwPolicy`.
    - Set and get DSC maximum slice number `dsc_max_slice`.
    - Set and get DSC version `dsc_version`.
    - Set and get debug options `debug_options` type `DebugOptions`
    """
    def __init__(self, json_obj):
        self.__timeout = Param(json_obj["TSI_DP20_SRCCTS_TIMEOUT"])
        self.__dut_caps = DutCapsDp21(json_obj)
        self.__test_automation = TestAutomationFlagsDP21(json_obj["TSI_DP20_SRCCTS_DUT_TA"])
        self.__hpd_pulse_duration = Param(json_obj["TSI_DP20_SRCCTS_LONG_HPD_PULSE"])
        self.__dut_link_rates = LinkRateDp21(json_obj["TSI_DP20_SRCCTS_DUT_LINK_RATES"])
        self.__lttpr_device_count = Param(json_obj["TSI_DP20_SRCCTS_LTTPR_DEVICE_COUNT"])
        self.__max_link_bw_policy = Param(json_obj["TSI_DP20_SRCCTS_MAX_LINK_BW_POLICY"])  # MaxLinkBwPolicy
        self.__dsc_max_slice = Param(json_obj["TSI_DP20_SRCCTS_DSC_DUT_MAX_SLICE"])
        self.__dsc_version = Param(json_obj["TSI_DP20_SRCCTS_DSC_VERSION"])
        self.__debug_options = DebugOptions(json_obj)

    @property
    def timeout(self) -> int:
        """
        Set and get test timeout, in milliseconds.

        Returns:
            object of int type
        """
        return self.__timeout.default_value

    @timeout.setter
    def timeout(self, timeout: int):
        if not(self.__timeout.min_value < timeout < self.__timeout.max_value):
            raise ValueError(f"Timeout cannot be less than {self.__timeout.min_value} and more than "
                             f"{self.__timeout.max_value}.")
        self.__timeout.default_value = timeout

    @property
    def dut_caps(self) -> DutCapsDp21:
        """
        Set and get DUT caps.

        Returns:
            object DutCapsDp21
        """
        return self.__dut_caps

    @dut_caps.setter
    def dut_caps(self, dut_caps: DutCapsDp21):
        self.__dut_caps = dut_caps

    @property
    def hpd_pulse_duration(self) -> int:
        """
        Set and get HPD pulse duration.

        Returns:
            object of int type
        """
        return self.__hpd_pulse_duration.default_value

    @hpd_pulse_duration.setter
    def hpd_pulse_duration(self, hpd_pulse_duration: int):
        if not(self.__hpd_pulse_duration.min_value < hpd_pulse_duration < self.__hpd_pulse_duration.max_value):
            raise ValueError(f"HPD pulse duration cannot be less than {self.__timeout.min_value} and more than "
                             f"{self.__hpd_pulse_duration.max_value}.")
        self.__hpd_pulse_duration.default_value = hpd_pulse_duration

    @property
    def test_automation(self) -> TestAutomationFlagsDP21:
        """
        Set and get test automation flags.

        Returns:
            object TestAutomationFlags
        """
        return self.__test_automation

    @test_automation.setter
    def test_automation(self, test_automation: TestAutomationFlagsDP21):
        self.__test_automation = test_automation

    @property
    def dut_link_rates(self) -> LinkRateDp21:
        """
        Set and get DUT link rates.

        Returns:
            object LinkRateDp21
        """
        return self.__dut_link_rates

    @dut_link_rates.setter
    def dut_link_rates(self, dut_link_rates: LinkRateDp21):
        self.__dut_link_rates = dut_link_rates

    @property
    def max_link_bw_policy(self) -> int:
        """
        Set and get maximum link bandwidth policy.

        Returns:
            object int
        """
        return self.__max_link_bw_policy.default_value

    @max_link_bw_policy.setter
    def max_link_bw_policy(self, max_link_bw_policy: MaxLinkBwPolicy):
        self.__max_link_bw_policy = max_link_bw_policy.value

    @property
    def lttpr_device_count(self) -> int:
        """
        Set and get LTTPR device count.

        Returns:
            object int
        """
        return self.__lttpr_device_count.default_value

    @lttpr_device_count.setter
    def lttpr_device_count(self, lttpr_device_count: int):
        if not(self.__lttpr_device_count.min_value < lttpr_device_count < self.__lttpr_device_count.max_value):
            raise ValueError(f"LTTPR device count cannot be less than "
                             f"{self.__lttpr_device_count.min_value} and more than "
                             f"{self.__lttpr_device_count.max_value}.")
        self.__lttpr_device_count.default_value = lttpr_device_count

    @property
    def dsc_max_slice(self) -> int:
        """
        Set and get DSC maximum slice number.

        Returns:
            object of int type
        """
        return self.__dsc_max_slice.default_value

    @dsc_max_slice.setter
    def dsc_max_slice(self, dsc_max_slice: int):
        if dsc_max_slice not in [1, 2, 4, 8, 10, 12, 16, 20, 24]:
            raise ValueError(f"DSC Max slice count available values {[1, 2, 4, 8, 10, 12, 16, 20, 24]}")
        self.__dsc_max_slice.default_value = dsc_max_slice

    @property
    def dsc_version(self) -> list:
        """
        Set and get DSC version.
        Setter: .dsc_version((1, 2))

        Returns:
            object of list type
        """
        return [self.__dsc_version.default_value >> 16 & 0xF, self.__dsc_version.default_value & 0xF]

    @dsc_version.setter
    def dsc_version(self, dsc_version: tuple):
        version = (dsc_version[0] << 16) | dsc_version[1]
        if version not in [65537, 65538]:
            raise ValueError("DSC Version must be 1.1 or 1.2")
        self.__dsc_version.default_value = version

    @property
    def debug_options(self) -> DebugOptions:
        """
        Set and get debug options.

        Returns:
            object of 'DebugOptions' type
        """
        return self.__debug_options

    @debug_options.setter
    def debug_options(self, debug_config: DebugOptions):
        self.__debug_options = debug_config

