import os
import sys
import time
import threading
import subprocess
import tkinter as tk
from tkinter import font
from pathlib import Path
from functools import wraps

cwd = __file__.rsplit("\\", 1)[0]
if Path(f"{cwd}/requirements.txt").exists():
    subprocess.check_call([sys.executable] + "-m pip install -r".split(' ') + [f"{cwd}/requirements.txt"])
os.system("cls")

import keyboard
from globals import *
from mudae import char_attr, character
from PIL.ImageFont import FreeTypeFont
from PIL import Image, ImageTk, ImageEnhance, ImageFont


__author__ = "Jochem van Dolder"
__license__ = "MIT"
__all__ = ["app"]
__name__ = "Color Picker"

ASSET_FOLDER = Path(cwd) / "Assets"
FONTS_FOLDER = Path(cwd) / "Fonts"

### --- DECORATORS --- ###

def timer(func):
    @wraps(func)
    def inner(*args, **kwargs):
        tic = time.perf_counter()
        result = func(*args, **kwargs)
        print(f"{func.__name__} took {time.perf_counter() - tic}s")
        return result
    return inner

def ui_decorator(func):
    @wraps(func)
    def inner(*args, **kwargs):
        self:app = args[0]
        result = func(*args, **kwargs)
        self.raise_tags()
        return result
    return inner

def ui_update(func):
    @wraps(func)
    def inner(*args, **kwargs):
        self:app = args[0]
        result = func(*args, **kwargs)
        self.ui.update()
        return result
    return inner

### --- MAIN APP CLASS --- ###

class app():
    def __init__(self, ui:tk.Canvas):
        def _data_parse(data:str):
            self.mudae_format = True
            if data.startswith('http') or data[1:].startswith(':/') or data.startswith('{'):
                self.mudae_format = False
            if (data[1:].startswith(':/') or (data.count('{') and data.count('}')) or data.startswith('{')) and not (('\n' in data) or data.startswith('#')):
                item_list = self.ui.tk.splitlist(data)
            else:
                item_list = data.split('\n')
            if item_list:
                for char in self.character_list:
                    char.close()
                self.attr_list = []
                self.character_index = 0
                self.character_list = []
                for item in item_list:
                    attr = char_attr(item.strip())
                    if not attr: continue
                    self.attr_list.append(attr)
                self.set_character(self.character_index, first=True)
        def _on_drop(event):
            _data_parse(event.data)
            self.ui.focus_force()
        def _on_paste(event):
            data = self.ui.clipboard_get()
            _data_parse(data)
                
        self.ui = ui
        self.ui.drop_target_register("*")
        self.ui.dnd_bind("<<Drop>>", _on_drop)
        self.ui.bind_all("<Control-v>", _on_paste)
        
        self.ui_size = (self.ui.winfo_width(),self.ui.winfo_height())
        self.ui_cntr = (self.ui_size[0]/2,self.ui_size[1]/2)
        
        self.get_fonts()
        self.get_images()
        
        self.mudae_format = True
        self.next_frame_call = None
        self.fetching:bool = False
        
        self.history_offset = ENTRY_COUNT/2
        self.visible_index = [0]
        self.character_index = 0
        self.attr_list:list[char_attr] = []
        self.character_list:list[character] = []
        
        def_char = self.parse_character(DEFAULT_CHAR)
        char = def_char if def_char else self.parse_character(DEFAULT_URL)
        if not char: return
        print(char)
        self.mudae_character = char
        self.character_list = [char]
        self.attr_list = [self.mudae_character.attr]
    
    @ui_decorator
    def build_ui(self):
        if not self.mudae_character: return
        self.clear_buttons()
        
        self.set_mudae_image()
        self.picked_color = self.mudae_character.mudae_image.color
        self.set_text_overlay()
        self.set_embed_bg()
        
        self.set_play_button()
        self.set_rem_ram()
        self.set_set_image()
        self.set_palette()
        self.set_copy_embedcolor()
        self.set_next_character()
        self.set_prev_character()
        self.set_paste_commands()
        self.set_char_history()
        self.set_copy_command()

