import sys
import threading
import subprocess
from globals import *

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"])

import keyboard
from mudae import char_attr, character

__author__ = "Jochem van Dolder"
__license__ = "MIT"
__all__ = ["app"]
__name__ = "Color Picker"

ASSET_FOLDER = Path(cwd) / "Assets"

### --- MAIN APP CLASS --- ###

class app(default_app):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        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.drop_target_register("*")
        self.ui.dnd_bind("<<Drop>>", _on_drop)
        self.create_button(26,26,self.ui_size[0]-4,4,anchor='ne',tags=["dnd", "ui_overlay"])
        self.create_textbox(20,20,self.ui_size[0]-6,8,anchor='ne',text=":drag_n_drop14:",tags=["dnd", "ui_overlay"],bg_col=EMB_BG)
        self.ui.bind_all("<Control-v>", _on_paste)
        self.register_binding("<Control-v>", ":paste14: Paste")
        
        self.get_images()
        
        self.mudae_format = True
        self.next_frame_call = None
        
        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.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 raise_tags(self):
        self.ui.tag_raise("mudae_image")
        # self.ui.tag_raise("embed_txt")
        # self.ui.tag_raise("loupe")
        self.ui.tag_raise("ui_overlay")
        
        self.ui.tag_lower("history")
        self.ui.tag_lower("mudae_command")
    
    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.attr_list[i] = char.attr
                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))
                self.ui.after(0, self.build_ui)
            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.loupe.config(image=self.loupe_img_TK)

    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 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()-FONTS["8b"].measure(images_str)
        
        if self.mudae_character.owner:
            footer = f"Belongs to {self.mudae_character.owner}"
            for i in range(len(footer)):
                if FONTS["8b"].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 FONTS["8b"].measure(footer[:i]) <= text_width: continue
                footer = footer[:i-1]
                break
            footer += images_str
            return footer
    
### --- CHARACTER --- ###
    def parse_character(self, source:char_attr|str):
        if not source: return
        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(100-4, 32,
                               cntr[0], se[1]+4, "n",
                               "set_color", "Color Command", _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("set_color")
        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(100-4,32,
                               ne[0]+4,ne[1],"nw",
                               "next_char", "Next Character", _on_click, active = active)
            if active and self.ui.find_withtag("next_char"):
                self.ui.bind_all("<Right>", _on_click)
                self.ui.bind_all("<Down>", _on_click)
                self.register_binding(["<Right>","<Down>"], ":arrow_right14::arrow_down14: Next character")
            else:
                self.register_binding(["<Right>","<Down>"])

    @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("set_color")
        if coord_list:
            nw, sw, se, ne, cntr = coord_list
            active = False if self.character_index == 0 else True
            self.create_button(100-4, 32,
                               nw[0]-4, nw[1], "ne",
                               "prev_char", "Prev Character", _on_click, active = active)
            if active and self.ui.find_withtag("prev_char"):
                self.ui.bind_all("<Left>", _on_click)
                self.ui.bind_all("<Up>", _on_click)
                self.register_binding(["<Left>","<Up>"], ":arrow_left14::arrow_up14: Prev character")
            else:
                self.register_binding(["<Left>","<Up>"])

### --- 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 not self.ui.focus_get():
                if self.hotkey:
                    self.hotkey = keyboard.remove_hotkey(self.hotkey)
                    self.hotkey = keyboard.add_hotkey("esc", _on_cancel)
                _next_char(0)
            else:
                out_str = ''
                out_str += "─────────────────────╮  \n"
                out_str += " COMMAND PASTE ERROR │  \n"
                out_str += "╭────────────────────╯  \n"
                out_str += "├ MudaeApps focused     \n"
                out_str += "│ focus discord then    \n"
                out_str += "│ try again             \n"
                out_str += "╯"
                print(out_str)
        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",
                               "set_paste", "Start Pasting Commands", _on_click)

