from tkinter import *
from tkinter import font as f
from tkinter.messagebox import *
from types import FunctionType

def mkRoot(title='ShrtCde window', size='500x500', image=None, mktoplevel=None, **kwargs):
    """
        Author: Udayshankar R

        Creates a Tkinter window and returns "root".

        KWARGS: minsize: str, maxsize: str, resizable: str, bg: str
    """

    minsize = list(kwargs['minsize'].split('x')) if 'minsize' in kwargs else ['0', '0']
    for i in range(len(minsize)): minsize[i] = int(minsize[i])
    maxsize = list(kwargs['maxsize'].split('x')) if 'maxsize' in kwargs else ['0', '0']
    for i in range(len(maxsize)): maxsize[i] = int(maxsize[i])

    resizeX = False if 'resizable' in kwargs and 'x' in kwargs['resizable'] else True
    resizeY = False if 'resizable' in kwargs and 'y' in kwargs['resizable'] else True
    
    bg = kwargs['bg'] if 'bg' in kwargs else None

    if mktoplevel == None: root = Tk()
    else: root = Toplevel(mktoplevel)

    root.title(title)
    root.geometry(size)
    root.resizable(resizeX, resizeY)

    if minsize != (0, 0): root.minsize(minsize[0], minsize[1])
    if maxsize != (0, 0): root.maxsize(maxsize[0], maxsize[1])

    if image != None:
        root.iconphoto(False, PhotoImage(file = image))

    if bg != None:
        root.config(bg=bg)
    
    return root
     
def mkFont(family="Calibri", size=15, weight="normal", underline=0, overstrike=0, slant="roman"):
    """
        Author: Udayshankar R

        Creates and returns a Tkinter font. Default = Calibri.
    """

    font = f.Font(family=family, size=size, weight=weight, underline=underline, overstrike=overstrike, slant=slant)

    return font

def mkMenu(root, commands):
    """
        Author: Udayshankar R

        Creates a menu for the tkinter window.
    """
    
    menu = Menu(root)

    # example
    # {"func" : lambda:print("lol"), "cascade" : {"func" : lambda:print("lol"), 0:0, ("func", "ctrl+lol") : lambda:print("lol")}}
    
    submenus = []
    if commands != {} and commands != None:
        for i in commands.keys():
            if isinstance(commands[i], FunctionType):
                menu.add_command(label=str(i), command=commands[i])
            elif isinstance(commands[i], dict):
                submenu = Menu(menu, tearoff=0)
                for j in commands[i].keys():
                    if j != 0: 
                        if isinstance(j, tuple): submenu.add_command(label=str(j[0]), accelerator=str(j[1]), command=commands[i][j])
                        else: submenu.add_command(label=str(j), command=commands[i][j])
                    else: submenu.add_separator()
                
                submenus.append(submenu)
                menu.add_cascade(label=i, menu=submenu)

    root.config(menu=menu)
    return menu, submenus

def mkLabel(root, text=None, image=None, **kwargs):
    """
        Author: Udayshankar R

        Creates and returns a Tkinter label. If image path is included, also returns image object.

        KWARGS: width: float, height: float, imgwidth: float, imgheight: float, font: tkinter.font, border: float, relief: str, fg: str, bg: str, hlcolor: str, hlsize: float
    """
    
    label = Label(root)

    border = kwargs['border'] if 'border' in kwargs else 1

    font = kwargs['font'] if 'font' in kwargs else mkFont()

    fg = kwargs['fg'] if 'fg' in kwargs else "Black"

    width = kwargs['width'] if 'width' in kwargs else None
    height = kwargs['height'] if 'height' in kwargs else None
    relief = kwargs['relief'] if 'relief' in kwargs else None
    imgwidth = kwargs['imgwidth'] if 'imgwidth' in kwargs else None
    imgheight = kwargs['imgheight'] if 'imgheight' in kwargs else None
    bg = kwargs['bg'] if 'bg' in kwargs else None
    hlcolor = kwargs['hlcolor'] if 'hlcolor' in kwargs else None
    hlsize = kwargs['hlsize'] if 'hlsize' in kwargs else None

    img = None
    if text != None:
        label = Label(root, text=text, font=font, fg=fg)
    elif image != None:
        img = PhotoImage(file=image)

        if imgwidth != None: img.config(width=imgwidth)
        if imgheight != None: img.config(height=imgheight)
        
        label = Label(root)
        label.config(image=img)
        label.image = img
    
    label.config(border=border)
    if width != None: label['width'] = width
    if height != None: label['height'] = height
    if relief != None: label['relief'] = relief
    if bg != None: label['bg'] = bg
    if hlcolor != None: label['highlightbackground'] = hlcolor
    if hlsize != None: label['highlightthickness'] = hlsize

    if img == None: return label
    else: return label, img

