import os
import typing as T

import cv2

from PIL import Image

from pycs.database.File import File

# pylint: disable=too-many-arguments, too-many-locals
def crop_file(file: File, project_root: str, x: float, y: float, w: float, h: float) -> str:
    """
    gets a file for the given file_id, crops the according image to the
    bounding box and saves the crops in the temp folder of the project.

    :param file: file object
    :param x: relative x-coordinate of the top left corner
    :param y: relative y-coordinate of the top left corner
    :param w: relative width of the bounding box
    :param h: relative height of the bounding box

    :return: directory and file name of the cropped patch
    """

    image = Image.open(file.absolute_path)
    width, height = image.size

    crop_width = int(width * w)
    crop_height = int(height * h)

    x0 = int(width * x)
    y0 = int(height * y)
    x1 = x0 + crop_width
    y1 = y0 + crop_height

    target_path = os.path.join(
        os.getcwd(),
        project_root,
        'temp',
        f'{file.uuid}_{x0}-{y0}_{x1}-{y1}.jpg',
    )

    if not os.path.exists(target_path):
        crop = image.crop((x0, y0, x0+crop_width, y0+crop_height))
        crop.save(target_path, quality=80)

    return os.path.split(target_path)


def resize_file(file: File,
                project_root: str,
                max_width: int,
                max_height: int) -> T.Tuple[str, str]:
    """
    If file type equals video this function extracts a thumbnail first. It calls resize_image
    to resize and returns the resized files directory and name.

    :param file: file object
    :param max_width: maximum image or thumbnail width
    :param max_height: maximum image or thumbnail height
    :return: resized file directory, resized file name
    """

    abs_file_path = file.absolute_path

    # extract video thumbnail
    if file.type == 'video':
        abs_target_path = os.path.join(os.getcwd(), project_root, 'temp', f'{file.uuid}.jpg')
        create_thumbnail(abs_file_path, abs_target_path)

        abs_file_path = abs_target_path

    # resize image file
    abs_target_path = os.path.join(os.getcwd(), project_root,
                                'temp', f'{file.uuid}_{max_width}_{max_height}.jpg')
    result = resize_image(abs_file_path, abs_target_path, max_width, max_height)

    # return path
    if result is not None:
        return os.path.split(abs_target_path)

    return os.path.split(abs_file_path)

def resize_image(file_path: str,
                 target_path: str,
                 max_width: int,
                 max_height: int) -> T.Optional[bool]:
    """
    resize an image so width < max_width and height < max_height

    :param file_path: path to source file
    :param target_path: path to target file
    :param max_width: maximum image width
    :param max_height: maximum image height
    :return:
    """
    # return if file exists
    if os.path.exists(target_path):
        return True

    # load full size image
    image = Image.open(file_path)
    img_width, img_height = image.size

    # abort if file is smaller than desired
    if img_width < max_width and img_height < max_height:
        return None

    # calculate target size
    target_width = int(max_width)
    target_height = int(max_width * img_height / img_width)

    if target_height > max_height:
        target_height = int(max_height)
        target_width = int(max_height * img_width / img_height)

    # resize image
    resized_image = image.resize((target_width, target_height))

    # save to file
    resized_image.save(target_path, quality=80)
    return True

def create_thumbnail(file_path: str, target_path: str) -> None:
    """
    extract a thumbnail from a video

    :param file_path: path to source file
    :param target_path: path to target file
    :return:
    """
    # return if file exists
    if os.path.exists(target_path):
        return

    # load video
    video = cv2.VideoCapture(file_path)

    # create thumbnail
    _, image = video.read()
    cv2.imwrite(target_path, image)

    # close video file
    video.release()