### --- HELPER FUNCS --- ###

    def set_text_width(self, text:str, width:int, font:font.Font):
        if font.measure(text) <= width: return text, font.measure(text)
        line_list = []
        word_list = text.split(' ')
        line = ''
        for word in word_list:
            line_part_list = []
            i = 0
            while font.measure(line) > width:
                size = font.measure(line[:i])
                if size <= width:
                    i += 1
                    continue
                line_part_list.append(line[:i-1])
                line = line[i-1:]
                i = 0
            line_list += line_part_list
            tmp = line + ' ' + word if line else word
            if font.measure(tmp) <= width:
                line = tmp
                continue
            line_list.append(line)
            line = word
        line_list.append(line)
        return '\n'.join(line_list), font.measure(line)
    
    def raise_tags(self):
        self.ui.tag_raise("mudae_image")
        self.ui.tag_raise("ui_overlay")
        self.ui.tag_raise("embed_txt")
        self.ui.tag_raise("loupe")
        
        self.ui.tag_lower("history")
        self.ui.tag_lower("mudae_command")
    
    def remove_tag_bind(self, tags:list[str], bindings:list[str]=[]):
        for tag in tags:
            if self.ui.find_withtag(tag):
                for binding in bindings:
                    self.ui.tag_unbind(tag, binding)
                self.ui.delete(tag)

    def set_character(self, i:int, show=True, first=False):
        def update_ui():
            self.set_char_history()
            self.set_next_character()
        def finish(index:int):
            if not (show and self.character_list): return
            self.mudae_character = self.character_list[index]
            self.build_ui()
            print(self.mudae_character)
        
        if i < len(self.character_list):
            return finish(i)

        if len(self.attr_list) == len(self.character_list): return
        
        def task():
            attr = self.attr_list[i]
            source = attr if self.mudae_format else attr.item
            char = self.parse_character(source)
            if i > len(self.character_list): return
            if char:
                self.character_list.append(char)
                if first: self.ui.after(0, lambda: self.set_character(i+1, False, first))
                finish(i)
            else:
                self.attr_list.pop(i)
                self.ui.after(0, lambda: self.set_character(i, show, first))
            # if i not in self.visible_index: return
            self.ui.after(0, update_ui)
        
        threading.Thread(target=task, daemon=True).start()

    def update_loupe_img(self):
        if not hasattr(self, "image_coords"): return
        loupe_img = self.mudae_character.image.crop((self.image_coords[0]-13,self.image_coords[1]-13,self.image_coords[0]+14,self.image_coords[1]+14))
        loupe_img = loupe_img.resize((81,81),resample=Image.NEAREST)
        
        temp = Image.new("RGBA",(85,85),(0,0,0,0))
        temp_bg = Image.new("RGBA",(81,81),EMB_BG)
        temp_bg = Image.alpha_composite(temp_bg,loupe_img)
        temp.paste(temp_bg,(2,2))
        
        loupe_img_crosshair = Image.alpha_composite(temp,self.loupe_img)
        self.loupe_img_TK = ImageTk.PhotoImage(loupe_img_crosshair)
        self.ui.itemconfig("loupe", image=self.loupe_img_TK)

    def recolor_keep_alpha(self, img, rgb, alpha=None):
        r, g, b = rgb
        if alpha == None:
            alpha = img.getchannel("A")
        recolored = Image.new("RGBA", img.size, (r, g, b, 255))
        recolored.putalpha(alpha)
        return recolored

    def get_corners(self, item_id):
        bbox = self.ui.bbox(item_id)
        if bbox == None: return
        return ((bbox[0],bbox[1]),(bbox[0],bbox[3]),(bbox[2],bbox[3]),(bbox[2],bbox[1]),(int((bbox[0]+bbox[2])/2),int((bbox[1]+bbox[3])/2)))

    def hex_to_rgb(self, hex_color):
        hex_color = hex_color.lstrip("#")
        return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
    
    def rgb_to_hex(self, rgb_color):
        return "#{:02X}{:02X}{:02X}".format(*rgb_color)

    def update_mudae_image(self):
        self.mudae_character.image = self.mudae_character.mudae_image.image()
        w,h = self.mudae_character.image.size
        scale = EMB_IMG_H/h
        self.Mudae_Image = ImageTk.PhotoImage(self.mudae_character.image.resize((int(w*scale),int(h*scale))))

    def create_widget_img(self, im_w, im_h):
        key = (im_w, im_h)

        if not hasattr(self, "_widget_cache"):
            self._widget_cache = {}

        if key not in self._widget_cache:
            edge_T = self.Embed_edge_T.resize((im_w-10, 5), Image.NEAREST)
            edge_B = self.Embed_edge_B.resize((im_w-10, 5), Image.NEAREST)
            edge_L = self.Embed_edge_L.resize((5, im_h-10), Image.NEAREST)
            edge_R = self.Embed_edge_R.resize((5, im_h-10), Image.NEAREST)
            
            img = Image.new("RGBA", (im_w, im_h), EMB_BG)

            img.paste(self.Embed_corner_NW, (0, 0))
            img.paste(self.Embed_corner_SW, (0, im_h - 5))
            img.paste(self.Embed_corner_SE, (im_w - 5, im_h - 5))
            img.paste(self.Embed_corner_NE, (im_w - 5, 0))

            img.paste(edge_T, (5, 0))
            img.paste(edge_B, (5, im_h - 5))
            img.paste(edge_L, (0, 5))
            img.paste(edge_R, (im_w - 5, 5))

            self._widget_cache[key] = img
            
        return self._widget_cache[key]

    def clear_buttons(self, tag=None):
        self.remove_tag_bind([f"{tag}_button" if tag != None else 'Button'], ["<ButtonRelease-1>","<Enter>","<Leave>"])
    
    def create_button(self, w, h, x, y, anchor, text, tag, click_func=None, txt_col=L_GREY, active=True, color='', font=None):
        def _on_leave_enter(event, pressed=False):
            key = (w, h, active, color, text, txt_col, id(font), pressed)
            if key not in self.button_dict:
                key_temp = (*key[:-1], False)
                widget_img = self.create_widget_img(self.button_dict[key_temp].width(),self.button_dict[key_temp].height())
                if color: widget_img = self.recolor_strip(widget_img, color)
                if text: widget_img = self.text_on_img(widget_img, text, font=font, txt_col=txt_col, edge_spacing=4)
                if pressed:widget_img = ImageEnhance.Brightness(widget_img).enhance(1.276)
                self.button_dict[key] = ImageTk.PhotoImage(widget_img)
            if pressed:
                self.ui.tag_bind(f"{tag}_button", "<ButtonRelease-1>", lambda event: _on_click(event))
            else:
                self.ui.tag_unbind(f"{tag}_button", "<ButtonRelease-1>")
            self.ui.itemconfig(f"{tag}_img", image=self.button_dict[key])
        def _on_click(event):
            click_func(event)
        
        self.clear_buttons(tag)
        
        if not hasattr(self, "button_dict"):
            self.button_dict:dict[str,ImageTk.PhotoImage] = {}
        
        if not font: font = self.pil_font_12_bold
        key = (w, h, active, color, text, txt_col, id(font), False)
        if key not in self.button_dict:
            widget_img = self.create_widget_img(w,h)
            if not active:
                widget_img = ImageEnhance.Brightness(widget_img).enhance(0.783)
                txt_col = N_GREY
            if color: widget_img = self.recolor_strip(widget_img, color)
            if text: widget_img = self.text_on_img(widget_img, text, font=font, txt_col=txt_col, edge_spacing=4)
            self.button_dict[key] = ImageTk.PhotoImage(widget_img)
        self.ui.create_image(x, y, image=self.button_dict[key], anchor=anchor, 
                             tags=(tag, f"{tag}_img", f"{tag}_button", "Button"))

        if (not active) or (not click_func): return
        self.ui.tag_bind(f"{tag}_button", "<Enter>", lambda event: _on_leave_enter(event, True))
        self.ui.tag_bind(f"{tag}_button", "<Leave>", lambda event: _on_leave_enter(event, False))

    def make_footer_str(self):
        images_str = self.mudae_character.get_img_number_str()
        if images_str: images_str = f" ~~ {images_str}"
        text_width = self.Mudae_Image.width()-self.font_8_bold.measure(images_str)
        
        if self.mudae_character.owner:
            footer = f"Belongs to {self.mudae_character.owner}"
            for i in range(len(footer)):
                if self.font_8_bold.measure(footer[:i]) <= text_width: continue
                footer = footer[:i-1]
                break
            footer += images_str
            return footer
        elif self.mudae_character.name and self.mudae_character.series:
            char_name = self.mudae_character.name
            if self.mudae_character.og_name:
                char_name = self.mudae_character.og_name
            footer = f"{char_name} / {self.mudae_character.series}"
            for i in range(len(footer)):
                if self.font_8_bold.measure(footer[:i]) <= text_width: continue
                footer = footer[:i-1]
                break
            footer += images_str
            return footer
    
    def text_on_img(self, Background:Image.Image, text:str, coords:tuple[int,int]=None, anchor:str='center', font:FreeTypeFont=None, txt_col:str=L_GREY, edge_spacing=None):
        if font == None:
            font = self.pil_font_10_bold
        if coords == None:
            coords = (Background.size[0]/2, Background.size[1]/2)
        if not hasattr(self, "_text_img_cache "):
            self._text_img_cache  = {}
        key = (text, id(font))
        if key not in self._text_img_cache:
            m = font.getmask(text)
            self._text_img_cache[key] = Image.frombytes("L", m.size, bytes(m))
        text_img = self._text_img_cache[key]
        text_tmp = Image.new("RGBA", Background.size, (0, 0, 0, 0))
        if edge_spacing == None: edge_spacing = int((text_tmp.size[1]-text_img.size[1])/2)
        if text_img.size[0] > text_tmp.size[0]-2*edge_spacing:
            text_img = Image.Image.crop(text_img, [0,0,text_tmp.size[0]-2*edge_spacing,text_img.size[1]])
        if anchor != 'nw':
            if anchor == 'center':
                coords = (max(edge_spacing,int(coords[0] - text_img.size[0]/2)), 
                          int(coords[1] - text_img.size[1]/2))
            if anchor == 'sw':
                coords = (max(edge_spacing,int(coords[0])), 
                          int(coords[1] - text_img.size[1]))
            if anchor == 'se':
                coords = (max(edge_spacing,int(coords[0] - text_img.size[0])), 
                          int(coords[1] - text_img.size[1]))
            if anchor == 'ne':
                coords = (max(edge_spacing,int(coords[0] - text_img.size[0])), 
                          int(coords[1]))
        text_tmp.paste(self.recolor_keep_alpha(text_img, self.hex_to_rgb(txt_col), text_img), coords)
        return Image.alpha_composite(Background, text_tmp)
    
    def recolor_strip(self, img:Image.Image, color, width=4):
        color = self.hex_to_rgb(color)
        img_edit = img.copy()
        pixels = img_edit.load()
        w, h = img_edit.size
        for x in range(min(width, w)):
            for y in range(h):
                r, g, b, a = pixels[x, y]
                if (y == 0) or (y == h-1):
                    cr, cg, cb = color
                    m_color = tuple((int(((width-x)*cr+x*r)/width),int(((width-x)*cg+x*g)/width),int(((width-x)*cb+x*b)/width)))
                    pixels[x, y] = (*m_color, a)
                else:
                    pixels[x, y] = (*color, a)  # preserve alpha
        return img_edit
    