def mkText(root, **kwargs):
    """
        Author: Udayshankar R

        Creates and returns a Tkinter text widget.

        KWARGS: default:str, width: float, height: float, font: tkinter.font, border: float, relief: str, cursorwidth: float, cursorfg: str, fg: str, bg: str, hlcolor: str, activehlcolor: str, hlsize: float
    """
    
    text = Text(root)

    border = kwargs['border'] if 'border' in kwargs else 1
    cursorwidth = kwargs['cursorwidth'] if 'cursorwidth' in kwargs else 1

    font = kwargs['font'] if 'font' in kwargs else mkFont()

    cursorfg = kwargs['cursorfg'] if 'cursorfg' in kwargs else "Black"
    fg = kwargs['fg'] if 'fg' in kwargs else "Black"
    bg = kwargs['bg'] if 'bg' in kwargs else "White"
    default = kwargs['default'] if 'default' in kwargs else ""

    width = kwargs['width'] if 'width' in kwargs else None
    height = kwargs['height'] if 'height' in kwargs else None
    relief = kwargs['relief'] if 'relief' in kwargs else None
    hlcolor = kwargs['hlcolor'] if 'hlcolor' in kwargs else None
    activehlcolor = kwargs['activehlcolor'] if 'activehlcolor' in kwargs else None
    hlsize = kwargs['hlsize'] if 'hlsize' in kwargs else None

    text.config(font=font, border=border, insertwidth=cursorwidth, insertbackground=cursorfg, fg=fg, bg=bg)
    
    if width != None: text.config(width=width)
    if height != None: text.config(height=height)
    if relief != None: text.config(relief=relief)
    if hlcolor != None: text.config(highlightbackground=hlcolor)
    if activehlcolor != None: text.config(highlightcolor=activehlcolor)
    if hlsize != None: text.config(highlightthickness=hlsize)

    text.insert('0.0', default)

    return text

def mkButton(root, text=None, image=None, **kwargs):
    """
        Author: Udayshankar R

        Creates and returns a tkinter button which responds to function. If image path is included, also returns image object.

        KWARGS: function: function, width: float, height: float, imgwidth: float, imgheight: float, font: tkinter.font, border: float, relief: str, fg: str, bg: str, activefg: str, activebg: str
    """

    button = Button(root)

    border = kwargs['border'] if 'border' in kwargs else 1

    font = kwargs['font'] if 'font' in kwargs else mkFont()

    fg = kwargs['fg'] if 'fg' in kwargs else "Black"
    activefg = kwargs['activefg'] if 'activefg' in kwargs else "Black"

    function = kwargs['function'] if 'function' in kwargs else None
    width = kwargs['width'] if 'width' in kwargs else None
    height = kwargs['height'] if 'height' in kwargs else None
    imgwidth = kwargs['imgwidth'] if 'imgwidth' in kwargs else None
    imgheight = kwargs['imgheight'] if 'imgheight' in kwargs else None
    relief = kwargs['relief'] if 'relief' in kwargs else None
    bg = kwargs['bg'] if 'bg' in kwargs else None
    activebg = kwargs['activebg'] if 'activebg' in kwargs else None

    img = None
    if text != None:
        button = Button(root, text=text, font=font)
    elif image != None:
        img = PhotoImage(file=image)

        if imgwidth != None: img.config(width=imgwidth)
        if imgheight != None: img.config(height=imgheight)
        
        button = Button(root)
        button.config(image=img)
        button.image = img

    button.config(border=border, fg=fg, activeforeground=activefg)
    if function != None: button.config(command=function)
    if width != None: button['width'] = width
    if height != None: button['height'] = height
    if relief != None: button['relief'] = relief
    if bg != None: button['bg'] = bg
    if activebg != None: button['activebackground'] = activebg
        
    if img == None: return button
    else: return button, img

def mkDropdown(root, options, **kwargs):
    """
        Author: Udayshankar R

        Creates and returns a tkinter dropdown with StringVar.

        KWARGS: vartype: str/int/float/bool, function: function, width: float, height: float, font: tkinter.font, border: float, relief: str, fg: str, bg: str, activefg: str, activebg: str, hlcolor: str, hlsize: float
    """

    border = kwargs['border'] if 'border' in kwargs else 1

    font = kwargs['font'] if 'font' in kwargs else mkFont()

    fg = kwargs['fg'] if 'fg' in kwargs else "Black"
    activefg = kwargs['activefg'] if 'activefg' in kwargs else "Black"

    vartype = kwargs['vartype'] if 'vartype' in kwargs else None
    function = kwargs['function'] if 'function' in kwargs else None
    width = kwargs['width'] if 'width' in kwargs else None
    height = kwargs['height'] if 'height' in kwargs else None
    relief = kwargs['relief'] if 'relief' in kwargs else None
    bg = kwargs['bg'] if 'bg' in kwargs else None
    activebg = kwargs['activebg'] if 'activebg' in kwargs else None
    hlcolor = kwargs['hlcolor'] if 'hlcolor' in kwargs else None
    hlsize = kwargs['hlsize'] if 'hlsize' in kwargs else None

    if isinstance(vartype, int): clicked = IntVar()
    elif isinstance(vartype, float): clicked = DoubleVar()
    elif vartype == 'BOOL': clicked = BooleanVar()
    elif vartype == None or isinstance(vartype, str): clicked = StringVar(root)

    def call_function(x):
        if function != None: function()
        pass

    clicked.set(options[0])
    dropdown = OptionMenu(root, clicked, *options, command=call_function)

    dropdown.config(font=font, fg=fg, activeforeground=activefg, border=border)

    if width != None: dropdown['width'] = width
    if height != None: dropdown['height'] = height
    if relief != None: dropdown['relief'] = relief
    if bg != None: dropdown['bg'] = bg
    if activebg != None: dropdown['activebackground'] = activebg
    if hlcolor != None: dropdown['highlightbackground'] = hlcolor
    if hlsize != None: dropdown['highlightthickness'] = hlsize

    dropdown.pack()

    return dropdown, clicked

