from UniTAP.common.timestamp import Timestamp
from ctypes import c_uint8, c_ubyte, c_uint16, c_uint32, c_uint64, Structure
from enum import IntEnum
from UniTAP.common import ColorInfo, DataInfo, VideoFrame


class CapturedDataType(IntEnum):
    Unknown = 0
    Video = 1
    Audio = 2
    Event = 3
    Bulk = 4


class CapturedData:

    def __init__(self):
        self.frame_number = 0
        self.timestamp = 0
        self.width = 0
        self.height = 0
        self.dataFormat = 0
        self.colorimetry = 0
        self.colorMode = 0
        self.bpc = 0
        self.type = 0
        self.data = 0


class CrcStruct(Structure):
    _fields_ = [
        ("r", c_uint16),
        ("g", c_uint16),
        ("b", c_uint16),
    ]


class VideoFrameHeader(Structure):
    __BPC_PA_TO_INT = {
        0: 6,
        1: 8,
        2: 10,
        3: 12,
        4: 16,
        5: 7,
        6: 14
    }

    __PA_PACKING_TO_VF_PACKING = {
        0: DataInfo.Packing.P_PLANAR,
        1: DataInfo.Packing.P_PLANAR
    }

    __PA_CF_TO_CI_CF = {
        0: ColorInfo.ColorFormat.CF_RGB,
        1: ColorInfo.ColorFormat.CF_YCbCr_422,
        2: ColorInfo.ColorFormat.CF_YCbCr_444,
        3: ColorInfo.ColorFormat.CF_YCbCr_420,
        4: ColorInfo.ColorFormat.CF_Y_ONLY,
        5: ColorInfo.ColorFormat.CF_RAW,
        6: ColorInfo.ColorFormat.CF_DSC,
        7: ColorInfo.ColorFormat.CF_IDO_DEFINED
    }
    __PA_CR_TO_CI_CR = {
        0: ColorInfo.Colorimetry.CM_NONE,
        1: ColorInfo.Colorimetry.CM_SMPTE_170M,
        2: ColorInfo.Colorimetry.CM_ITUR_BT709,
        3: ColorInfo.Colorimetry.CM_NONE
    }

    __PA_EXT_CR_TO_CI_CR = {
        0: ColorInfo.Colorimetry.CM_xvYCC601,
        1: ColorInfo.Colorimetry.CM_xvYCC709,
        2: ColorInfo.Colorimetry.CM_sYCC601,
        3: ColorInfo.Colorimetry.CM_AdobeYCC601,
        4: ColorInfo.Colorimetry.CM_AdobeRGB,
        5: ColorInfo.Colorimetry.CM_ITUR_BT2020_YcCbcCrc,
        6: ColorInfo.Colorimetry.CM_ITUR_BT2020_RGB,
        7: ColorInfo.Colorimetry.CM_ITUR_BT601
    }

    class PixelAttributes(Structure):
        _fields_ = [
            ("component_format", c_uint32, 4),
            ("", c_uint32, 1),
            ("bpc", c_uint32, 3),
            ("video_mode", c_uint32, 1),
            ("stereo_mode", c_uint32, 1),
            ("field_id", c_uint32, 1),
            ("stereo_id", c_uint32, 1),
            ("blanked", c_uint32, 1),
            ("encrypted", c_uint32, 1),
            ("pixel_packing_format", c_uint32, 2),
            ("color_format", c_uint32, 3),
            ("dynamic_range", c_uint32, 1),
            ("colorimetry", c_uint32, 2),
            ("ext_colorimetry", c_uint32, 3),
            ("", c_uint32, 1),
            ("dsc_compressed", c_uint32, 1),
            ("ext_color_format", c_uint32, 2),
            ("video_footer", c_uint32, 1),
            ("video_packet_crc", c_uint32, 1),
        ]

    _fields_ = [
        ('sync', c_uint32),
        ('number', c_uint32),
        ('', c_uint32),
        ('size_words', c_uint32),
        ('frame_timestamp', c_uint64, 62),
        ('time_unit', c_uint64, 2),
        ('attributes', PixelAttributes),
        ("f_width", c_uint32, 16),
        ("f_height", c_uint32, 16)
    ]

    @property
    def bpc(self) -> int:
        return self.__BPC_PA_TO_INT.get(self.attributes.bpc, 0)

    @property
    def dynamic_range(self) -> ColorInfo.DynamicRange:
        if self.attributes.dynamic_range:
            return ColorInfo.DynamicRange.DR_CTA
        else:
            return ColorInfo.DynamicRange.DR_VESA

    @property
    def color_format(self) -> ColorInfo.ColorFormat:
        return self.__PA_CF_TO_CI_CF.get(self.attributes.color_format,
                                         ColorInfo.ColorFormat.CF_UNKNOWN)

    @property
    def colorimetry(self) -> ColorInfo.Colorimetry:
        if self.attributes.colorimetry == 3:
            return self.__PA_EXT_CR_TO_CI_CR.get(self.attributes.ext_colorimetry,
                                                 ColorInfo.Colorimetry.CM_NONE)
        else:
            return self.__PA_CR_TO_CI_CR.get(self.attributes.colorimetry,
                                             ColorInfo.Colorimetry.CM_NONE)

    @property
    def timestamp(self) -> Timestamp:
        value = self.frame_timestamp
        value &= 0x7FFFFFFFFFFFFFFF
        value *= 100 if (self.time_unit & 0x1) else 1000
        return Timestamp(value)

    @property
    def packing(self) -> DataInfo.Packing:
        return self.__PA_PACKING_TO_VF_PACKING.get(self.attributes.pixel_packing_format,
                                                   DataInfo.Packing.P_UNKNOWN)

    def is_dsc(self) -> bool:
        return self.attributes.color_format == 6

    @property
    def width(self):
        if self.color_format == ColorInfo.ColorFormat.CF_YCbCr_420:
            return self.f_width << 1
        else:
            return self.f_width

    @property
    def height(self):
        return self.f_height


class CaptureError(Exception):

    def __init__(self, message: str):
        self.__message = message
        super().__init__(self.__message)


class BufferedCaptureError(Exception):

    def __init__(self, message: str):
        self.__message = message
        super().__init__(self.__message)