### --- CHARACTER --- ###
    
    def parse_character(self, source:char_attr|str):
        if isinstance(source, char_attr):
            char = character(source)

        elif source.startswith('https://mudae.net/uploads/'):
            attr = char_attr()
            attr.url = source
            char = character(attr)
        
        elif source.startswith('http'):
            attr = char_attr()
            attr.url = source
            char = character(attr)
        
        elif source[1:3] == ':/':
            attr = char_attr()
            attr.path = source
            char = character(attr)
        
        elif source != '':
            attr = char_attr()
            attr.name = source
            char = character(attr)
        
        else:
            char = None

        if char:
            return char

### --- SET REM RAM --- ###
    @ui_decorator
    def set_rem_ram(self):
        self.remove_tag_bind(["rem","ram"], ["<ButtonRelease-1>","<Enter>","<Leave>"])
        def create_rem_ram_image(rem_ram, pressed=False):
            if rem_ram == "rem":
                rem_ram_img = self.rem_img
            elif rem_ram == "ram":
                rem_ram_img = self.ram_img
            widget = self.create_widget_img(60,32)
            if pressed:
                widget = ImageEnhance.Brightness(widget).enhance(1.276)
            temp = Image.new("RGBA", widget.size, (0, 0, 0, 0))
            temp.paste(rem_ram_img.resize((20,19)),(20,6))
            widget_alpha = Image.alpha_composite(widget, temp)
            return ImageTk.PhotoImage(widget_alpha)
        def _rem_ram_click(event, rem_ram):
            if rem_ram == 'rem': self.mudae_character.prev_image()
            if rem_ram == 'ram': self.mudae_character.next_image()
            out_string = ''
            out_string += f"────────────────╮\n"
            out_string += f"  Current image │\n"
            out_string += f"╭───────────────╯\n"
            out_string += f"{self.mudae_character.mudae_image}\n"
            out_string += f"╯"
            print(out_string)
            
            self.build_ui()
        def _rem_ram_leave_enter(event, rem_ram, pressed):
            if rem_ram == 'rem': 
                self.rem_widget_TK = create_rem_ram_image(rem_ram, pressed=pressed)
                self.ui.itemconfig(rem_ram, image=self.rem_widget_TK)
                if pressed:
                    self.ui.tag_bind(rem_ram, "<ButtonRelease-1>", lambda event,rr=rem_ram: _rem_ram_click(event, rr))
                else:
                    self.ui.tag_unbind(rem_ram, "<ButtonRelease-1>")          
            if rem_ram == 'ram': 
                self.ram_widget_TK = create_rem_ram_image(rem_ram, pressed=pressed)
                self.ui.itemconfig(rem_ram, image=self.ram_widget_TK)
                if pressed:
                    self.ui.tag_bind(rem_ram, "<ButtonRelease-1>", lambda event,rr=rem_ram: _rem_ram_click(event, rr))
                else:
                    self.ui.tag_unbind(rem_ram, "<ButtonRelease-1>")                    

        if len(self.mudae_character.images) < 2: return
        
        self.rem_widget_TK = create_rem_ram_image("rem")
        self.ram_widget_TK = create_rem_ram_image("ram")

        coord_list = self.get_corners("embed_bg")
        if coord_list != None:
            sw = coord_list[1]
            self.ui.create_image(sw[0], sw[1]+4, image=self.rem_widget_TK, anchor='nw', tags=("rem"))
            self.ui.create_image(sw[0]+68, sw[1]+4, image=self.ram_widget_TK, anchor='nw',  tags=("ram"))
            
            for rem_ram in ("rem", "ram"):
                self.ui.tag_bind(rem_ram, "<Enter>", lambda event,rr=rem_ram: _rem_ram_leave_enter(event, rr, True))
                self.ui.tag_bind(rem_ram, "<Leave>", lambda event,rr=rem_ram: _rem_ram_leave_enter(event, rr, False))

