from UniTAP.common import VideoMode, VideoFrame, ColorInfo, Timing, ImageFileFormat, DataInfo
from .uicl_types import UICL_Image, UICL_ImageParameters, UICL_Colorimetry, UICL_Sampling,\
    UICL_Colorspace, UICL_Packing, UICL_Alignment, UICL_ComponentOrder, UICL_SUCCESS,\
    UICL_ImageFileFormat
from .uicl import UICL_Convert, UICL_GetRequiredBufferSize, UICL_SaveToFile, UICL_CRC16, UICL_CalculateCRC16

from typing import Tuple
from ctypes import c_uint8


def uicl_cf_from_vm(color_format: ColorInfo.ColorFormat) -> UICL_Colorspace:
    if color_format == ColorInfo.ColorFormat.CF_RGB:
        return UICL_Colorspace.Colorspace_RGB
    elif color_format in [ColorInfo.ColorFormat.CF_YCbCr_444, ColorInfo.ColorFormat.CF_YCbCr_422,
                          ColorInfo.ColorFormat.CF_YCbCr_420]:
        return UICL_Colorspace.Colorspace_YCbCr
    else:
        return UICL_Colorspace.Colorspace_Unknown


def uicl_sampling_from_vm(color_format: ColorInfo.ColorFormat) -> UICL_Sampling:
    if color_format in [ColorInfo.ColorFormat.CF_RGB, ColorInfo.ColorFormat.CF_YCbCr_444]:
        return UICL_Sampling.Sampling_444
    elif color_format == ColorInfo.ColorFormat.CF_YCbCr_422:
        return UICL_Sampling.Sampling_422
    elif color_format == ColorInfo.ColorFormat.CF_YCbCr_420:
        return UICL_Sampling.Sampling_420
    else:
        return UICL_Sampling.Sampling_Unknown


def uicl_colorimetry_from_ci_colorimetry(colorimetry: ColorInfo.Colorimetry) -> UICL_Colorimetry:
    if colorimetry == ColorInfo.Colorimetry.CM_ITUR_BT601:
        return UICL_Colorimetry.Colorimetry_ITU_R_BT601
    elif colorimetry == ColorInfo.Colorimetry.CM_ITUR_BT709:
        return UICL_Colorimetry.Colorimetry_ITU_R_BT709
    elif colorimetry == ColorInfo.Colorimetry.CM_ITUR_BT2020_YCbCr:
        return UICL_Colorimetry.Colorimetry_ITU_R_BT2020
    else:
        return UICL_Colorimetry.Colorimetry_Unknown


def uicl_colorimetry_to_ci_colorimetry(colorimetry: UICL_Colorimetry) -> ColorInfo.Colorimetry:
    if colorimetry == UICL_Colorimetry.Colorimetry_ITU_R_BT601:
        return ColorInfo.Colorimetry.CM_ITUR_BT601
    elif colorimetry == UICL_Colorimetry.Colorimetry_ITU_R_BT709:
        return ColorInfo.Colorimetry.CM_ITUR_BT709
    elif colorimetry == UICL_Colorimetry.Colorimetry_ITU_R_BT2020:
        return ColorInfo.Colorimetry.CM_ITUR_BT2020_YCbCr
    else:
        return ColorInfo.Colorimetry.CM_NONE


