import re
from os import path, getcwd

import cv2
from PIL import Image
from eventlet import tpool
from flask import abort, send_from_directory
from flask.views import View

from pycs.database.Database import Database


class GetResizedFile(View):
    """
    returns binary file after resizing
    """
    # pylint: disable=arguments-differ
    methods = ['GET']

    def __init__(self, db: Database):
        # pylint: disable=invalid-name
        self.db = db

    def dispatch_request(self, file_id: int, resolution: str):
        # get file from database
        file = self.db.file(file_id)
        if file is None:
            return abort(404)

        project = file.project()

        # extract desired resolution
        resolution = re.split(r'[^0-9]', resolution)
        max_width = int(resolution[0])
        max_height = int(resolution[1]) if len(resolution) > 1 else 2 ** 24

        # send data
        file_directory, file_name = tpool.execute(self.resize_file,
                                                  project, file, max_width, max_height)
        return send_from_directory(file_directory, file_name)

    @staticmethod
    def resize_file(project, file, max_width, max_height):
        """
        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 project: associated project
        :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
        """
        # get absolute path
        if path.isabs(file.path):
            abs_file_path = file.path
        else:
            abs_file_path = path.join(getcwd(), file.path)

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

            abs_file_path = abs_target_path

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

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

        return path.split(abs_file_path)

    @staticmethod
    def resize_image(file_path, target_path, max_width, max_height):
        """
        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 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

    @staticmethod
    def create_thumbnail(file_path, target_path):
        """
        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 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()