### --- SET IMAGE --- ###
    @ui_decorator
    def set_set_image(self):
        def _on_click(event):
            ret = self.mudae_character.c_command()
            if ret:
                self.ui.clipboard_clear()
                self.ui.clipboard_append(ret)
        
        coord_list_embed = self.get_corners("embed_bg")
        coord_list_ram = self.get_corners("ram")

        if (coord_list_embed != None) and (coord_list_ram != None):
            active = False if self.mudae_character.mudae_image.title in ("External", "Imported", "Custom") else True
            self.create_button(coord_list_embed[2][0]-coord_list_ram[2][0]-8, 32,
                               coord_list_embed[2][0], coord_list_embed[2][1]+4, "ne",
                               "Set image",
                               "set_image", _on_click, active = active)

### --- SET COLOR --- ###
    @ui_decorator
    def set_copy_embedcolor(self):
        def _on_click(event):
            ret = self.mudae_character.ec_command()
            if ret:
                self.ui.clipboard_clear()
                self.ui.clipboard_append(ret)
        
        coord_list = self.get_corners("palette")
        if coord_list:
            nw, sw, se, ne, cntr = coord_list 
            self.create_button(int((se[0]-sw[0])/3)-4, 32,
                               cntr[0], se[1]+4, "n",
                               "Color Command",
                               "set_color", _on_click)
            
### --- SET NEXT PREV CHARACTER --- ###
    @ui_decorator
    def set_next_character(self):
        def _on_click(event):
            if (self.character_index + 1) < len(self.character_list):
                self.character_index += 1
                self.set_character(self.character_index)
            self.set_next_character()
        
        coord_list = self.get_corners("palette")
        if coord_list:
            nw, sw, se, ne, cntr = coord_list
            active = False if self.character_index == (len(self.character_list)-1) else True
            self.create_button(int((se[0]-sw[0])/3)-4,32,
                               se[0],se[1]+4,"ne",
                               "Next Character",
                               "next_char", _on_click, active = active)
        
        if self.ui.find_withtag("next_char"):
            self.ui.bind_all("<Right>", _on_click)
        if self.ui.find_withtag("next_char"):
            self.ui.bind_all("<Down>", _on_click)

    @ui_decorator
    def set_prev_character(self):
        def _on_click(event):
            if (self.character_index - 1) >= 0:
                self.character_index -= 1
                self.set_character(self.character_index)
            self.set_prev_character()
        
        coord_list = self.get_corners("palette")
        if coord_list:
            nw, sw, se, ne, cntr = coord_list
            active = False if self.character_index == 0 else True
            self.create_button(int((se[0]-sw[0])/3)-4, 32,
                               sw[0], se[1]+4, "nw",
                               "Prev Character",
                               "prev_char", _on_click, active = active)
        
        if self.ui.find_withtag("prev_char"):
            self.ui.bind_all("<Left>", _on_click)
        if self.ui.find_withtag("prev_char"):
            self.ui.bind_all("<Up>", _on_click)

### --- SET PASTE COMMANDS --- ###
    @ui_decorator
    def set_paste_commands(self):
        self.hotkey = None
        self.after_call = None
        self.paste_char = self.mudae_character

        def _next_char(index):
            if index >= len(self.character_list):
                if self.hotkey:
                    self.hotkey = keyboard.remove_hotkey(self.hotkey)
                    keyboard.write("Done Pasting!")
                    print("Done Pasting!")
                return
            char = self.character_list[index]
            if char:
                self.paste_char = char
                _copy_c(index)
            else:
                _next_char(index+1)
        def _copy_c(index):
            def _paste_c(index):
                keyboard.send("ctrl+v")
                keyboard.send("enter")
                self.after_call = self.ui.after(1000,_copy_ec,(index))
            if self.paste_char.name and (self.paste_char.original_index != self.paste_char.mudae_image.number):
                ret = self.paste_char.c_command()
                if ret:
                    self.ui.clipboard_clear()
                    self.ui.clipboard_append(ret)
                    self.after_call = self.ui.after(1000,_paste_c,(index))
                    return
            self.after_call = self.ui.after(1000,_copy_ec,(index))
        def _copy_ec(index):
            def _paste_ec(index):
                keyboard.send("ctrl+v")
                keyboard.send("enter")
                self.after_call = self.ui.after(1000,_next_char,(index+1))
            if (self.paste_char.mudae_image.color != self.paste_char.mudae_image.original_color):
                ret = self.paste_char.ec_command()
                if ret:
                    self.ui.clipboard_clear()
                    self.ui.clipboard_append(ret)
                    self.after_call = self.ui.after(1000,_paste_ec,(index))
                    return
            self.after_call = self.ui.after(1000,_next_char,(index+1))
        def _on_cancel():
            if self.hotkey:
                self.hotkey = keyboard.remove_hotkey(self.hotkey)
                self.ui.after_cancel(self.after_call)
                self.ui.clipboard_clear()
                keyboard.write("Canceled Pasting!")
        def _on_trigger():
            if self.hotkey:
                self.hotkey = keyboard.remove_hotkey(self.hotkey)
                self.hotkey = keyboard.add_hotkey("esc", _on_cancel)
            _next_char(0)
        def _on_click(event):
            if not self.hotkey:
                self.hotkey = keyboard.add_hotkey("esc", _on_trigger)
                out_str = ''
                out_str += "───────────────────────╮\n"
                out_str += " STARTED COMMAND PASTE │\n"
                out_str += "╭──────────────────────╯\n"
                out_str += "├ Paste commands ready  \n"
                out_str += "│ focus discord in the  \n"
                out_str += "│ mudae channel and     \n"
                out_str += "│ press 'esc' to start  \n"
                out_str += "│ pasting commands      \n"
                out_str += "│                       \n"
                out_str += "├ If at any point you   \n"
                out_str += "│ want to stop pasting  \n"
                out_str += "│ press 'esc' again and \n"
                out_str += "│ the command pasting   \n"
                out_str += "│ will be canceled      \n"
                out_str += "│                       \n"
                out_str += "├ After all commands are\n"
                out_str += "│ pasted, you will see a\n"
                out_str += "│ message 'Done Pasting'\n"
                out_str += "│ after which all colors\n"
                out_str += "│ and images are set\n"
                out_str += "╯"
                print(out_str)
        
        coord_list_palette = self.get_corners("palette")
        coord_list_prev = self.get_corners("prev_char")
        if bool(coord_list_palette) and bool(coord_list_prev):
            cntr = coord_list_palette[4]
            se = coord_list_prev[2]
            self.create_button(150, 32,
                               cntr[0], se[1]+4, "n",
                               "Start Pasting Commands",
                               "set_paste", _on_click)