### --- SET PALETTE --- ###
    @ui_decorator
    def set_palette(self, num_cols=NUM_COLORS):
        num_cols = max(1, min(num_cols, 9))
        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()
        self.remove_tag_bind(["palette"], ["<ButtonRelease-1>"])
        [self.ui.unbind_all(f"{num+1}") for num in range(num_cols)]
        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 := FONTS["pil_10b"].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"))
            if self.ui.find_withtag("palette"):
                self.ui.tag_bind("palette", "<ButtonPress-1>", _palette_click)
                [self.ui.bind_all(f"{num+1}", _on_key) for num in range(num_cols)]
                self.register_binding([f"{num+1}" for num in range(num_cols)], f"{''.join([f":{NUMBERS_DICT[num+1]}14:" for num in range(num_cols)])} Pick color")
            else:
                self.register_binding([f"{num+1}" for num in range(num_cols)])

        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=FONTS["pil_12b"]))
        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', 
                               f"history_{tag}", 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 = FONTS["pil_10b"], 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"history_{i}", f"{i+1}", lambda e, index=i : _on_click(e,index), 
                               active = (i < len(self.character_list)) and (i != self.character_index),
                               font = FONTS["pil_10b"])
            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",
                               "mudae_command", "Copy 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)
                self.register_binding("<space>", ":space_bar14: Start gif")
            else:
                self.mudae_character.is_playing = True
                self.ui.itemconfig("play_button", image=self.pause_TK)
                _schedule_next_frame(self.mudae_character, True)
                self.register_binding("<space>", ":space_bar14: Stop gif")
        
        if not self.mudae_character.mudae_image.is_animated:
            self.register_binding("<space>")
            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)
            self.register_binding("<space>", ":space_bar14: Stop gif" if self.mudae_character.is_playing else ":space_bar14: Start gif")

### --- 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.loupe = tk.Label(image=self.loupe_img_TK, bg="white", borderwidth=0)
            self.ui.create_window(event.x-loupe_dx,event.y+loupe_dy, window=self.loupe, 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"])
        char = self.mudae_character
        if char.name or char.series or char.info or char.rank or char.kakera or char.type or char.keys:
            info_text = ""
            if char.name:
                if info_text: info_text += '\n'
                info_text += f"*{char.name}*\n"
            if char.series:
                if info_text: info_text += '\n'
                info_text += char.series
                if 'w' in char.type:
                    info_text += ':female_sign:'
                if 'h' in char.type:
                    info_text += ':male_sign:'
            if ('a' in char.type) or ('g' in char.type) or char.kakera or char.keys:
                if info_text: info_text += '\n'
                if ('a' in char.type) or ('g' in char.type):
                    if info_text: info_text += ' · '
                    if ('a' in char.type) and not ('g' in char.type):
                        info_text += 'Animanga roulette'
                    if not ('a' in char.type) and ('g' in char.type):
                        info_text += 'Game roulette'
                    if ('a' in char.type) and ('g' in char.type):
                        info_text += 'Game & Animanga'
                if char.kakera:
                    if ('a' in char.type) or ('g' in char.type):
                        if info_text: info_text += ' · '
                    info_text += f'*{char.kakera:,}*:kakera:'
                if char.keys:
                    if info_text: info_text += ' · '
                    if char.keys >= 1: key_img = ':bronzekey:'
                    if char.keys >= 3: key_img = ':silverkey:'
                    if char.keys >= 6: key_img = ':goldkey:'
                    if char.keys >= 10:key_img = ':chaoskey:'
                    info_text += f'{key_img}({char.keys:,})'
            if char.rank:
                if info_text: info_text += '\n'
                info_text += f'Rank: #{char.rank:,}'
            if char.info:
                if info_text: info_text += '\n'
                info_text += char.info
            if char.mudae_image.title == 'Custom':
                if info_text: info_text += '\n'
                info_text += '(Custom Pic)'
        
            coord_list = self.get_corners("mudae_image")
            if not coord_list: return
            nw, sw, se, ne, cntr = coord_list
            self.create_textbox(w=self.Mudae_Image.width()+10, h=0, x=nw[0], y=nw[1]-13-8, anchor='sw', font=FONTS['10'],
                                text=info_text, tags=("info_embed_txt", "embed_txt"), txt_col="white", bg_col=EMB_BG)

        footer = self.make_footer_str()
        if footer:
            self.create_textbox(w=self.Mudae_Image.width()+10, h=0, x=sw[0], y=sw[1]+13+8, anchor='sw', font=FONTS['8'],
                                text=f"*{footer}*", tags=("footer_embed_txt", "embed_txt"), txt_col="white", bg_col=EMB_BG)
        
### --- 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(EMOJI_FOLDER / "rem.webp")
        self.ram_img = Image.open(EMOJI_FOLDER / "ram.webp")
        
        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)