import PySimpleGUI as sg
import PIL
import PIL.Image
import io
import base64
import win32clipboard
import win32con
import sys

"""
    Demo_Emoji_Toolbar

    Shows a toolbar of emoji icons and radio buttons to choose size.
    When an emoji is clicked, the image is placed on the clipboard.

    If this program is invoked with a location on the command line, then it will
    show the window at the indicated x,y location, get a SINGLE image click, and then close the window.
    The idea here is to emulate the Window+. behavior like on Windows that enables emojis to be added
    easily while typing in text.

    This program currently integrates with PySimpleHotkey to create this popup, single choice behavior.

    You can replace the emojis with your own version of emojis.  Use one of the many PySimpleGUI Base64 encoder demos
        to create your Base64 strings based on PNG files.  PNG is what enables a transparent background.
    
    6-Feb-2022
        The design took a turn and is kinda weird now... sorry about that but wanted to work in more emojis
        and the pre-rendering approach from before got replaced with a resizing of a single starting image.

    Copyright 2021, 2022 PySimpleGUI
"""

EMOJI_SIZES = (28, 56, 112)
NUM_ROWS = 3                # Number of rows of Emojis. Note, may be 1 more than this if there is an incomplete row

'''
MM"""""""`YM M""M M""MMMMMMMM    M""MMMMM""MM          dP                                     
MM  mmmmm  M M  M M  MMMMMMMM    M  MMMMM  MM          88                                     
M'        .M M  M M  MMMMMMMM    M         `M .d8888b. 88 88d888b. .d8888b. 88d888b. .d8888b. 
MM  MMMMMMMM M  M M  MMMMMMMM    M  MMMMM  MM 88ooood8 88 88'  `88 88ooood8 88'  `88 Y8ooooo. 
MM  MMMMMMMM M  M M  MMMMMMMM    M  MMMMM  MM 88.  ... 88 88.  .88 88.  ... 88             88 
MM  MMMMMMMM M  M M         M    M  MMMMM  MM `88888P' dP 88Y888P' `88888P' dP       `88888P' 
MMMMMMMMMMMM MMMM MMMMMMMMMMM    MMMMMMMMMMMM             88                                  
                                                          dP
'''


def make_square(im, min_size=256, fill_color=(0, 0, 0, 0)):
    """

    :param im:
    :type im:
    :param min_size:
    :type min_size:
    :param fill_color:
    :type fill_color:
    :return:
    :rtype:
    """

    x, y = im.size
    size = max(min_size, x, y)
    new_im = PIL.Image.new('RGBA', (size, size), fill_color)
    new_im.paste(im, (int((size - x) / 2), int((size - y) / 2)))
    return new_im


def convert_to_bytes(file_or_bytes, resize=None, fill=False):
    """
    Will convert into bytes and optionally resize an image that is a file or a base64 bytes object.
    Turns into  PNG format in the process so that can be displayed by tkinter
    :param file_or_bytes: either a string filename or a bytes base64 image object
    :type file_or_bytes:  (Union[str, bytes])
    :param resize:        optional new size
    :type resize:         (Tuple[int, int] or None)
    :param fill:          If True then the image is filled/padded so that the image is not distorted
    :type fill:           (bool)
    :return:              (bytes) a byte-string object
    :rtype:               (bytes)
    """

    if isinstance(file_or_bytes, str):
        img = PIL.Image.open(file_or_bytes)
    else:
        try:
            img = PIL.Image.open(io.BytesIO(base64.b64decode(file_or_bytes)))
        except Exception as e:
            dataBytesIO = io.BytesIO(file_or_bytes)
            img = PIL.Image.open(dataBytesIO)

    cur_width, cur_height = img.size
    if resize:
        new_width, new_height = resize
        scale = min(new_height / cur_height, new_width / cur_width)
        img = img.resize((int(cur_width * scale), int(cur_height * scale)), PIL.Image.ANTIALIAS)
    if fill:
        if resize is not None:
            img = make_square(img, resize[0])
    with io.BytesIO() as bio:
        img.save(bio, format="PNG")
        del img
        return bio.getvalue()


