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


class CrcVideoTestBpp(IntEnum):
    BPP12 = 12
    BPP15 = 15
    BPP16 = 16
    BPP18 = 18
    BPP20 = 20
    BPP21 = 21
    BPP24 = 24
    BPP30 = 30
    BPP32 = 32
    BPP36 = 36
    BPP48 = 48


class BrokenFrameExportFormat(IntEnum):
    BIN = 0
    PPM = 1
    BMP = 2


class CrcVideoTestParam:
    """
    Class `CrcVideoTestParam` describes requirement parameters for CRC tests:
    - Set and get `timeout`. Describes test timeout, in milliseconds.
    - Set and get `number_frames_to_test`. Describes total number of frames to be tested.
    - Set and get `number_reference_frames`. Describes number of reference frames.
    - Set and get `number_frames_mismatch`. Describes number of bad frames allowed in single CRC tests.
    - Set and get `reference_width`. Describes expected video width, in pixels.
    - Set and get `reference_height`. Describes expected video weight, in pixels.
    - Set and get `reference_color_depth`. Describes expected color depth, as bits per pixel `CrcVideoTestBpp`.
    - Set and get `required_frame_rate`. Describes expected frame rate, in millihertz (mHz).
    - Set and get `frame_rate_tolerance`. Describes frame rate tolerance, in millihertz (mHz).
    - Set and get `reference_crc_values`. Describes CRC reference values. Each CRC set consists of 3 16-bit words.
    - Set and get `motion_test_iteration`. Describes the number of iterations the defined CRC sequence must
    be found in order to pass the test.
    - Set and get `data_transfer_timeout`. Describes data transfer timeout in milliseconds.
    - Set and get `failed_frames_folder`. Describes location where the failed frames are to be saved.
    - Set and get `max_export_failed`. Describes the number of failed frames to be exported from the video test.
    """
    def __init__(self, json_obj):
        self.__timeout = Param(json_obj["TSI_CRC_TIMEOUT"])
        self.__number_frames_to_test = Param(json_obj["TSI_CRC_FRAMES_TO_TEST"])
        self.__number_reference_frames = Param(json_obj["TSI_CRC_REF_FRAME_COUNT"])
        self.__number_frames_mismatch = Param(json_obj["TSI_CRC_LIM_FRAME_MISMATCHES"])
        self.__reference_width = Param(json_obj["TSI_CRC_REF_WIDTH"])
        self.__reference_height = Param(json_obj["TSI_CRC_REF_HEIGHT"])
        self.__reference_color_depth = Param(json_obj["TSI_CRC_REF_COLORDEPTH"])  # CrcVideoTestBpp
        self.__required_frame_rate = Param(json_obj["TSI_CRC_REQUIRED_FRAME_RATE"])
        self.__frame_rate_tolerance = Param(json_obj["TSI_CRC_FRAME_RATE_TOLERANCE"])
        self.__reference_crc_values = Param(json_obj["TSI_CRC_REFERENCE_CRC_VALUES"])
        self.__motion_test_iteration = Param(json_obj["TSI_CRC_MOTION_TEST_ITERATIONS"])
        self.__color_format = Param(json_obj["TSI_CRC_COLOR_FORMAT"])  # Do not provide. Must be 0
        self.__data_transfer_timeout = Param(json_obj["TSI_CRC_DATA_TRANSFER_TIMEOUT"])
        self.__failed_frames_folder = Param(json_obj["TSI_CRC_FAILED_FRAME_TARGET_FOLDER"])
        self.__max_export_failed = Param(json_obj["TSI_CRC_MAX_EXPORT_FAILED"])
        self.__export_format = Param(json_obj["TSI_CRC_EXPORT_FORMAT"])

    @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 number_frames_to_test(self) -> int:
        """
        Set and get total number of frames to be tested.

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

    @number_frames_to_test.setter
    def number_frames_to_test(self, number_frames_to_test: int):
        if not(self.__number_frames_to_test.min_value < number_frames_to_test < self.__number_frames_to_test.max_value):
            raise ValueError(f"Number frames to test cannot be less than {self.__number_frames_to_test.min_value} "
                             f"and more than {self.__number_frames_to_test.max_value}.")
        self.__number_frames_to_test.default_value = number_frames_to_test

    @property
    def number_reference_frames(self) -> int:
        """
        Set and get number of reference frames.

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

    @number_reference_frames.setter
    def number_reference_frames(self, number_reference_frames: int):
        if not(self.__number_reference_frames.min_value < number_reference_frames <
               self.__number_reference_frames.max_value):
            raise ValueError(f"Number reference frames cannot be less than {self.__number_reference_frames.min_value} "
                             f"and more than {self.__number_reference_frames.max_value}.")
        self.__number_reference_frames.default_value = number_reference_frames

    @property
    def number_frames_mismatch(self) -> int:
        """
        Set and get number of bad frames allowed in single CRC tests.

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

    @number_frames_mismatch.setter
    def number_frames_mismatch(self, number_frames_mismatch: int):
        if not(self.__number_frames_mismatch.min_value <= number_frames_mismatch <
               self.__number_frames_mismatch.max_value):
            raise ValueError(f"Number frames mismatch cannot be less than {self.__number_frames_mismatch.min_value} "
                             f"and more than {self.__number_frames_mismatch.max_value}.")
        self.__number_frames_mismatch.default_value = number_frames_mismatch

    @property
    def reference_width(self) -> int:
        """
        Set and get expected video width, in pixels.

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

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

    @property
    def reference_height(self) -> int:
        """
        Set and get expected video weight, in pixels.

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

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

    @property
    def reference_color_depth(self) -> int:
        """
        Set and get expected color depth, as bits per pixel `CrcVideoTestBpp`.

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

    @reference_color_depth.setter
    def reference_color_depth(self, reference_color_depth: CrcVideoTestBpp):
        self.__reference_color_depth.default_value = reference_color_depth.value

    @property
    def required_frame_rate(self) -> int:
        """
        Set and get expected frame rate, in millihertz (mHz).

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

    @required_frame_rate.setter
    def required_frame_rate(self, required_frame_rate: int):
        if not(self.__required_frame_rate.min_value <= required_frame_rate < self.__required_frame_rate.max_value):
            raise ValueError(f"Required frame rate cannot be less than {self.__required_frame_rate.min_value} "
                             f"and more than {self.__required_frame_rate.max_value}.")
        self.__required_frame_rate.default_value = required_frame_rate

    @property
    def frame_rate_tolerance(self) -> int:
        """
        Set and get frame rate tolerance, in millihertz (mHz).

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

    @frame_rate_tolerance.setter
    def frame_rate_tolerance(self, frame_rate_tolerance: int):
        if not(self.__frame_rate_tolerance.min_value <= frame_rate_tolerance < self.__frame_rate_tolerance.max_value):
            raise ValueError(f"Frame rate tolerance cannot be less than {self.__frame_rate_tolerance.min_value} "
                             f"and more than {self.__frame_rate_tolerance.max_value}.")
        self.__frame_rate_tolerance.default_value = frame_rate_tolerance

    @property
    def reference_crc_values(self) -> list:
        """
        Set and get CRC reference values. Each CRC set consists of 3 16-bit words.

        Returns:
            object of list type
        """
        return self.__reference_crc_values.default_value

    @reference_crc_values.setter
    def reference_crc_values(self, reference_crc_values: list):
        if len(reference_crc_values) <= 0:
            raise ValueError(f"Length of reference crc list must be more that 0")
        self.__reference_crc_values.default_value = reference_crc_values

    @property
    def motion_test_iteration(self) -> int:
        """
        Set and get number of iterations the defined CRC sequence must be found in order to pass the test.

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

    @motion_test_iteration.setter
    def motion_test_iteration(self, motion_test_iteration: int):
        if not(self.__motion_test_iteration.min_value <= motion_test_iteration < self.__motion_test_iteration.max_value):
            raise ValueError(f"Motion test iteration cannot be less motion_test_iteration "
                             f"{self.__motion_test_iteration.min_value} and more than "
                             f"{self.__motion_test_iteration.max_value}.")
        self.__motion_test_iteration.default_value = motion_test_iteration

    @property
    def data_transfer_timeout(self) -> int:
        """
        Set and get data transfer timeout in milliseconds.

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

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

    @property
    def failed_frames_folder(self) -> str:
        """
        Set and get location where the failed frames are to be saved.

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

    @failed_frames_folder.setter
    def failed_frames_folder(self, failed_frames_folder: str):
        if len(failed_frames_folder) <= 0:
            raise ValueError(f"Path length of folder list must be more that 0")
        self.__failed_frames_folder.default_value = failed_frames_folder

    @property
    def max_export_failed(self) -> int:
        """
        Set and get number of failed frames to be exported from the video test.

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

    @max_export_failed.setter
    def max_export_failed(self, max_export_failed: int):
        if not(self.__max_export_failed.min_value <= max_export_failed < self.__max_export_failed.max_value):
            raise ValueError(f"Number of max export failed frames cannot be less than "
                             f"{self.__max_export_failed.min_value} and more than "
                             f"{self.__max_export_failed.max_value}.")
        self.__max_export_failed.default_value = max_export_failed

    @property
    def export_format(self) -> BrokenFrameExportFormat:
        """
        Set and get crc failed frame file format.

        Returns:
            object of int type
        """
        return BrokenFrameExportFormat(self.__export_format.default_value)

    @export_format.setter
    def export_format(self, export_format: BrokenFrameExportFormat):
        self.__export_format.default_value = export_format.value
