import string
from functools import cache
from typing import Literal

import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont

from mower.utils import typealias as tp
from mower.utils.image import cropimg


@cache
def load_font(font_size):
    return ImageFont.truetype("mower/fonts/SourceHanSansCN-Medium.otf", font_size)


@cache
def generate_name(
    name: str,
    font_size: int = 40,
    pad_left: int = 0,
    pad_right: int = 0,
    width: int = 0,
    align: Literal["left", "right", "center"] = "left",
    style: Literal["light", "dark"] = "light",
    draw: bool = False,
) -> tp.GrayImage:
    """生成思源黑体的关卡名图片

    Args:
        name: 关卡名
        font_size: 字号
        pad_left: 左侧边距
        pad_right: 右侧边距
        width: 宽度，为零时宽度根据内容调整，非零时忽略未设置的留白
        align: 设置width后的对齐方式
        style: 亮/暗
        draw: 调试时显示结果
    """
    font = load_font(font_size)
    img = Image.new(mode="L", size=(800, 70))
    ImageDraw.Draw(img).text((20, 0), name, fill=(255,), font=font)
    img = np.array(img, dtype=np.uint8)
    contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    rect = [cv2.boundingRect(c) for c in contours]
    left = min(x for x, y, w, h in rect)
    right = max(x + w for x, y, w, h in rect)
    top = min(y for x, y, w, h in rect)
    bottom = max(y + h for x, y, w, h in rect)

    img = cropimg(img, ((left, top), (right, bottom)))
    img = cv2.copyMakeBorder(
        img, 0, 0, pad_left, pad_right, cv2.BORDER_CONSTANT, None, 0
    )
    h, w = img.shape
    if w < width:
        dx = width - w
        if align == "left":
            img = cv2.copyMakeBorder(img, 0, 0, 0, dx, cv2.BORDER_CONSTANT, None, 0)
        elif align == "right":
            img = cv2.copyMakeBorder(img, 0, 0, dx, 0, cv2.BORDER_CONSTANT, None, 0)
        else:
            x = int(dx / 2)
            img = cv2.copyMakeBorder(
                img, 0, 0, dx, dx - x, cv2.BORDER_CONSTANT, None, 0
            )
    elif w > width > 0:
        dx = w - width
        if align == "left":
            img = cropimg(img, ((0, 0), (width, h)))
        elif align == "right":
            img = cropimg(img, ((dx, 0), (width, h)))
        else:
            x = int(dx / 2)
            img = cropimg(img, ((x, 0), (x + width, h)))

    if style == "light":
        img = cv2.bitwise_not(img)

    if draw:
        from matplotlib import pyplot as plt

        plt.imshow(img, cmap="gray", vmin=0, vmax=255)
        plt.show()

    return img


last_letters = {}
for i in list(range(10)) + list(string.ascii_uppercase):
    i = str(i)
    last_letters[i] = generate_name(i, 42)