'''
M"""""`'"""`YM          dP                
M  mm.  mm.  M          88                
M  MMM  MMM  M .d8888b. 88  .dP  .d8888b. 
M  MMM  MMM  M 88'  `88 88888"   88ooood8 
M  MMM  MMM  M 88.  .88 88  `8b. 88.  ... 
M  MMM  MMM  M `88888P8 dP   `YP `88888P' 
MMMMMMMMMMMMMM                            

M""MMM""MMM""M oo                dP                     
M  MMM  MMM  M                   88                     
M  MMP  MMP  M dP 88d888b. .d888b88 .d8888b. dP  dP  dP 
M  MM'  MM' .M 88 88'  `88 88'  `88 88'  `88 88  88  88 
M  `' . '' .MM 88 88    88 88.  .88 88.  .88 88.88b.88' 
M    .d  .dMMM dP dP    dP `88888P8 `88888P' 8888P Y8P  
MMMMMMMMMMMMMM
'''


def make_toolbar(location=(None, None)):
    buttons_per_row = len(all_emojis)//NUM_ROWS
    button_rows = []
    for row_num in range(NUM_ROWS+1):
        row = []
        for i in range(buttons_per_row*row_num, buttons_per_row*(row_num+1)):
            try:        # The final row may be partial, so avoid crashing when overflowing beyond the total length
                row.append(sg.Button(image_data=all_emojis[i][2], border_width=0, tooltip=all_emojis[i][0], key=all_emojis[i]))
            except:
                pass
        button_rows.append(row)

    size_col = [sg.Col([[sg.Radio(s, 1, default=True if s == EMOJI_SIZES[1] else False, font='_ 6', k=i, pad=(0, 0))] for i, s in enumerate(EMOJI_SIZES)], pad=(0, 0))]
    layout = []
    for i, row in enumerate(button_rows):
        if i == 0:
            layout.append(sg.vtop(row+size_col))
        else:
            layout.append(row)

    return sg.Window('', layout, element_padding=(0, 0), margins=(0, 0), finalize=True, no_titlebar=True, grab_anywhere=True,
                     right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT, keep_on_top=True, button_color='black', location=location)


'''
M"""""`'"""`YM          oo          
M  mm.  mm.  M                      
M  MMM  MMM  M .d8888b. dP 88d888b. 
M  MMM  MMM  M 88'  `88 88 88'  `88 
M  MMM  MMM  M 88.  .88 88 88    88 
M  MMM  MMM  M `88888P8 dP dP    dP 
MMMMMMMMMMMMMM
'''


def main(location=(None, None)):
    sg.theme('dark black')
    window = make_toolbar(location)

    while True:
        event, values = window.read()
        # print(event, values)
        if event in ('Exit', sg.WIN_CLOSED):
            break
        elif event == 'Edit Me':
            sg.execute_editor(__file__)
        elif event == 'Version':
            sg.popup_scrolled(__file__, sg.get_versions(), location=window.current_location(), keep_on_top=True, non_blocking=True)
        elif event != sg.TIMEOUT_EVENT:
            emoji_data = None
            for e in all_emojis:
                if event == e:
                    print(f'you clicked {e[0]}')
                    emoji_data = e
                    break
            if emoji_data is None:
                continue
            size_index = [key for key in values.keys() if values[key]][0]
            size = EMOJI_SIZES[size_index]
            decoded = emoji_data[size_index + 1]

            output = io.BytesIO()
            image = PIL.Image.new("RGBA", (size, size))

            win32clipboard.OpenClipboard()
            win32clipboard.EmptyClipboard()

            image.save(output, "PNG")
            fmt = win32clipboard.RegisterClipboardFormat("PNG")
            win32clipboard.SetClipboardData(fmt, decoded)

            background = PIL.Image.new("RGB", image.size, (255, 255, 255))
            background.paste(image, mask=image.split()[3])
            output = io.BytesIO()
            background.save(output, 'BMP')
            data = output.getvalue()[14:]
            win32clipboard.SetClipboardData(win32con.CF_DIB, data)

            win32clipboard.CloseClipboard()
            break
        # If started in a specific location, then assume should get ONE selection then close
        if location != (None, None):
            break
    window.close()