### --- SET PALETTE --- ###
    @ui_decorator
    def set_palette(self, num_cols=NUM_COLORS):
        def _on_key(event):
            self.picked_color = self.rgb_to_hex(self.color_palette_rgb[int(event.char)-1])
            self.mudae_character.mudae_image.color = self.picked_color
            self.set_embed_bg()
        if not self.ui.find_withtag("palette"):
            self.ui.bind_all("1", _on_key)
            self.ui.bind_all("2", _on_key)
            self.ui.bind_all("3", _on_key)
            self.ui.bind_all("4", _on_key)
            self.ui.bind_all("5", _on_key)
        self.remove_tag_bind(["palette"], ["<ButtonRelease-1>"])
        def create_palette_image(width=50, height=40, padding=10):

            num_cols = len(self.color_palette_rgb)
            im_w = width * num_cols + padding * (num_cols + 1)
            im_h = height + padding * 2
            
            self.Palette_Img = self.create_widget_img(im_w, im_h)
            paste_x = padding
            paste_y = padding
            
            temp = Image.new("RGBA", self.Palette_Img.size, (0, 0, 0, 0))
            text = Image.new("RGBA", self.Palette_Img.size, (0, 0, 0, 0))
            for col in self.color_palette_rgb:
                col_avg = int(sum(col)/len(col))
                temp.paste(self.recolor_keep_alpha(self.create_widget_img(width, height), col), (paste_x,paste_y))
                text_img = Image.frombytes("L", (m := self.pil_font_10_bold.getmask(self.rgb_to_hex(col))).size, bytes(m))
                text_col = tuple((c - col_avg) if col_avg > 127 else (c + (255-col_avg)) for c in col)
                text_pos = tuple((int(paste_x+width/2-text_img.width/2),int(paste_y+height-2-text_img.height)))
                text.paste(self.recolor_keep_alpha(text_img, text_col, text_img), text_pos)
                paste_x += padding + width
            self.Palette_Img = Image.alpha_composite(self.Palette_Img, temp)
            self.Palette_Img = Image.alpha_composite(self.Palette_Img, text)
        def update_palette():
            def _palette_click(event):
                coords = self.get_corners("palette")
                img_x = event.x - coords[0][0]
                img_y = event.y - coords[0][1]
                pixel_rgb = self.Palette_Img.getpixel((img_x,img_y))[0:3]
                if pixel_rgb in self.color_palette_rgb:
                    self.picked_color = self.rgb_to_hex(pixel_rgb)
                    self.mudae_character.mudae_image.color = self.picked_color
                    self.set_embed_bg()
            
            self.Palette_Img_TK = ImageTk.PhotoImage(self.Palette_Img)
            coord_list = self.get_corners("embed_bg")
            if coord_list != None:
                nw, sw, se, ne, cntr = coord_list
                spacing = 36 if len(self.mudae_character.images) > 1 else 0
                self.ui.create_image(cntr[0],sw[1]+4+spacing, image=self.Palette_Img_TK, anchor="n", tags=("palette"))
                self.ui.tag_bind("palette", "<ButtonPress-1>", _palette_click)

        img = self.mudae_character.image
        crop_img = img.crop((1, 1, img.width - 1, img.height - 1))
        pal_img = crop_img.quantize(colors=num_cols, kmeans=10, dither=Image.Dither.NONE)
        palette = pal_img.getpalette()[:num_cols * 3]
        colors = [
            tuple(palette[i:i+3])
            for i in range(0, len(palette), 3)
        ]
        self.color_palette_rgb = colors
        create_palette_image()
        update_palette()
    
### --- SET CHAR HISTORY --- ###
    @ui_decorator
    def set_char_history(self):
        self.remove_tag_bind(["history"])
        def _on_mousewheel(event):
            self.history_offset -= int(event.delta/120)
            self.set_char_history()
        def _on_click(event, index):
            self.history_offset = 0
            while self.character_index != index:
                self.character_index += 1 if self.character_index < index else -1
                self.set_char_history()
                self.ui.update()
            self.set_character(self.character_index)
        
        self.history_offset = max(ENTRY_COUNT/2 - self.character_index, min(self.history_offset, max(-ENTRY_COUNT/2, len(self.attr_list) - ENTRY_COUNT/2 - self.character_index)))
        bg_padding=20
        entry_padding=10
        if not hasattr(self, "history_img"):
            self.history_img = ImageTk.PhotoImage(self.create_widget_img(HISTORY_WIDTH, ((ENTRY_COUNT+1)*(ENTRY_HEIGHT+entry_padding)+entry_padding)))
        self.ui.create_image((bg_padding, bg_padding),
                             image=self.history_img, anchor='nw', tags=["history", "history_bg"])
        self.history_ttl = ImageTk.PhotoImage(self.text_on_img(self.create_widget_img(HISTORY_WIDTH, ENTRY_HEIGHT+entry_padding), 
                                                               f"{len(self.attr_list)} {"Character" if self.mudae_format else "Images"} loaded", font=self.pil_font_12_bold))
        self.ui.create_image((bg_padding, bg_padding),
                             image=self.history_ttl, anchor='nw', tags=["history", "history_ttl"])
        coord_list = self.get_corners(f"history_bg")
        if coord_list == None: return
        nw, ne = coord_list[0], coord_list[3]
        count = 1
        self.visible_index = []
        for i in range(max(0, int(self.character_index + self.history_offset - ENTRY_COUNT/2)), len(self.attr_list)):
            if count > ENTRY_COUNT: break
            self.visible_index.append(i)
            tag = self.attr_list[i].name
            if not tag: tag = self.attr_list[i].url
            if not tag: tag = self.attr_list[i].item
            tag = tag.replace('!', '').replace('&', '').replace('-', '').replace('~', '').replace(':', '').replace('\\', '').replace('"', '').replace("'", '')
            if not tag: return
            self.create_button(ne[0]-nw[0]-3*entry_padding-ENTRY_HEIGHT*2, ENTRY_HEIGHT, 
                               ne[0]-entry_padding, ne[1]+entry_padding+count*(ENTRY_HEIGHT+entry_padding), 'ne', 
                               tag, f"history_{tag}", active = (i < len(self.character_list)), 
                               color = self.character_list[i].mudae_image.color if i < len(self.character_list) else DEFAULT_COLOR,
                               font = self.pil_font_10_bold, txt_col = ("#FFFFFF" if i == self.character_index else L_GREY))
            self.create_button(ENTRY_HEIGHT*2, ENTRY_HEIGHT, nw[0]+entry_padding, 
                               nw[1]+entry_padding+count*(ENTRY_HEIGHT+entry_padding), 'nw',
                               f"{i+1}", f"history_{i}", lambda e, index=i : _on_click(e,index), 
                               active = (i < len(self.character_list)) and (i != self.character_index),
                               font = self.pil_font_10_bold)
            count += 1
        self.ui.bind("<MouseWheel>", _on_mousewheel)
    
