import os.path

from PIL import Image, ImageDraw, ImageEnhance, ImageFile
import pillow_avif

# import cv2
import numpy as np
from blend_modes import multiply

ImageFile.LOAD_TRUNCATED_IMAGES = True


class PictureProcessing(object):
    def __init__(self, *args, **kwargs):
        self.im: Image
        if args:
            p = args[0]
            if isinstance(p, str):
                if p == "RGB" or p == "RGBA":
                    size = int(args[1][0]), int(args[1][1])
                    self.im = Image.new(args[0], size, args[2])
                else:
                    # 设计优先取值处理
                    _path = args[0]
                    file_path, file = os.path.split(_path)
                    custom_path = "{}/custom/{}".format(file_path, file)
                    if os.path.exists(custom_path):
                        _path = custom_path
                    self.im = Image.open(_path)

        elif "im" in kwargs:
            self.im = kwargs["im"]
        else:
            self.im = Image.new(mode="RGB", size=(1600, 500), color=(255, 255, 255))

    def getbbox(self):
        return self.im.getbbox()

    def expand_bbox(self, bbox, value=100):
        x1, y1, x2, y2 = (
            bbox[0] - value,
            bbox[1] - value,
            bbox[2] + value,
            bbox[3] + value,
        )
        if x1 < 0:
            x1 = 0
        if y1 < 0:
            y1 = 0
        bbox = (x1, y1, x2, y2)
        return [int(x) for x in bbox]

    def show(self):
        self.im.show()

    def get_size(self):
        return self.im.size

    def get_im(self):
        self.im: Image
        return self.im

    def to_color_2(self, target, blend):  # 正片叠底
        return np.array(np.multiply(target / 256, blend / 256) * 256, dtype=np.uint8)

    def get_overlay_pic(self, top_img, color=None):
        # 正片叠底
        top_img: PictureProcessing
        image_white = Image.new("RGB", (self.get_size()), color)
        backdrop_prepped = np.asfarray(image_white.convert("RGBA"))
        source_prepped = np.asfarray(self.im.convert("RGBA"))
        blended_np = multiply(backdrop_prepped, source_prepped, 1)
        img = Image.fromarray(np.uint8(blended_np)).convert("RGB")
        img.paste(top_img.im, (0, 0), top_img.im)
        return PictureProcessing(im=img)

    def to_overlay_pic_advance(
        self,
        mode="pixel",
        top_img="",
        base="nw",
        value=(0, 0),
        percentage=(0, 0),
        margins=(0, 0, 0, 0),
        opacity=100,
        top_png_img=None,
    ):
        # opacity 透明度
        # 正片叠底
        _p = PictureProcessing("RGBA", (self.width, self.height), (255, 255, 255, 255))
        top_img = _p.paste_img(
            mode=mode,
            top_img=top_img,
            base=base,
            value=value,
            percentage=percentage,
            margins=margins,
        )

        backdrop_prepped = np.asfarray(self.im.convert("RGBA"))
        source_prepped = np.asfarray(top_img.im.convert("RGBA"))
        blended_np = multiply(backdrop_prepped, source_prepped, opacity / 100)
        im = Image.fromarray(np.uint8(blended_np)).convert("RGB")
        pp = PictureProcessing(im=im)
        if top_png_img:
            pp = pp.paste_img(
                mode=mode,
                top_img=top_png_img,
                base=base,
                value=value,
                percentage=percentage,
                margins=margins,
            )

        return pp

    def __to_resize(self, width=None, height=None):
        _im_x, _im_y = self.get_size()
        if width and height:
            if _im_x >= _im_y:
                high = None
            else:
                width = None
        if width:
            re_x = int(width)
            re_y = int(_im_y * re_x / _im_x)
        else:
            re_y = int(height)
            re_x = int(_im_x * re_y / _im_y)
        img = self.get_im().resize((re_x, re_y))
        return img

    # 锐化图片
    def sharpen_image(self, factor=1.0):
        # 创建一个ImageEnhance对象
        enhancer = ImageEnhance.Sharpness(self.im)
        # 应用增强，值为0.0给出模糊图像，1.0给出原始图像，大于1.0给出锐化效果
        # 调整这个值来增加或减少锐化的程度
        sharp_img = enhancer.enhance(factor)
        return PictureProcessing(im=sharp_img)

    @property
    def width(self):
        return self.im.width

    @property
    def height(self):
        return self.im.height

    @property
    def size(self):
        return self.im.size

    def resize(
        self,
        mode="pixel",
        base="width",
        value=0,
        base_im=None,
        percentage=0,
        base_by_box=None,
    ):
        """
        plugins_mode # relative相对（宽度、高度、其他参考图），或  pixel绝对像素
        base # pixel,width,height,by_im 基于长边、基于短边  （基于短边时，则缩放到指定尺寸）by_im确保能塞进参考图内
        value # 固定值，如果为百分比，则为0
        percentage #百分比
        base_im # 参考基于其他图 PictureProcessing 格式
        """

        # 在箱子内
        if base_by_box:
            mode = "relative"
            base = "by_im"
            base_by_box = (int(base_by_box[0]), int(base_by_box[1]))
            base_im = Image.new("RGB", base_by_box, (255, 255, 255))

        # 绝对值
        if mode == "pixel":
            if base == "width":
                img = self.__to_resize(width=value)
            if base == "high":
                img = self.__to_resize(height=value)

        # 相对值
        if mode == "relative":
            if base == "width":
                img = self.__to_resize(
                    width=(
                        self.width * percentage
                        if not base_im
                        else int(base_im.width * percentage)
                    )
                )
            if base == "height":
                img = self.__to_resize(
                    width=(
                        self.height * percentage
                        if not base_im
                        else int(base_im.height * percentage)
                    )
                )

            # by_im确保能塞进参考图内
            if base == "by_im":
                percentage = 1 if not percentage else percentage
                box_width, box_height = int(base_im.width * percentage), int(
                    base_im.height * percentage
                )
                width, height = self.width, self.height
                if box_width / box_height < width / height:
                    scale = box_width / width
                else:
                    scale = box_height / height
                img = self.get_im().resize((int(width * scale), int(height * scale)))

        return PictureProcessing(im=img)

    def paste_img(
        self,
        mode="pixel",
        top_img="",
        base="nw",
        value=(0, 0),
        percentage=(0, 0),
        margins=(0, 0, 0, 0),
    ):
        top_img: PictureProcessing
        """
        {
            "command": "paste_img",
            "im": 需要粘贴的图片
            "pos": {"plugins_mode": "relative",  # pixel
                    "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
                    "value": (100, 100),
                    "percentage": (0.5, 0.5),
                    },
            "margins": (0, 0, 0, 0),  # 上下左右边距
        }
        """
        value = (int(value[0]), int(value[1]))
        # 处理默认值
        base = "nw" if not base else base
        percentage = (0, 0) if not percentage else percentage
        if margins:
            top, down, left, right = margins
        else:
            top, down, left, right = 0, 0, 0, 0

        if percentage != (0, 0):  # percentage 不按占比模式
            if base in ("nw", "wn", "wc", "cw", "nc", "cn", "center"):
                value = (int(self.width), int(self.height))
            if base in ("sw", "ws", "sc", "cs", "center"):
                value = (int(self.width), -1 * int(self.height))
            if base in ("ec", "ce"):
                value = (int(self.width), int(self.height))

        if mode == "pixel":
            # 基于右边，上下居中
            if base == "ec" or base == "ce":
                p_x = int(self.width - (top_img.width + value[0]))
                p_y = int((self.height - top_img.height) / 2) + value[1]

            # 基于顶部，左右居中
            if base == "nc" or base == "cn":
                # 顶部对齐
                deviation_x, deviation_y = int((self.width - top_img.width) / 2), int(
                    (self.height - top_img.height) / 2
                )
                p_x = deviation_x + value[0] + left
                p_y = value[1]

            # 基于右上角
            if base == "en" or base == "ne":
                p_x = int(self.width - (top_img.width + value[0])) + left
                p_y = value[1]

            # 基于左上角
            if base == "nw" or base == "wn":
                deviation_x, deviation_y = 0, 0
                p_x, p_y = value

            # 基于底部，左右居中
            if base == "cs" or base == "sc":
                deviation_x, deviation_y = int((self.width - top_img.width) / 2), int(
                    (self.height - top_img.height) / 2
                )

                p_y = self.height - (top_img.height + value[1] + down)
                p_x = deviation_x + value[0] + left

            # 上下左右居中
            if base == "center" or base == "cc":
                deviation_x, deviation_y = int((self.width - top_img.width) / 2), int(
                    (self.height - top_img.height) / 2
                )
                p_x = deviation_x + value[0] + left
                p_y = deviation_y + value[1] + top

            # 基于左下角
            if base == "sw" or base == "ws":
                # deviation_x, deviation_y = 0, int((img.height - img_1.height))
                p_x = value[0] + left
                p_y = self.height - (top_img.height + value[1] + down)

            # 基于左边，上下居中
            if base == "wc" or base == "cw":
                p_x = value[0] + left
                p_y = int((self.height - top_img.height) / 2) + value[1] + top

            # 基于右下角
            if base == "es" or base == "se":
                p_x = int(self.width - (top_img.width + value[0])) + left
                p_y = self.height - (top_img.height + value[1] + down) + top

            img = PictureProcessing(im=self.im).im
            # top_img.get_im().show()
            top_img_im = top_img.im
            try:
                img.paste(top_img_im, box=(p_x, p_y), mask=top_img_im.convert("RGBA"))
            except:
                img.paste(top_img_im, box=(p_x, p_y), mask=top_img_im)

        return PictureProcessing(im=img)

    def paste_img_invert(
        self,
        mode="pixel",
        top_img="",
        base="nw",
        value=(0, 0),
        percentage=(0, 0),
        margins=(0, 0, 0, 0),
    ):
        top_img: PictureProcessing
        bg_img = top_img.im

        top_img = PictureProcessing(im=self.im)
        self.im = bg_img
        return self.paste_img(
            mode=mode,
            top_img=top_img,
            base=base,
            value=value,
            percentage=percentage,
            margins=margins,
        )

    # 水平分布处理，一行N个
    def horizontal_distribution(
        self,
        pp_list,
        bg_width=1200,
        margins=(0, 0, 0, 0),
        line_spacing=0,
        number_per_row=3,
    ):
        """
        pp_list
        line_spacing:行间距
        number_per_row：每行个数
        margins: (0, 0, 0, 0),  # 上下左右边距
        """
        bg_width = int(bg_width)

        total_row = len(pp_list) // number_per_row
        if len(pp_list) % number_per_row > 0:
            total_row = total_row + 1
        # print("total_row", total_row)
        every_height = pp_list[0].height
        bg_pp = PictureProcessing(
            "RGBA",
            (
                bg_width,
                margins[0]
                + every_height * total_row
                + line_spacing * (total_row - 1)
                + margins[1],
            ),
            (255, 255, 255, 0),
        )

        row_num = 0
        y = margins[0]
        for i in range(0, len(pp_list), number_per_row):  # 切片获取每N个元素
            row_list = pp_list[i : i + number_per_row]  # 输出每行的元素
            if row_list:
                row_num += 1
                # 计算每个元素间距
                t_w = bg_pp.width - margins[2] - margins[3]
                al_w = sum([x.width for x in row_list])
                space = int((t_w - al_w) / (len(row_list) + 1))
                x = margins[2]
                # 粘贴每个元素
                for pp in row_list:
                    x = x + space
                    bg_pp = bg_pp.paste_img(
                        mode="pixel", top_img=pp, base="", value=(x, y)
                    )
                    x = x + pp.width

                if row_num != total_row:
                    y += every_height + line_spacing
        return bg_pp

    def get_text_image_advanced(
        self,
        value=(0, 0),
        font="",
        text="",
        anchor=None,
        align="left",
        spacing=0,
        fill=(0, 0, 0),
        return_mode="image",
        margins=(0, 0, 0, 0),
        max_len_one_line=0,
    ):
        """
        {
            "command": "add_text",
            "pos": {"plugins_mode": "relative",  # pixel
                    "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
                    "value": (100, 100),
                    "percentage": 0, },
            "font": "",
            "text": "",
            "anchor": "",  # mm 为居中 ma 为左右居中，上面居顶 rs 右边;从哪边开始输入
            "align": "对齐方式",left  center right
            "direction": "文本的方向",
            "max_len_one_line": "单行长度",
            "spacing": 10,
            "fill": "文字颜色",
        }
        margins 边距像素，上下左右;只有返回min_image_high 有效
        """

        # =====
        """
        return_mode:image返回图片，min_image_high返回最小高度尺寸但宽度不变，min_image返回最小高度与宽度
        """
        if not text:
            return self
        # pp = PictureProcessing("RGBA", self.size, (0, 0, 0, 0))
        pp = PictureProcessing("RGBA", self.size, (255, 255, 255, 0))
        draw_1 = ImageDraw.Draw(pp.im)
        # 定义字体，你需要有一个.ttf字体文件
        spacing = 4 if not spacing else spacing
        align = "left" if not align else align  # left, center 或 right
        if max_len_one_line > 0:
            text = text[:max_len_one_line]
        _, _, text_width, text_height = draw_1.textbbox((0, 0), text, font=font)
        value = (int(value[0]), int(value[1]))

        draw_1.multiline_text(
            (value[0], value[1] + margins[0]),
            text,
            fill=fill,
            font=font,
            anchor=anchor,
            spacing=spacing,
            align=align,
            direction=None,
            features=None,
            language=None,
            stroke_width=0,
            stroke_fill=None,
            embedded_color=False,
        )

        if return_mode == "image":
            pp = pp.paste_img_invert(
                mode="pixel", top_img=self, base="nw", value=(0, 0)
            )
            pass
        if return_mode == "min_image_high":
            # pp.show()
            bbox = pp.getbbox()
            bbox = (0, 0, pp.width, bbox[3] + margins[1])
            pp = pp.paste_img_invert(
                mode="pixel", top_img=self, base="nw", value=(0, 0)
            )
            pp = pp.crop(bbox)

        if return_mode == "min_image":
            bbox = pp.getbbox()
            pp = pp.crop(bbox)

        return pp

    def add_text(
        self,
        mode="",
        base="",
        value="",
        percentage="",
        font="",
        text="",
        anchor="",
        align="",
        direction="",
        max_len_one_line="",
        spacing="",
        fill="",
    ):
        """
        {
            "command": "add_text",
            "pos": {"plugins_mode": "relative",  # pixel
                    "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
                    "value": (100, 100),
                    "percentage": 0, },
            "font": "",
            "text": "",
            "anchor": "",  # mm 为居中 ma 为左右居中，上面居顶 rs 右边;从哪边开始输入
            "align": "对齐方式",left  center right
            "direction": "文本的方向",
            "max_len_one_line": "单行长度",
            "spacing": 10,
            "fill": "文字颜色",
        }

        """
        pp = PictureProcessing(im=self.im)
        draw_1 = ImageDraw.Draw(pp.im)
        # 定义字体，你需要有一个.ttf字体文件
        spacing = 4 if not spacing else spacing
        anchor = None if not anchor else anchor
        align = "left" if not align else align  # left, center 或 right
        _, _, text_width, text_height = draw_1.textbbox((0, 0), text, font=font)

        xy = (0, 0)
        if mode == "pixel":
            xy = value

        draw_1.multiline_text(
            xy,
            text,
            fill=fill,
            font=font,
            anchor=anchor,
            spacing=spacing,
            align=align,
            direction=None,
            features=None,
            language=None,
            stroke_width=0,
            stroke_fill=None,
            embedded_color=False,
        )
        return pp

    def crop_img(
        self,
        mode="pixel",
        base="nw",
        value=(100, 100, 10, 10),
        color_fill=(255, 255, 255),
        margins=(0, 0, 0, 0),
    ):
        """
        {
            "command": "crop_img",
            "img": {"im": "im"},
            "pos": {"plugins_mode": "relative",  # pixel
                    "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
                    "value": (100, 100, 10, 10),# 顶点，以及图片大小
                    },
            "color_fill": (255, 255, 255)
        }

        """
        pp = PictureProcessing(im=self.im)

        base = "nw" if not base else base
        if margins:
            top, down, left, right = margins
        else:
            top, down, left, right = 0, 0, 0, 0

        out_img_size = (value[2], value[3])
        # 默认填充色
        if not color_fill:
            color_fill = (0, 0, 0)

        if mode == "pixel":
            # 左上脚
            if base == "nw" or "wn":
                box = value
            # 左下角
            if base == "sw" or base == "ws":
                # deviation_x, deviation_y = 0, int((img.height - img_1.height))
                box = (value[0], pp.height - (value[1] + value[3]), value[2], value[3])
                # print(box)

            # 右下角
            if base == "se" or base == "es":
                box = (
                    pp.width - (value[0] + value[2]),
                    pp.height - (value[1] + value[3]),
                    value[2],
                    value[3],
                )
                # print(box)

            # 居中
            if base == "cc":
                # print("11-value", value)
                x = int((pp.width + value[0] - value[2]) / 2)
                y = int((pp.height + value[1] - value[3]) / 2)
                box = (x, y, value[2], value[3])
                # print("11-box", box)

            box = [box[0], box[1], box[0] + box[2], box[1] + box[3]]

        # print("12-box", box)
        # print("ww-hhh", pp.width, pp.height)
        out_img = pp.im.crop(box=box)
        # print("out_img", out_img.width, out_img.height)
        # print("-----")

        if box[0] < 0:
            out_img.paste(
                Image.new("RGB", (-1 * box[0], out_img.height), color_fill), (0, 0)
            )

        if box[1] < 0:
            out_img.paste(
                Image.new("RGB", (out_img.width, -1 * box[1]), color_fill), (0, 0)
            )

        if box[2] > pp.width:
            # print(box[2] - img.width, img.height)
            i = Image.new("RGB", (box[2] - pp.width, out_img.height), color_fill)
            out_img.paste(i, (pp.width - box[0], 0))

        if box[3] > pp.height:
            out_img.paste(
                Image.new("RGB", (out_img.width, box[3] - pp.height), color_fill),
                (0, pp.height - box[1]),
            )

        return PictureProcessing(im=out_img)

    def crop(self, bbox=(0, 0, 0, 0), mode=None):
        bbox = (int(bbox[0]), int(bbox[1]), int(bbox[2]), int(bbox[3]))
        if mode == "min":
            bbox = self.getbbox()
        return PictureProcessing(im=self.im.crop(bbox))

    def rotate(self, doge):
        return PictureProcessing(im=self.im.rotate(doge))

    def rotate_advance(self, doge, is_crop=True):
        max_px = max(self.width, self.height)
        max_px = max_px * 2
        bg = PictureProcessing("RGBA", (max_px, max_px), (255, 255, 255, 0))
        bg = bg.paste_img(top_img=self, base="cc")
        bg = bg.rotate(doge)
        if is_crop:
            bg = bg.crop(mode="min")

        return bg

    def radius(self, mode="pixel", circular_pos=(1, 1, 1, 1), value=30, percentage=""):
        """
        {"command": "radius",  # radius
             "plugins_mode": "relative",  # pixel 相对（短边），或绝对像素
             "circular_pos": (0, 1, 0, 1),  # 从左上角顺时针，记录圆角数量
             "value": 649,  # 固定值，如果为百分比，则为0
             "percentage": 0, }  # 百分比
        """
        # 单图圆角处理
        pp = PictureProcessing(im=self.im)

        radii = value
        if radii > pp.width / 2:
            radii = int(pp.width / 2)
        if radii > pp.height / 2:
            radii = int(pp.height / 2)

        # 画圆（用于分离4个角）
        circle = Image.new("L", (radii * 2, radii * 2), 0)  # 创建一个黑色背景的画布
        draw = ImageDraw.Draw(circle)
        draw.ellipse((0, 0, radii * 2, radii * 2), fill=255)  # 画白色圆形

        # 原图
        img = pp.im.convert("RGBA")
        w, h = img.size

        # 画4个角（将整圆分离为4个部分）
        alpha = Image.new("L", img.size, 255)
        _pos = circular_pos
        if not _pos:
            _pos = (1, 1, 1, 1)
        for index, i in enumerate(_pos):
            if index == 0 and i == 1:
                alpha.paste(circle.crop((0, 0, radii, radii)), (0, 0))  # 左上角
            if index == 1 and i == 1:
                alpha.paste(
                    circle.crop((radii, 0, radii * 2, radii)), (w - radii, 0)
                )  # 右上角
            if index == 2 and i == 1:
                alpha.paste(
                    circle.crop((radii, radii, radii * 2, radii * 2)),
                    (w - radii, h - radii),
                )  # 右下角
            if index == 3 and i == 1:
                alpha.paste(
                    circle.crop((0, radii, radii, radii * 2)), (0, h - radii)
                )  # 左下角
        # alpha.show()
        img.putalpha(alpha)  # 白色区域透明可见，黑色区域不可见
        return PictureProcessing(im=img)

    # 图片翻转
    def transpose(self, mode="left_right"):
        img = self.get_im()
        img = img.transpose(Image.FLIP_LEFT_RIGHT)
        return PictureProcessing(im=img)

    def convert(self, mode):
        return PictureProcessing(im=self.im.convert(mode))

    def save_as_rgb(self, path):
        self.im = self.im.convert("RGB")
        self.im.save(path, format="JPEG")

    def save_as_png(self, path):
        self.im = self.im.convert("RGBA")
        self.im.save(path)

    def save_as_other(self, path, format):
        self.im.save(path, format=format)