def uicl_parameters_from_vm(vm: VideoMode) -> UICL_ImageParameters:
    parameters = UICL_ImageParameters()

    parameters.Width = vm.timing.hactive
    parameters.Height = vm.timing.vactive
    parameters.BitsPerColor = vm.color_info.bpc
    parameters.Colorimetry = uicl_colorimetry_from_ci_colorimetry(vm.color_info.colorimetry)

    if vm.color_info.color_format == ColorInfo.ColorFormat.CF_RGB:
        parameters.Colorspace = UICL_Colorspace.Colorspace_RGB
        parameters.ComponentOrder = UICL_ComponentOrder.Order_RGB
        parameters.Sampling = UICL_Sampling.Sampling_444
        parameters.Packing = UICL_Packing.Packing_Packed
        parameters.Alignment = UICL_Alignment.Alignment_LSB
        parameters.IsFullRange = vm.color_info.DynamicRange == ColorInfo.DynamicRange.DR_VESA

    elif vm.color_info.color_format == ColorInfo.ColorFormat.CF_YCbCr_444:
        parameters.Colorspace = UICL_Colorspace.Colorspace_YCbCr
        parameters.ComponentOrder = UICL_ComponentOrder.Order_YCbCr
        parameters.Sampling = UICL_Sampling.Sampling_444
        parameters.Packing = UICL_Packing.Packing_Packed
        parameters.Alignment = UICL_Alignment.Alignment_LSB
        parameters.IsFullRange = False

    elif vm.color_info.color_format == ColorInfo.ColorFormat.CF_YCbCr_422:
        parameters.Colorspace = UICL_Colorspace.Colorspace_YCbCr
        parameters.ComponentOrder = UICL_ComponentOrder.Order_CbY0CrY1
        parameters.Sampling = UICL_Sampling.Sampling_422
        parameters.Packing = UICL_Packing.Packing_Packed
        parameters.Alignment = UICL_Alignment.Alignment_LSB
        parameters.IsFullRange = False

    elif vm.color_info.color_format == ColorInfo.ColorFormat.CF_YCbCr_420:
        parameters.Colorspace = UICL_Colorspace.Colorspace_YCbCr
        parameters.ComponentOrder = UICL_ComponentOrder.Order_YCbCr
        parameters.Sampling = UICL_Sampling.Sampling_420
        parameters.Packing = UICL_Packing.Packing_Planar
        parameters.Alignment = UICL_Alignment.Alignment_LSB
        parameters.IsFullRange = False

    return parameters


def uicl_image_from_vm_and_data(video_mode: VideoMode, data: bytearray) -> UICL_Image:
    image = UICL_Image()

    image.DataPtr = (c_uint8 * len(data))(*data)
    image.DataSize = len(data)
    image.Parameters = uicl_parameters_from_vm(video_mode)

    return image


def uicl_image_convert_to_vm(src_image: UICL_Image, video_mode: VideoMode) -> bytearray:
    dest_image = UICL_Image()

    dest_image.Parameters = uicl_parameters_from_vm(video_mode)
    result = UICL_GetRequiredBufferSize(dest_image)

    assert result >= UICL_SUCCESS, f"Calculation required buffer size failed with error {result}"

    dest_image.DataPtr = (c_uint8 * result)()
    dest_image.DataSize = result

    result = UICL_Convert(src_image, dest_image)

    assert result == UICL_SUCCESS, f"Image conversion failed with error {result}"

    return bytearray(dest_image.DataPtr[:dest_image.DataSize])


def image_from_vf(video_frame: VideoFrame) -> UICL_Image:
    image = UICL_Image()

    image.Parameters = image_params_from_size_and_ci(video_frame.width, video_frame.height, video_frame.color_info,
                                                     video_frame.data_info)
    image.DataSize = len(video_frame.data)
    image.DataPtr = (c_uint8 * len(video_frame.data)).from_buffer(video_frame.data)

    return image


def image_to_vf(image: UICL_Image) -> VideoFrame:
    video_frame = VideoFrame()

    video_frame.width, video_frame.height, video_frame.color_info, video_frame.data_info = image_params_to_size_and_ci(image.Parameters)
    video_frame.data = bytearray(image.DataPtr[:image.DataSize])

    return video_frame


def uicl_image_calculate_crc(src_vf: VideoFrame) -> tuple:
    crc = UICL_CRC16()

    src_image = image_from_vf(src_vf)

    result = UICL_CalculateCRC16(src_image, crc)

    assert result >= UICL_SUCCESS, f"Calculation CRC failed with error {result}"

    return crc.R, crc.G, crc.B


def image_params_to_vm(image_params: UICL_ImageParameters) -> VideoMode:
    pass