if __name__ == '__main__':
    '''
    MM""""""""`M                     oo oo          
    MM  mmmmmmmM                                    
    M`      MMMM 88d8b.d8b. .d8888b. dP dP .d8888b. 
    MM  MMMMMMMM 88'`88'`88 88'  `88 88 88 Y8ooooo. 
    MM  MMMMMMMM 88  88  88 88.  .88 88 88       88 
    MM        .M dP  dP  dP `88888P' 88 dP `88888P' 
    MMMMMMMMMMMM                     88             
                                     dP
    '''
    # This is where the emoji images are defined
    # Each emoji is a tuple with (description, starting image)
    # Each image is the size found in the EMOJI_SIZES tuple
    # These are the starting images that will be resized.  They are LARGER than the version that is already in PySimpleGUI.
    e_cry = ('Cry',
             b'[encoded data, 16712 characters]')

    e_dead = ('Dead',
              b'[encoded data, 16072 characters]')

    e_dream = ('Dream',
               b'[encoded data, 16460 characters]')

    e_eyebrow = ('Eyebrow',
                 b'[encoded data, 14072 characters]')

    e_fingers_crossed = ('Fingers Crossed',
                         b'[encoded data, 19404 characters]')

    e_frust = ('Frustrated',
               b'[encoded data, 16900 characters]')

    e_gasp = ('Gasp',
              b'[encoded data, 14520 characters]')

    e_grimace = ('Grimace',
                 b'[encoded data, 13496 characters]')

    e_guess = ('Gruess',
               b'[encoded data, 20064 characters]')

    e_happy = ('Happy',
               b'[encoded data, 12540 characters]')

    e_clap = ('Clap',
              b'[encoded data, 16700 characters]')

    e_idea = ('Idea',
              b'[encoded data, 15008 characters]')

    e_joy = ('Joy',
             b'[encoded data, 17248 characters]')

    e_laugh = ('Laugh',
               b'[encoded data, 13344 characters]')

    e_love = ('Love',
              b'[encoded data, 16196 characters]')

    e_no_hear = ('No Hear',
                 b'[encoded data, 18792 characters]')

    e_no_see = ('No See',
                b'[encoded data, 18664 characters]')

    e_no_speak = ('No Speak',
                  b'[encoded data, 18628 characters]')

    e_palm = ('Face Palm',
              b'[encoded data, 17436 characters]')

    e_ponder = ('Ponder',
                b'[encoded data, 15500 characters]')

    e_pray = ('Pray',
              b'[encoded data, 18508 characters]')

    e_relief = ('Relief',
                b'[encoded data, 13772 characters]')

    e_smile = ('Smile',
               b'[encoded data, 12640 characters]')

    e_stare = ('Stare',
               b'[encoded data, 12428 characters]')

    e_think = ('Think',
               b'[encoded data, 17600 characters]')

    e_thumb = ('Thumb',
               b'[encoded data, 13532 characters]')

    e_weary = ('Weary',
               b'[encoded data, 13968 characters]')

    e_wink = ('Wink',
              b'[encoded data, 12728 characters]')

    e_zipped_shut = ('Zipped',
                     b'[encoded data, 16936 characters]')

    e_not_understand = ('Not Understand',
                        b'[encoded data, 19620 characters]')

    e_blank_stare = ('blank stare', b'[encoded data, 14580 characters]')

    e_cool = ('cool', b'[encoded data, 13552 characters]')

    e_eye_roll = ('eye roll', b'[encoded data, 13992 characters]')

    e_ok = ('ok', b'[encoded data, 17152 characters]')

    e_question = ('question',  b'[encoded data, 15916 characters]')

    e_skeptic = ('skeptic', b'[encoded data, 13248 characters]')

    e_sleeping = ('sleeping', b'[encoded data, 15584 characters]')

    e_tear = ('tear', b'[encoded data, 13688 characters]')

    e_upside_down = ('upside down', b'[encoded data, 13104 characters]')


    e_wizard = ('wizard', b'[encoded data, 20496 characters]')


    e_jedi = ('jedi',b'[encoded data, 14968 characters]')


    e_wave = ('wave', b'[encoded data, 16768 characters]')

    e_crazy = ('crazy', b'[encoded data, 15272 characters]')

    e_glasses = ('glasses', b'[encoded data, 14668 characters]')

    e_head_explode = ('head explode', b'[encoded data, 13080 characters]')

    e_laptop = ('laptop', b'[encoded data, 13380 characters]')

    e_party = ('party', b'[encoded data, 15060 characters]')

    e_rainedon = ('rained on', b'[encoded data, 21140 characters]')

    e_reading = ('reading', b'[encoded data, 12292 characters]')

    e_santa = ('santa', b'[encoded data, 14468 characters]')

    e_search = ('search', b'[encoded data, 19992 characters]')

    e_depressed = ('depressed', b'[encoded data, 13324 characters]')

    e_gold_star = ('Gold Star', b'[encoded data, 15508 characters]')

    e_honest = ('Honest', b'[encoded data, 14752 characters]')

    e_ill2 = ('Ill 2', b'[encoded data, 13156 characters]')

    e_ill = ('Ill', b'[encoded data, 15300 characters]')

    e_key = ('Key',b'[encoded data, 16020 characters]')

    e_mask = ('Mask', b'[encoded data, 14544 characters]')

    e_salute = ('Salute', b'[encoded data, 15064 characters]')

    e_scream = ('Scream', b'[encoded data, 14528 characters]')

    e_smirking = ('Smirking', b'[encoded data, 13144 characters]')

    e_warning2 = ('Warning2', b'[encoded data, 16668 characters]')

    e_warning = ('Warning', b'[encoded data, 14436 characters]')

    e_python_hearts = ('PyHearts', sg.PYTHON_COLORED_HEARTS_BASE64)
    e_psg_hero = ('PySimpleGUI Hero', sg.EMOJI_BASE64_SUPERHERO)

    all_emojis =  (e_happy, e_wave, e_search, e_santa, e_reading, e_blank_stare, e_stare, e_party,  e_laptop, e_head_explode, e_glasses,e_laugh, e_joy, e_idea, e_cool, e_relief, e_wink, e_thumb, e_love, e_clap, e_fingers_crossed, e_ok, e_pray, e_smile, e_guess, e_crazy, e_eye_roll, e_dream, e_sleeping, e_python_hearts, e_psg_hero, e_wizard, e_jedi, e_grimace, e_weary, e_rainedon, e_upside_down, e_think, e_frust, e_dead, e_gasp, e_tear, e_cry, e_no_speak, e_no_hear, e_no_see, e_zipped_shut, e_ponder, e_skeptic, e_palm, e_eyebrow, e_not_understand, e_question, e_depressed, e_ill, e_ill2, e_mask, e_warning, e_warning2, e_key, e_salute, e_honest, e_gold_star, e_scream, e_smirking)


    # Build the "all_emojis" data structure.  Converts the all_emojis tuple from 1 size to having 3 sizes
    # all_emojis = ('Description', smallest size, mid size, large size)

    new_all_emojis = []
    for e in all_emojis:
        description = e[0]
        base_emoji = e[1]
        new_emoji_info = [description, ]
        for s in EMOJI_SIZES:
            new_encoded = convert_to_bytes(base_emoji, (s, s))
            new_emoji_info.append(new_encoded)
        new_all_emojis.append(tuple(new_emoji_info))
    all_emojis = new_all_emojis

    if len(sys.argv) > 1:
        location = sys.argv[1]
        location = location.split(',')
        location = (int(location[0]), int(location[1]))
    else:
        location = (None, None)
    main(location)

