from typing import Dict, Tuple, Optional
import cv2
import more_itertools
import numpy as np
from PIL import ImageFont, ImageDraw, Image
from pygifsicle import gifsicle
import os
from .text_style import TextStyle


def get_output_name(filename: str) -> str:
    return "".join(os.path.basename(filename).split('.')[:-1])


def get_video_data(filename: str) -> Dict:
    cap = cv2.VideoCapture(filename)
    fps = int(cap.get(cv2.CAP_PROP_FPS))
    frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    resolution = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),
                  int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
    cap.release()

    return {'fps': fps, 'frame_count': frame_count, 'resolution': resolution}


def create_text_overlay(
        resolution: Tuple,
        text: str,
        width: Optional[int] = None,
        text_style: TextStyle = TextStyle.TOP
) -> Optional[Tuple]:
    frame = np.zeros((resolution[1], resolution[0], 3), np.uint8)

    if width:
        frame = resize_frame(frame, width)

    if text == "":
        return frame, text_style, ((frame.shape[1], frame.shape[0]),)

    if text == "":
        return frame, (frame.shape[1], frame.shape[0])

    frame = Image.new('RGBA', (frame.shape[1], frame.shape[0]), (0, 0, 0, 0))
    size, text = __calculate_font_size(frame, text)
    font = ImageFont.truetype("impact.ttf", size)
    draw = ImageDraw.Draw(frame)

    x = frame.size[0] // 2 - draw.textsize(text, font=font)[0] // 2
    y = frame.size[1] // 20
    font = ImageFont.truetype("impact.ttf", size)

    if text_style == TextStyle.CAPTION:
        top_margin = draw.textsize(text, font=font)[1] + y * 2
        new_resolution = (frame.width, frame.height + top_margin)
        new_frame = Image.new("RGB", new_resolution, (255, 255, 255))
        new_frame.paste(frame, (0, top_margin))
        frame = new_frame
        draw = ImageDraw.Draw(frame)
        draw.text((x, y // 2), text, font=font, fill=(0, 0, 0))
        return frame, text_style, (new_resolution, top_margin)

    if text_style == TextStyle.BOTTOM:
        y = frame.size[1] - y - draw.textsize(text, font=font)[1]

    offset = size * 3 // 80

    draw.text((x - offset - 1, y - offset - 1),
              text, font=font, fill=(0, 0, 0))
    draw.text((x + offset + 1, y - offset - 1),
              text, font=font, fill=(0, 0, 0))
    draw.text((x - offset - 1, y + offset + 1),
              text, font=font, fill=(0, 0, 0))
    draw.text((x + offset + 1, y + offset + 1),
              text, font=font, fill=(0, 0, 0))

    draw.text((x, y), text, font=font, fill=(255, 255, 255))
    new_resolution = (frame.width, frame.height)
    return frame, text_style, (new_resolution,)


def cv22pil(frame: np.ndarray) -> Image.Image:
    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    return Image.fromarray(frame)


def pil2cv2(pil_image: Image.Image) -> np.ndarray:
    return cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR)


def morb_frame(
        frame: np.ndarray,
        text_overlay_image: Optional[Image.Image],
        width: Optional[int] = None,
        text: str = "",
        disable_dither: bool = False,
) -> np.ndarray:
    if width:
        frame = resize_frame(frame, width)
    if text != "":
        frame = caption_video(frame, text_overlay_image)
    if not disable_dither:
        frame = quantize_dither_frame(frame)
    return frame


def quantize_dither_frame(frame: np.ndarray) -> np.ndarray:
    frame = cv22pil(frame)
    frame = frame.convert('P', colors=256, palette=Image.Palette.WEB, dither=Image.Dither.FLOYDSTEINBERG)
    frame = frame.convert("RGB")
    frame = pil2cv2(frame)
    return frame


def shit_optimize(filename: str) -> None:
    gifsicle(sources=f"{filename}.gif", optimize=True, colors=256)


def resize_frame(frame: np.ndarray, width: int) -> np.ndarray:
    if width < 16:
        return frame
    return cv2.resize(frame, (width, frame.shape[0] * width // frame.shape[1]))


def caption_video(frame: np.ndarray, text_overlay_image: Image.Image) -> np.ndarray:
    return __overlay_text(frame, text_overlay_image)


def __calculate_font_size(frame: Image.Image, text: str = "", depth: int = 0) -> Tuple[int, str]:
    min_size = frame.size[0] // 32
    max_size = frame.size[0] // 16
    max_text_size = frame.size[0] - max_size

    giant_ass_font = ImageFont.truetype("impact.ttf", 10000)
    draw = ImageDraw.Draw(frame)
    size = 10000 * max_text_size // draw.textsize(text, font=giant_ass_font)[0]
    if size < min_size:
        new_text = text.replace("\n", "").split(" ")
        new_text = [list(x)
                    for x in more_itertools.divide(depth + 2, new_text)]
        new_text = " \n".join(" ".join(x) for x in new_text)
        size, text = __calculate_font_size(
            frame, new_text, depth + 1)
    elif size > max_size:
        size = max_size

    return size, text


def __overlay_text(frame: np.ndarray, text_overlay_image: Image.Image) -> np.ndarray:
    frame = cv22pil(frame)
    text_style = text_overlay_image[1]
    if text_style == TextStyle.CAPTION:
        top_margin = text_overlay_image[2][1]
        text_overlay_image[0].paste(frame, (0, top_margin))
        frame = text_overlay_image[0]
        return pil2cv2(frame)
    frame.paste(text_overlay_image[0], (0, 0), text_overlay_image[0])
    return pil2cv2(frame)