def mkEntry(root, **kwargs):
    """
        Author: Udayshankar R

        Creates and returns a tkinter entry with StringVar.

        KWARGS: default:str, width: float, font: tkinter.font, border: float, relief: str, cursorwidth: float, cursorfg: str, fg: str, bg: str, hlcolor: str, activehlcolor: str, hlsize: float
    """
    
    intext = StringVar(root)
    entry = Entry(root, textvariable=intext)

    border = kwargs['border'] if 'border' in kwargs else 1
    cursorwidth = kwargs['cursorwidth'] if 'cursorwidth' in kwargs else 1

    font = kwargs['font'] if 'font' in kwargs else mkFont()

    default = kwargs['default'] if 'default' in kwargs else ""
    cursorfg = kwargs['cursorfg'] if 'cursorfg' in kwargs else "Black"
    fg = kwargs['fg'] if 'fg' in kwargs else "Black"

    width = kwargs['width'] if 'width' in kwargs else None
    relief = kwargs['relief'] if 'relief' in kwargs else None
    bg = kwargs['bg'] if 'bg' in kwargs else None
    hlcolor = kwargs['hlcolor'] if 'hlcolor' in kwargs else None
    activehlcolor = kwargs['activehlcolor'] if 'activehlcolor' in kwargs else None
    hlsize = kwargs['hlsize'] if 'hlsize' in kwargs else None

    entry.config(font=font, border=border, insertbackground=cursorfg, insertwidth=cursorwidth, fg=fg)

    if width != None: entry['width'] = width
    if relief != None: entry['relief'] = relief
    if bg != None: entry['bg'] = bg
    if hlcolor != None: entry['highlightbackground'] = hlcolor
    if activehlcolor != None: entry['highlightcolor'] = activehlcolor
    if hlsize != None: entry['highlightthickness'] = hlsize

    intext.set(default)

    return entry, intext

def mkScale(root, start, end, orient, **kwargs):
    """
        Author: Udayshankar R

        Creates and returns a scale widget.

        KWARGS: default: float, text: str, mindist: float, width: float, length: float, font: tkinter.font, border: float, relief: str, troughcolor: str, activefg: str, fg: str, bg: str, hlcolor: str, hlsize: float
    """

    scale = Scale(root, from_=start, to=end, orient=orient)
    
    default = kwargs['default'] if 'default' in kwargs else 0
    mindist = kwargs['mindist'] if 'mindist' in kwargs else 1
    border = kwargs['border'] if 'border' in kwargs else 1

    font = kwargs['font'] if 'font' in kwargs else mkFont()

    text = kwargs['text'] if 'text' in kwargs else None
    width = kwargs['width'] if 'width' in kwargs else None
    length = kwargs['length'] if 'length' in kwargs else None
    relief = kwargs['relief'] if 'relief' in kwargs else None
    troughcolor = kwargs['troughcolor'] if 'troughcolor' in kwargs else None
    activefg = kwargs['activefg'] if 'activefg' in kwargs else None
    fg = kwargs['fg'] if 'fg' in kwargs else None
    bg = kwargs['bg'] if 'bg' in kwargs else None
    hlcolor = kwargs['hlcolor'] if 'hlcolor' in kwargs else None
    hlsize = kwargs['hlsize'] if 'hlsize' in kwargs else None

    scale.config(resolution=mindist, border=border, font=font)

    if text != None: scale['label'] = text
    if width != None: scale['width'] = width
    if length != None: scale['length'] = length
    if relief != None: scale['relief'] = relief
    if troughcolor != None: scale['troughcolor'] = troughcolor
    if activefg != None: scale['activebackground'] = activefg
    if fg != None: scale['fg'] = fg
    if bg != None: scale['bg'] = bg
    if hlcolor != None: scale['highlightbackground'] = hlcolor
    if hlsize != None: scale['highlightthickness'] = hlsize

    scale.set(default)

    return scale
