import os
import tkinter as tk
from tkinter import filedialog as fd
from tkinter.messagebox import showinfo, askyesno, askokcancel
from tkinter import ttk
from tooltip import Hovertip
from pathlib import Path
from deploy import download_ffmpeg, download_git, deploy, get_ffmpeg_version, update
import logging
import sys
import re
import unicodedata


def slugify(value, allow_unicode=False):
    """
    Taken from https://github.com/django/django/blob/master/django/utils/text.py
    Convert to ASCII if 'allow_unicode' is False. Convert spaces or repeated
    dashes to single dashes. Remove characters that aren't alphanumerics,
    underscores, or hyphens. Convert to lowercase. Also strip leading and
    trailing whitespace, dashes, and underscores.
    """
    value = str(value)
    if allow_unicode:
        value = unicodedata.normalize('NFKC', value)
    else:
        value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii')
    value = re.sub(r'[^\w\s-]', '', value.lower())
    return re.sub(r'[-\s]+', '-', value).strip('-_')


def get_curr_screen_geometry():
    """
    Workaround to get the size of the current screen in a multi-screen setup.

    Returns:
        geometry (str): The standard Tk geometry string.
            [width]x[height]+[left]+[top]
    """
    root = tk.Tk()
    root.update_idletasks()
    root.attributes('-fullscreen', True)
    root.state('iconic')
    geometry = root.winfo_geometry()
    root.destroy()

    return geometry


def create_name_frame(container):
    frame = ttk.LabelFrame(container, text='Mod Name')

    # Override Workshop checkbox
    override_workshop = tk.StringVar()
    override_workshop.set("0")
    override_workshop_check = ttk.Checkbutton(
        frame,
        variable=override_workshop,
        text='Override Workshop',
        command=lambda: override_action())

    override_workshop_check.pack(side="left", padx=3, pady=3)

    name = tk.StringVar()
    name.set("Sunken Tapez")

    name_entry = ttk.Entry(frame, width=30, textvariable=name)
    name_entry.pack(side="left", padx=3, pady=3, expand=True, fill="x")

    slug = tk.StringVar()
    slug.set(slugify("Sunken Tapez"))

    slug_entry = ttk.Entry(frame, width=30, textvariable=slug)
    slug_entry["state"] = "disable"
    slug_entry.pack(side="left", padx=3, pady=3, expand=True, fill="x")

    Hovertip(override_workshop_check,
             'Keep this unchecked to prevent Steam Workshop'
             '\noverriding your custom installation.'
             '\n\nIt exists only for the author\'s convenience.')

    Hovertip(name_entry,
             'Name of the deploy mod. You can customise this'
             '\nif you want to create your own version.')

    Hovertip(slug_entry,
             'Slug of the deployed mod name that is'
             '\nused for file paths and references.')

    def update_slug(*args):
        slug.set(slugify(name.get()))

    name.trace("w", update_slug)

    def override_action():
        if override_workshop.get() == "1":
            name.set("Sunken Tapes")
            name_entry["state"] = "disable"
            slug.set("sunken_tapes")
        elif override_workshop.get() == "0":
            name.set("Sunken Tapez")
            name_entry["state"] = "normal"

    return frame, name, slug


def create_install_frame(container):
    def find_barotrauma_directory():
        first_guess = Path("C:/Program Files (x86)/Steam/steamapps/common/Barotrauma")
        second_guess = Path("D:/Steam/steamapps/common/Barotrauma")

        if first_guess.is_dir():
            return first_guess.as_posix()
        elif second_guess.is_dir():
            return second_guess.as_posix()
        else:
            return False

    def select_barotrauma_directory():
        return_dir = fd.askdirectory(initialdir="C:/")
        install_dir.set(return_dir)

    frame = ttk.LabelFrame(container, text='Barotrauma installation directory')

    find_button = ttk.Button(frame, text='Guess',
                             command=lambda: install_dir.set(find_barotrauma_directory()))
    find_button.pack(side="left", padx=3, pady=3)

    install_dir = tk.StringVar()
    install_dir.set(find_barotrauma_directory())

    directory_entry = ttk.Entry(frame, width=60, textvariable=install_dir)
    directory_entry.pack(side="left", padx=3, pady=3, expand=True, fill="x")

    browse_button = ttk.Button(frame, text='Browse', command=select_barotrauma_directory)
    browse_button.pack(side="left", padx=3, pady=3)

    return frame, install_dir


def create_options_frame(container):
    frame = ttk.LabelFrame(container, text='Options')

    # Use ITA checkbox
    use_ita = tk.StringVar()
    use_ita.set("0")
    use_ita_check = ttk.Checkbutton(
        frame,
        variable=use_ita,
        text='Use ITA mod',
        command=lambda: print(use_ita.get()))

    # Buffs checkbox
    buffs = tk.StringVar()
    buffs.set("1")
    buffs_check = ttk.Checkbutton(
        frame,
        variable=buffs,
        text='Buffs',
        command=lambda: print(buffs.get()))

    Hovertip(use_ita_check,
             'Check this box if you use Into The Abyss mod.'
             '\nITA and Sunken Tapes override the same style file.')

    Hovertip(buffs_check,
             'Check this box to enable some songs causing strange effects'
             '\nThis is the intended default behaviour.')

    for widget in frame.winfo_children():
        widget.pack(side="top", padx=3, pady=3, fill="x")

    return frame, use_ita, buffs