def image_params_to_size_and_ci(image_params: UICL_ImageParameters) -> Tuple[int, int, ColorInfo, DataInfo]:
    width = image_params.Width
    height = image_params.Height
    color_info = ColorInfo()

    color_info.bpc = image_params.BitsPerColor
    color_info.colorimetry = uicl_colorimetry_to_ci_colorimetry(image_params.Colorimetry)
    color_info.dynamic_range = ColorInfo.DynamicRange.DR_VESA if image_params.IsFullRange else ColorInfo.DynamicRange.DR_CTA

    if image_params.Colorspace == UICL_Colorspace.Colorspace_RGB:
        color_info.color_format = ColorInfo.ColorFormat.CF_RGB
    elif image_params.Colorspace == UICL_Colorspace.Colorspace_YCbCr:
        if image_params.Sampling == UICL_Sampling.Sampling_444:
            color_info.color_format = ColorInfo.ColorFormat.CF_YCbCr_444
        elif image_params.Sampling == UICL_Sampling.Sampling_422:
            color_info.color_format = ColorInfo.ColorFormat.CF_YCbCr_422
        elif image_params.Sampling == UICL_Sampling.Sampling_420:
            color_info.color_format = ColorInfo.ColorFormat.CF_YCbCr_420
        else:
            color_info.color_format = ColorInfo.ColorFormat.CF_UNKNOWN
    elif image_params.Colorspace == UICL_Colorspace.Colorspace_Y:
        color_info.color_format = ColorInfo.ColorFormat.CF_Y_ONLY
    elif image_params.Colorspace == UICL_Colorspace.Colorspace_Raw:
        color_info.color_format = ColorInfo.ColorFormat.CF_RAW

    data_info = DataInfo()
    data_info.alignment = uicl_alignment_to_vf_alignment(image_params.Alignment)
    data_info.component_order = uicl_com_order_to_vf_com_order(image_params.ComponentOrder)
    data_info.packing = uicl_packing_to_vf_packing(image_params.Packing)

    return width, height, color_info, data_info


def vf_com_order_to_uicl_com_order(component_order: DataInfo.ComponentOrder) -> UICL_ComponentOrder:
    if component_order == DataInfo.ComponentOrder.CO_UCDRX:
        return UICL_ComponentOrder.Order_UCDRX
    elif component_order == DataInfo.ComponentOrder.CO_RGB:
        return UICL_ComponentOrder.Order_RGB
    elif component_order == DataInfo.ComponentOrder.CO_RGBA:
        return UICL_ComponentOrder.Order_RGBA
    elif component_order == DataInfo.ComponentOrder.CO_BGR:
        return UICL_ComponentOrder.Order_BGR
    elif component_order == DataInfo.ComponentOrder.CO_BGRA:
        return UICL_ComponentOrder.Order_BGRA
    elif component_order == DataInfo.ComponentOrder.CO_YCbCr:
        return UICL_ComponentOrder.Order_YCbCr
    elif component_order == DataInfo.ComponentOrder.CO_CbY0CrY1:
        return UICL_ComponentOrder.Order_CbY0CrY1
    else:
        return UICL_ComponentOrder.Order_Unknown


def uicl_com_order_to_vf_com_order(component_order: UICL_ComponentOrder) -> DataInfo.ComponentOrder:
    if component_order == UICL_ComponentOrder.Order_UCDRX:
        return DataInfo.ComponentOrder.CO_UCDRX
    elif component_order == UICL_ComponentOrder.Order_RGB:
        return DataInfo.ComponentOrder.CO_RGB
    elif component_order == UICL_ComponentOrder.Order_RGBA:
        return DataInfo.ComponentOrder.CO_RGBA
    elif component_order == UICL_ComponentOrder.Order_BGR:
        return DataInfo.ComponentOrder.CO_BGR
    elif component_order == UICL_ComponentOrder.Order_BGRA:
        return DataInfo.ComponentOrder.CO_BGRA
    elif component_order == UICL_ComponentOrder.Order_YCbCr:
        return DataInfo.ComponentOrder.CO_YCbCr
    elif component_order == UICL_ComponentOrder.Order_CbY0CrY1:
        return DataInfo.ComponentOrder.CO_CbY0CrY1
    else:
        return DataInfo.ComponentOrder.CO_UNKNOWN


def vf_packing_to_uicl_packing(packing: DataInfo.Packing) -> UICL_Packing:
    if packing == DataInfo.Packing.P_PLANAR:
        return UICL_Packing.Packing_Planar
    elif packing == DataInfo.Packing.P_PACKED:
        return UICL_Packing.Packing_Packed
    else:
        return UICL_Packing.Packing_Unknown


