from enum import IntEnum


class DscCompressionInfo:
    """

    Class contains information about DSC compression used on frame.

    """
    class DscColorFormat(IntEnum):
        """
        Contains values of possible color format.
        """
        CF_NONE = -1
        CF_RGB = 0
        CF_YCbCr_422 = 1
        CF_YCbCr_444 = 2
        CF_YCbCr_420 = 3
        CF_Simple_422 = 4

    def __init__(self):
        self.color_format = DscCompressionInfo.DscColorFormat.CF_NONE
        self.bpp = 0
        self.is_block_prediction_enabled = False
        self.h_slice_size = 0
        self.v_slice_size = 0
        self.buffer_bit_depth = 0
        self.version = (0, 0)
        self.is_simple_as_444 = False

    def is_valid(self) -> bool:
        """
        Return state of the video frame and check color_format, bpp, h and v slice_size and DSC version.
        If everything ok, return True, otherwise - False.

        Returns:
            object of `bool` type.
        """
        return self.color_format is not None and\
               self.bpp > 0 and\
               self.h_slice_size > 0 and\
               self.v_slice_size > 0 and\
               self.version in [(1, 2), (1, 1)]


def create_from_pps(pps_bytearray: bytearray) -> DscCompressionInfo:
    """
    Fill structure 'DscCompressionInfo' from PPS header of the DSC image.

    Returns:
        object of `DscCompressionInfo` type.
    """
    if b'DSCF' not in pps_bytearray:
        if len(pps_bytearray) < 128:
            raise ValueError("Incorrect PPS size!")
        pps = pps_bytearray
    else:
        if len(pps_bytearray) < 132:
            raise ValueError("Incorrect PPS size!")
        pps = pps_bytearray[4:]

    is_yuv = ((pps[4] >> 4) & 1) == 0
    is_simple422 = ((pps[4] >> 3) & 1) == 1
    is_native422 = (pps[88] & 1) == 1
    is_native420 = ((pps[88] >> 1) & 1) == 1
    width = (pps[8] << 8) | pps[9]
    height = (pps[6] << 8) | pps[7]
    slice_height = (pps[10] << 8) | pps[11]
    slice_width = (pps[12] << 8) | pps[13]

    info = DscCompressionInfo()

    info.version = (pps[0] >> 4 & 0xf, pps[0] & 0xf)
    info.buffer_bit_depth = pps[3] & 0xf
    info.is_block_prediction_enabled = bool(pps[4] >> 5 & 0x1)
    info.h_slice_size = int(width / slice_width)
    info.v_slice_size = int(height / slice_height)

    if is_native422 or is_native420:
        info.bpp = int(pps[4] & 0x3 << 8 | pps[5]) / 2
    else:
        info.bpp = int(pps[4] & 0x3 << 8 | pps[5])

    if is_yuv:
        if is_simple422:
            info.color_format = DscCompressionInfo.DscColorFormat.CF_Simple_422
        elif is_native422:
            info.color_format = DscCompressionInfo.DscColorFormat.CF_YCbCr_422
        elif is_native420:
            info.color_format = DscCompressionInfo.DscColorFormat.CF_YCbCr_420
        else:
            info.color_format = DscCompressionInfo.DscColorFormat.CF_YCbCr_444
    else:
        info.color_format = DscCompressionInfo.DscColorFormat.CF_RGB

    return info