def create_resolution_frame(container):
    frame = ttk.LabelFrame(container, text='Resolution')

    def detect_screen_resolution():
        w, h = get_curr_screen_geometry().split("+")[0].split("x")
        width.set(w)
        height.set(h)

    width = tk.StringVar()
    width.set("1920")
    width_label = ttk.Label(frame, text='Width:')
    width_frame = ttk.Entry(frame, width=12, textvariable=width)

    height = tk.StringVar()
    height.set("1080")
    height_label = ttk.Label(frame, text='Height:')
    height_frame = ttk.Entry(frame, width=12, textvariable=height)

    detect_button = ttk.Button(frame, text='Detect', command=detect_screen_resolution)

    for widget in frame.winfo_children():
        widget.pack(side="left", padx=3, pady=3)

    return frame, width, height


def create_deploy_frame(container, config):
    def deploy_action():
        config_values = {"installdir": config["installdir"].get(),
                         "use_ita": config["use_ita"].get() == "1",
                         "buffs": config["buffs"].get() == "1",
                         "name": config["name"].get(),
                         "slug": config["slug"].get(),
                         "resolution_x": int(config["resolution_x"].get()),
                         "resolution_y": int(config["resolution_y"].get())}

        logging.info(f"deploying with config: {config_values}")
        deploy(config_values)

        if config_values["name"] != "Sunken Tapes":
            note = f'\n\nThis is a custom version and is named {config_values["name"]} to differentiate it from ' \
                   + 'the Steam Workshop version that would overwrite it otherwise.' \
                   + '\n\nPlease include a link to the original mod if you publish it to Steam Workshop.'
        else:
            note = ""

        showinfo(title='Success!',
                 message=f'{config_values["name"]} was successfully installed to:'
                         f'\n\n{config_values["installdir"]}/Mods/{config_values["slug"]}'
                         f'\n\nThis installer will now close.'
                         f'{note}')

        container.destroy()

    def download_ffmpeg_action():
        def finish():
            does_ffmpeg_exists()
            showinfo(title='Success!',
                     message=f'ffmpeg downloaded. You can now deploy Sunken Tapes:')

        if not Path("./utils/ffmpeg-" + get_ffmpeg_version() + "-full_build/bin/ffmpeg.exe").exists():
            download_ffmpeg()
            finish()
            return
        else:
            answer = askyesno(title='ffmpeg already downloaded',
                              message='ffmpeg is already in utils directory!'
                                      '\n\nDelete the existing one and download again?')

        if answer:
            download_ffmpeg(clean=True)
            finish()
        else:
            return

    def update_action():
        if not Path("./utils/git").exists():
            answer = askokcancel(
                title='Confirmation',
                message='Portable version of git will be downloaded.'
                        '\nThis is needed to check for updates in the repository')
            if answer:
                download_git()
            else:
                return
        else:
            logging.info(f"git already in utils")

        update()

    def does_ffmpeg_exists():
        if Path("./utils/ffmpeg-" + get_ffmpeg_version() + "-full_build/bin/ffmpeg.exe").exists():
            deploy_button["state"] = "normal"
            download_tools_button["state"] = "disable"
        else:
            deploy_button["state"] = "disable"
            download_tools_button["state"] = "normal"

    frame = ttk.LabelFrame(container, text='Install')

    frame_1 = ttk.Frame(frame)

    download_tools_label = ttk.Label(frame_1, text='Download and unpack ffmpeg:')
    download_tools_label.pack(side="left", padx=3, pady=3)
    download_tools_button = ttk.Button(frame_1, text='Download', command=download_ffmpeg_action)
    download_tools_button.pack(side="right", padx=3, pady=3)

    frame_1.pack(side="top", fill="x")

    frame_2 = ttk.Frame(frame)

    update_label = ttk.Label(frame_2, text='Download update from git.kompot.si/jaka/barotrauma-sunken-tapes:')
    update_label.pack(side="left", padx=3, pady=3)
    update_button = ttk.Button(frame_2, text='Update', command=update_action)
    update_button.pack(side="right", padx=3, pady=3)

    frame_2.pack(side="top", fill="x")

    frame_3 = ttk.Frame(frame)

    deploy_label = ttk.Label(frame_3, text='Download songs and deploy mod to Barotrauma Mods directory:')
    deploy_label.pack(side="left", padx=3, pady=3)
    deploy_button = ttk.Button(frame_3, text='Deploy', command=deploy_action)
    deploy_button.pack(side="right", padx=3, pady=3)

    frame_3.pack(side="top", fill="x")

    does_ffmpeg_exists()

    return frame


def create_main_window():
    # root window
    root = tk.Tk()
    root.title('Sunken Tapes')
    middle_frame = ttk.Frame(root)

    name_frame, name, slug = create_name_frame(root)
    install_frame, install_dir = create_install_frame(root)
    options_frame, use_ita, buffs = create_options_frame(middle_frame)
    resolution_frame, width, height = create_resolution_frame(middle_frame)

    config = {"installdir": install_dir,
              "use_ita": use_ita,
              "buffs": buffs,
              "name": name,
              "slug": slug,
              "resolution_x": width,
              "resolution_y": height}

    deploy_frame = create_deploy_frame(root, config)

    name_frame.pack(side="top", fill="x", pady=3, padx=3)
    install_frame.pack(side="top", fill="x", pady=3, padx=3)
    options_frame.pack(side="left", fill="y", pady=3, padx=3)
    resolution_frame.pack(side="left", fill="both", pady=3, padx=3, expand=True)
    middle_frame.pack(side="top", fill="x")
    deploy_frame.pack(side="top", fill="x", pady=3, padx=3)

    root.mainloop()


if __name__ == "__main__":
    os.chdir(Path(__file__).parents[0])
    logging.basicConfig(stream=sys.stdout, level=logging.INFO)
    create_main_window()