### --- SET COPY COMMAND --- ###
    @ui_decorator
    def set_copy_command(self):
        def _on_click(event):
            self.ui.clipboard_clear()
            self.ui.clipboard_append("$mmyc-i-skr-dot")
            out_str = ''
            out_str += "───────────────────────╮\n"
            out_str += "  COPIED MUDAE COMMAND │\n"
            out_str += "╭──────────────────────╯\n"
            out_str += "├ Copied the command    \n"
            out_str += "│ please paste it in    \n"
            out_str += "│ the mudae channel.    \n"
            out_str += "│                       \n"
            out_str += "├ After all the dm's    \n"
            out_str += "│ are sent, copy-paste  \n"
            out_str += "│ or drag-and-drop the  \n"
            out_str += "│ dm's from mudae into  \n"
            out_str += "│ the app.         \n"
            out_str += "╯"
            print(out_str)
        coord_list = self.get_corners("history")
        if coord_list:
            nw, sw, se, ne, cntr = coord_list
            self.create_button(HISTORY_WIDTH-50, 32,
                               cntr[0], se[1]+4, "n",
                               "Copy Mudae command",
                               "mudae_command", _on_click)
    
### --- PLAY BUTTON --- ###
    @ui_decorator
    def set_play_button(self):
        if self.ui.find_withtag("play_button"):
            self.ui.unbind_all("<space>")
        self.remove_tag_bind(["play_button"], ["<ButtonRelease-1>"])
        if self.next_frame_call: self.ui.after_cancel(self.next_frame_call)
        def _schedule_next_frame(char:character, start=False):
            if start:
                self.tic = time.perf_counter()
            if not self.ui.winfo_exists(): return
            if char.src != self.mudae_character.src: return
            if not self.mudae_character.mudae_image: return
            if not (self.mudae_character.is_playing and self.mudae_character.mudae_image.is_animated): return

            next_frame = (self.mudae_character.mudae_image.frame + 1) % len(self.mudae_character.mudae_image.frames)
            self.mudae_character.mudae_image.set_frame(next_frame)
            duration = self.mudae_character.mudae_image.durations[next_frame]
            if start:
                self.last_dur = duration
            
            self.update_mudae_image()
            self.set_palette()
            
            self.ui.itemconfig("mudae_image", image=self.Mudae_Image)
            if self.ui.find_withtag("loupe"):
                self.update_loupe_img()
            
            duration -= (round((time.perf_counter()-self.tic)*1000) - self.last_dur)
            
            self.next_frame_call = self.ui.after(duration, lambda prev_char=char :_schedule_next_frame(prev_char))
            self.tic = time.perf_counter()
            self.last_dur = duration
        def _on_click(event):
            if self.mudae_character.is_playing:
                self.ui.after_cancel(self.next_frame_call)
                self.mudae_character.is_playing = False
                self.ui.itemconfig("play_button", image=self.play_TK)
            else:
                self.mudae_character.is_playing = True
                self.ui.itemconfig("play_button", image=self.pause_TK)
                _schedule_next_frame(self.mudae_character, True)
        
        if not self.mudae_character.mudae_image.is_animated:
            return

        corner = 'ne'
        coord_list = self.get_corners(f"corner_{corner}")
        if coord_list:
            pos = coord_list[('se','ne','nw','sw').index(corner)]
            if self.mudae_character.is_playing:
                self.ui.create_image(pos, image=self.pause_TK, anchor=corner, tags=("ui_overlay", "play_button"))
                _schedule_next_frame(self.mudae_character, True)
            else:
                self.ui.create_image(pos, image=self.play_TK, anchor=corner, tags=("ui_overlay", "play_button"))
            self.ui.tag_bind("play_button", "<ButtonRelease-1>", _on_click)
            self.ui.bind_all("<space>", _on_click)

### --- SET MUDAE IMAGE --- ###
    @ui_decorator
    def set_mudae_image(self):
        self.remove_tag_bind(["mudae_image", "mudae_image_corners"], ["<Motion>", "<Enter>", "<Leave>", "<ButtonRelease-1>"])
        loupe_dx = loupe_dy = 60
        def _on_move_mudae_image(event):
            if not self.mudae_character.mudae_image: return
            if not self.ui.find_withtag("loupe"):
                return
            self.ui.coords("loupe", (event.x-loupe_dx,event.y+loupe_dy))

            coord_list = self.get_corners("mudae_image")
            if coord_list:
                nw,sw,se,ne,cntr = coord_list
                mouse_coords = (max(0,min(se[0]-sw[0],event.x-nw[0])), max(0,min(se[1]-ne[1],event.y-nw[1])))
                pointer_percent = (mouse_coords[0]/(se[0]-sw[0]), mouse_coords[1]/(se[1]-ne[1]))
                image_coords = (int((self.mudae_character.image.width-1)*pointer_percent[0]),int((self.mudae_character.image.height-1)*pointer_percent[1]))
                self.image_coords = image_coords
                self.update_loupe_img()
        def _on_enter_mudae_image(event):
            self.ui.delete("loupe")
            self.loupe_img_TK = ImageTk.PhotoImage(self.loupe_img)
            self.ui.create_image(event.x-loupe_dx,event.y+loupe_dy, image=self.loupe_img_TK, anchor='center', tags=("loupe"))
            _on_move_mudae_image(event)
        def _on_leave_mudae_image(event):
            self.ui.delete("loupe")
        def _on_click_mudae_image(event):
            if not self.mudae_character.mudae_image: return
            self.picked_color = self.rgb_to_hex(self.mudae_character.image.getpixel(self.image_coords))
            self.mudae_character.mudae_image.set_color(self.picked_color)
            self.set_embed_bg()

        self.update_mudae_image()
        self.ui.create_image(self.ui_cntr[0],self.ui_cntr[1],image=self.Mudae_Image,anchor='center',tags=("mudae_image"))
        coord_list = self.get_corners("mudae_image")
        if coord_list != None:
            nw, sw, se, ne, cntr = coord_list
            self.ui.create_image(nw,image=self.arch_4x4_NW_TK,anchor='nw',tags=("ui_overlay", "corner_nw", "mudae_image_corners"))
            self.ui.create_image(sw,image=self.arch_4x4_SW_TK,anchor='sw',tags=("ui_overlay", "corner_sw", "mudae_image_corners"))
            self.ui.create_image(se,image=self.arch_4x4_SE_TK,anchor='se',tags=("ui_overlay", "corner_se", "mudae_image_corners"))
            self.ui.create_image(ne,image=self.arch_4x4_NE_TK,anchor='ne',tags=("ui_overlay", "corner_ne", "mudae_image_corners"))
        
        self.ui.tag_bind("mudae_image", "<Motion>", _on_move_mudae_image)
        self.ui.tag_bind("mudae_image", "<Enter>", _on_enter_mudae_image)
        self.ui.tag_bind("mudae_image", "<Leave>", _on_leave_mudae_image)
        self.ui.tag_bind("mudae_image", "<ButtonRelease-1>", _on_click_mudae_image)
        
        self.ui.tag_bind("mudae_image_corners", "<Motion>", _on_move_mudae_image)
        self.ui.tag_bind("mudae_image_corners", "<Enter>", _on_enter_mudae_image)
        self.ui.tag_bind("mudae_image_corners", "<Leave>", _on_leave_mudae_image)
        self.ui.tag_bind("mudae_image_corners", "<ButtonRelease-1>", _on_click_mudae_image)