def uicl_packing_to_vf_packing(packing: UICL_Packing) -> DataInfo.Packing:
    if packing == UICL_Packing.Packing_Planar:
        return DataInfo.Packing.P_PLANAR
    elif packing == UICL_Packing.Packing_Packed:
        return DataInfo.Packing.P_PACKED
    else:
        return  DataInfo.Packing.P_UNKNOWN


def vf_alignment_to_uicl_alignment(alignment: DataInfo.Alignment) -> UICL_Alignment:
    if alignment == DataInfo.Alignment.A_LSB:
        return UICL_Alignment.Alignment_LSB
    elif alignment == DataInfo.Alignment.A_MSB:
        return UICL_Alignment.Alignment_MSB
    else:
        return UICL_Alignment.Alignment_Unknown


def uicl_alignment_to_vf_alignment(alignment: UICL_Alignment) -> DataInfo.Alignment:
    if alignment == UICL_Alignment.Alignment_LSB:
        return DataInfo.Alignment.A_LSB
    elif alignment == UICL_Alignment.Alignment_MSB:
        return DataInfo.Alignment.A_MSB
    else:
        return DataInfo.Alignment.A_UNKNOWN


def image_params_from_vm(video_mode: VideoMode) -> UICL_ImageParameters:
    pass


def image_params_from_size_and_ci(width: int, height: int, color_info: ColorInfo,
                                  data_info: DataInfo) -> UICL_ImageParameters:
    parameters = UICL_ImageParameters()

    parameters.Width = width
    parameters.Height = height
    parameters.BitsPerColor = color_info.bpc
    parameters.Colorimetry = uicl_colorimetry_from_ci_colorimetry(color_info.colorimetry)
    parameters.ComponentOrder = vf_com_order_to_uicl_com_order(data_info.component_order)
    parameters.Packing = vf_packing_to_uicl_packing(data_info.packing)
    parameters.Alignment = vf_alignment_to_uicl_alignment(data_info.alignment)

    if color_info.color_format == ColorInfo.ColorFormat.CF_RGB:
        parameters.Colorspace = UICL_Colorspace.Colorspace_RGB
        parameters.Sampling = UICL_Sampling.Sampling_444
        parameters.IsFullRange = color_info.DynamicRange == ColorInfo.DynamicRange.DR_VESA

    elif color_info.color_format == ColorInfo.ColorFormat.CF_YCbCr_444:
        parameters.Colorspace = UICL_Colorspace.Colorspace_YCbCr
        parameters.Sampling = UICL_Sampling.Sampling_444
        parameters.IsFullRange = True if data_info.component_order == DataInfo.ComponentOrder.CO_UCDRX else False

    elif color_info.color_format == ColorInfo.ColorFormat.CF_YCbCr_422:
        parameters.Colorspace = UICL_Colorspace.Colorspace_YCbCr
        parameters.Sampling = UICL_Sampling.Sampling_422
        parameters.IsFullRange = True if data_info.component_order == DataInfo.ComponentOrder.CO_UCDRX else False

    elif color_info.color_format == ColorInfo.ColorFormat.CF_YCbCr_420:
        parameters.Colorspace = UICL_Colorspace.Colorspace_YCbCr
        parameters.Sampling = UICL_Sampling.Sampling_420
        parameters.IsFullRange = True if data_info.component_order == DataInfo.ComponentOrder.CO_UCDRX else False

    else:
        parameters.Colorspace = UICL_Colorspace.Colorspace_Unknown
        parameters.Sampling = UICL_Sampling.Sampling_Unknown
        parameters.IsFullRange = True if data_info.component_order == DataInfo.ComponentOrder.CO_UCDRX else False

    return parameters


def image_convert_to_parameters(src_image: UICL_Image, parameters: UICL_ImageParameters) -> UICL_Image:
    dst_image = UICL_Image()

    dst_image.Parameters = parameters
    result = UICL_GetRequiredBufferSize(dst_image)

    assert result >= UICL_SUCCESS, f"Calculation required buffer size failed with error {result}"

    dst_image.DataSize = result
    dst_image.DataPtr = (c_uint8 * result)()

    result = UICL_Convert(src_image, dst_image)

    assert result >= UICL_SUCCESS, f"Image conversion failed with error {result}"

    return dst_image


def image_save_to_file(image: UICL_Image, path: str, image_format: UICL_ImageFileFormat) -> bool:
    return UICL_SaveToFile(image, path, image_format)