### --- SET TEXT OVERLAY --- ###
    @ui_decorator
    def set_text_overlay(self):
        self.remove_tag_bind(["embed_txt"])
        
        top_offset = 8
        bot_offset = 8
        white_space_distance = 13
        font_10_distance = 17
        font_8_distance = 15
        image_str = '       '
        coord_list = self.get_corners("mudae_image")
        if not coord_list: return
        nw, sw, se, ne, cntr = coord_list
        char = self.mudae_character
        if char.series or char.info or char.rank or char.kakera or char.type or char.keys:
            top_offset += white_space_distance
            if char.mudae_image.title == 'Custom':
                self.ui.create_text(nw[0],nw[1]-top_offset,text='(Custom Pic)',anchor='sw',fill='white',font=self.font_10,tags=("char_imported", "embed_txt"))
                top_offset +=font_10_distance
            if char.info:
                self.ui.create_text(nw[0],nw[1]-top_offset,text=char.info,anchor='sw',fill='white',font=self.font_10,tags=("char_info", "embed_txt"))
                top_offset +=font_10_distance
            if char.rank:
                self.ui.create_text(nw[0],nw[1]-top_offset,text=f'Rank: #{char.rank:,}',anchor='sw',fill='white',font=self.font_10,tags=("char_rank", "embed_txt"))
                top_offset += font_10_distance
            if ('a' in char.type) or ('g' in char.type) or char.kakera or char.keys:
                t_k_y_txt = ''
                self.ui.create_text(nw[0],nw[1]-top_offset,text=t_k_y_txt,anchor='sw',fill='white',font=self.font_10,tags=("char_t_k_y", "embed_txt"))
                char_t_k_y_sw = self.get_corners("char_t_k_y")[1]
                if ('a' in char.type) or ('g' in char.type):
                    if t_k_y_txt: t_k_y_txt+=' · '
                    if ('a' in char.type) and not ('g' in char.type):
                        t_k_y_txt += 'Animanga roulette'
                    if not ('a' in char.type) and ('g' in char.type):
                        t_k_y_txt += 'Game roulette'
                    if ('a' in char.type) and ('g' in char.type):
                        t_k_y_txt += 'Game & Animanga'
                if char.kakera:
                    if t_k_y_txt: t_k_y_txt+=' · '
                    t_k_y_txt += f'{char.kakera:,}'
                    kakera_distance = self.font_10.measure(t_k_y_txt) + char_t_k_y_sw[0] + 2
                    self.ui.create_image(kakera_distance,char_t_k_y_sw[1]+1,image=self.emoji_dict_TK['kakera'],anchor='sw',tags=("embed_txt"))
                    t_k_y_txt += image_str
                if char.keys:
                    if t_k_y_txt: t_k_y_txt+=' · '
                    if char.keys >= 1: key_img = self.emoji_dict_TK['bronzekey']
                    if char.keys >= 3: key_img = self.emoji_dict_TK['silverkey']
                    if char.keys >= 6: key_img = self.emoji_dict_TK['goldkey']
                    if char.keys >= 10:key_img = self.emoji_dict_TK['chaoskey']
                    key_distance = self.font_10.measure(t_k_y_txt) + char_t_k_y_sw[0]
                    self.ui.create_image(key_distance,char_t_k_y_sw[1]+1,image=key_img,anchor='sw',tags=("embed_txt"))
                    t_k_y_txt += image_str
                    t_k_y_txt += f'({char.keys:,})'
                self.ui.itemconfig("char_t_k_y", text=t_k_y_txt)
                top_offset += font_10_distance
            if char.series:
                series_text, last_line_len = self.set_text_width(char.series,self.Mudae_Image.width(),self.font_10)
                self.ui.create_text(nw[0],nw[1]-top_offset,text=series_text,anchor='sw',fill='white',font=self.font_10,tags=("char_series", "embed_txt"))
                series_coords_sw = self.get_corners("char_series")[1]
                last_line_len += 2
                if 'w' in char.type:
                    pos = (series_coords_sw[0]+last_line_len, series_coords_sw[1]+1)
                    self.ui.create_image(pos,image=self.emoji_dict_TK['female'],anchor='sw',tags=("embed_txt"))
                    last_line_len += 18
                if 'h' in char.type:
                    pos = (series_coords_sw[0]+last_line_len, series_coords_sw[1]+1)
                    self.ui.create_image(pos,image=self.emoji_dict_TK['male'],anchor='sw',tags=("embed_txt"))
                    last_line_len += 18
                top_offset += font_10_distance * (1 + series_text.count('\n'))
        if char.name:
            top_offset += white_space_distance
            name_text = self.set_text_width(char.name,self.Mudae_Image.width(),self.font_10_bold)[0]
            self.ui.create_text(nw[0],nw[1]-top_offset,text=name_text,anchor='sw',fill='white',font=self.font_10_bold,tags=("char_name", "embed_txt"))
            top_offset += font_10_distance
        footer = self.make_footer_str()
        if footer:
            bot_offset += white_space_distance
            self.ui.create_text(sw[0],sw[1]+bot_offset,text=footer,anchor='sw',fill='white',font=self.font_8_bold,tags=("char_footer", "embed_txt"))
            bot_offset += font_8_distance

### --- SET EMBED --- ###
    @ui_decorator
    def set_embed_bg(self):
        self.remove_tag_bind(["embed_bg"])

        dx1,dx2,dy1,dy2 = 15,17,15,18
        coord_list_txt = self.get_corners('embed_txt')
        coord_list_img = self.get_corners("mudae_image")
        if bool(coord_list_txt) and bool(coord_list_img):
            nw_txt, sw_txt, se_txt, ne_txt, cntr_txt = coord_list_txt
            nw_img, sw_img, se_img, ne_img, cntr_img = coord_list_img
            
            frame_w = (max(se_img[0],se_txt[0])+dx2)-(min(nw_img[0],nw_txt[0])-dx1)
            frame_h = (max(se_img[1],se_txt[1])+dy2)-(min(nw_img[1],nw_txt[1])-dy1)
            anchor = (min(nw_img[0],nw_txt[0])-dx1, min(nw_img[1],nw_txt[1])-dy1)
            
            Embed_BG = self.create_widget_img(frame_w, frame_h)
            self.Embed_BG_TK = ImageTk.PhotoImage(self.recolor_strip(Embed_BG, self.picked_color))
            self.ui.itemconfig("embed_bg", image=self.Embed_BG_TK)
            self.ui.create_image(anchor,image=self.Embed_BG_TK,anchor='nw',tags=("embed_bg"))
            self.set_char_history()
        elif coord_list_img:
            nw_img, sw_img, se_img, ne_img, cntr_img = coord_list_img
            
            frame_w = (se_img[0]+dx2)-(nw_img[0]-dx1)
            frame_h = (se_img[1]+dy2)-(nw_img[1]-dy1)
            anchor = (nw_img[0]-dx1, nw_img[1]-dy1)

            Embed_BG = self.create_widget_img(frame_w, frame_h)
            self.Embed_BG_TK = ImageTk.PhotoImage(self.recolor_strip(Embed_BG, self.picked_color))
            self.ui.itemconfig("embed_bg", image=self.Embed_BG_TK)
            self.ui.create_image(anchor,image=self.Embed_BG_TK,anchor='nw',tags=("embed_bg"))
            self.set_char_history()

### --- INITS --- ###

    def get_images(self):
        self.rem_img = Image.open(ASSET_FOLDER / "rem.webp")
        self.ram_img = Image.open(ASSET_FOLDER / "ram.webp")
        
        self.emoji_dict_TK = {}
        emoji_names = ('female','male','chaoskey','goldkey','silverkey','bronzekey','kakera')
        for emoji in emoji_names:
            temp = Image.open(ASSET_FOLDER / f"{emoji}.webp")
            self.emoji_dict_TK[emoji] = ImageTk.PhotoImage(temp.resize((18,int((temp.height/temp.width)*18))))
        
        self.loupe_img = Image.open(ASSET_FOLDER / "Loupe(85x85).png")
        self.play_TK = ImageTk.PhotoImage(Image.open(ASSET_FOLDER / "Play(19x19).png"))
        self.pause_TK = ImageTk.PhotoImage(Image.open(ASSET_FOLDER / "Pause(19x19).png"))
        
        self.arch_5x5 = Image.open(ASSET_FOLDER / "FeatherArch(5x5).png")
        self.arch_4x4_NW:Image.Image = self.arch_5x5.resize((4,4))
        self.arch_4x4_SW:Image.Image = self.arch_4x4_NW.rotate(90, expand=True)
        self.arch_4x4_SE:Image.Image = self.arch_4x4_SW.rotate(90, expand=True)
        self.arch_4x4_NE:Image.Image = self.arch_4x4_SE.rotate(90, expand=True)
        
        self.arch_4x4_NW_TK = ImageTk.PhotoImage(self.arch_4x4_NW)
        self.arch_4x4_SW_TK = ImageTk.PhotoImage(self.arch_4x4_SW)
        self.arch_4x4_SE_TK = ImageTk.PhotoImage(self.arch_4x4_SE)
        self.arch_4x4_NE_TK = ImageTk.PhotoImage(self.arch_4x4_NE)
        
        self.Embed_corner_NW = Image.open(ASSET_FOLDER / "Embed_corner(5x5).png")
        self.Embed_corner_SW:Image.Image = self.Embed_corner_NW.rotate(90, expand=True)
        self.Embed_corner_SE:Image.Image = self.Embed_corner_SW.rotate(90, expand=True)
        self.Embed_corner_NE:Image.Image = self.Embed_corner_SE.rotate(90, expand=True)
        
        self.Embed_edge_T = Image.open(ASSET_FOLDER / "Embed_edge(1x5).png")
        self.Embed_edge_L:Image.Image = self.Embed_edge_T.rotate(90, expand=True)
        self.Embed_edge_B:Image.Image = self.Embed_edge_L.rotate(90, expand=True)
        self.Embed_edge_R:Image.Image = self.Embed_edge_B.rotate(90, expand=True)
    
    def get_fonts(self):
        self.font_10 = font.Font(family=FONT_FAM, size=10)
        self.font_10_bold = font.Font(family=FONT_FAM, size=10, weight="bold")
        self.font_8 = font.Font(family=FONT_FAM, size=8)
        self.font_8_bold = font.Font(family=FONT_FAM, size=8, weight="bold")
        self.pil_font_12 = ImageFont.truetype(FONTS_FOLDER / f"{FONT_FAM.upper()}_REGULAR.TTF", 12)
        self.pil_font_12_bold = ImageFont.truetype(FONTS_FOLDER / f"{FONT_FAM.upper()}_BOLD.TTF", 12)
        self.pil_font_10 = ImageFont.truetype(FONTS_FOLDER / f"{FONT_FAM.upper()}_REGULAR.TTF", 10)
        self.pil_font_10_bold = ImageFont.truetype(FONTS_FOLDER / f"{FONT_FAM.upper()}_BOLD.TTF", 10)
        self.pil_font_8 = ImageFont.truetype(FONTS_FOLDER / f"{FONT_FAM.upper()}_REGULAR.TTF", 8)
        self.pil_font_8_bold = ImageFont.truetype(FONTS_FOLDER / f"{FONT_FAM.upper()}_BOLD.TTF", 8)
    