import customtkinter
import PIL
import data_backend
import client_details
import pgwrapper
import sys
import json
import os
import cache_utils
import imagetools
import webbrowser

customtkinter.set_appearance_mode("dark")
customtkinter.set_default_color_theme("blue")

app = customtkinter.CTk()

app.geometry("1490x700")
last_geometry = app.winfo_geometry()

scrollable_frame = customtkinter.CTkScrollableFrame(app, width=app.winfo_width(), height=app.winfo_height())

buttons = []
details_elements = []

non_disabled_entry_color = None

db = None # app data-backend (i.e. LocalFS or FTP)

CONFIG_FILE = "gamevault_config.json"

def close_input_window(input_window):
    '''Close the config window and save the settings'''

    # retrieve values #
    entries = list(filter(lambda x: isinstance(x, (customtkinter.CTkEntry, customtkinter.CTkOptionMenu)), input_window.winfo_children()))
    labels = [ input_window.grid_slaves(row=e.grid_info()["row"], column=e.grid_info()["column"]-1)[0] for e in entries ]

    ret_dict = dict()
    for e, l in zip(entries, labels):
        ret_dict.update({ l.cget("text") : e.get() })

    # dump config and write to file #
    print(json.dumps(ret_dict))
    with open(CONFIG_FILE, "w") as f:
        json.dump(ret_dict, f, indent=2)

    # quit input window #
    input_window.update()
    input_window.quit()
    input_window.withdraw()

def dropdown_changed(dropdown_var, user_entry, password_entry, server_path_entry,
                     install_dir_entry):

    global non_disabled_entry_color

    # FIXME or not idfk #
    if type(dropdown_var) != str:
        dropdown_var = dropdown_var.get()

    # Check the current value of the dropdown and enable/disable user and password inputs accordingly
    if dropdown_var == "Local Filesystem":
        user_entry.configure(state=customtkinter.DISABLED)
        password_entry.configure(state=customtkinter.DISABLED)
        non_disabled_entry_color = password_entry.cget("fg_color")
        print(non_disabled_entry_color)
        password_entry.configure(fg_color="#CCCCCC")
        user_entry.configure(fg_color="#CCCCCC")
        server_path_entry.delete(0, customtkinter.END)
        server_path_entry.insert(0, "C:/path/to/game/mount/")
    else:
        user_entry.configure(state=customtkinter.NORMAL)
        password_entry.configure(state=customtkinter.NORMAL)
        if non_disabled_entry_color: # else first run and nothing to do
            password_entry.configure(fg_color=non_disabled_entry_color)
            user_entry.configure(fg_color=non_disabled_entry_color)
        server_path_entry.delete(0, customtkinter.END)
        server_path_entry.insert(0, "ftp://server:port/path or ftps://server:port/path")

    install_dir_entry.delete(0, customtkinter.END)
    install_dir_entry.insert(0, "./install-dir")

# Create a frame to hold the inputs

def get_config_inputs():
    '''Create a config input window and use the settings from it'''

    input_window = customtkinter.CTk()
    input_window.geometry("500x250")
    input_window.title("Vault Configuration")

    # Server/Path
    server_path_label = customtkinter.CTkLabel(input_window, text="Server/Path:")
    server_path_label.grid(row=1, column=0, sticky="w", padx=10, pady=5)
    server_path_entry = customtkinter.CTkEntry(input_window)
    server_path_entry.grid(row=1, column=1, padx=10, pady=5, sticky="ew", columnspan=2)

    # User
    user_label = customtkinter.CTkLabel(input_window, text="User:")
    user_label.grid(row=2, column=0, sticky="w", padx=10, pady=5)
    user_entry = customtkinter.CTkEntry(input_window)
    user_entry.grid(row=2, column=1, padx=10, pady=5)

    # Password
    password_label = customtkinter.CTkLabel(input_window, text="Password:")
    password_label.grid(row=3, column=0, sticky="w", padx=10, pady=5)
    password_entry = customtkinter.CTkEntry(input_window, show="*")
    password_entry.grid(row=3, column=1, padx=10, pady=5)

    # Install dir
    install_dir_label = customtkinter.CTkLabel(input_window, text="Install dir:")
    install_dir_label.grid(row=4, column=0, sticky="w", padx=10, pady=5)
    install_dir_entry = customtkinter.CTkEntry(input_window)
    install_dir_entry.grid(row=4, column=1, padx=10, pady=5, sticky="ew", columnspan=2)

    # Dropdown
    dropdown_var = customtkinter.StringVar(value="Local Filesystem")
    dropdown_label = customtkinter.CTkLabel(input_window, text="Select option:")
    dropdown_label.grid(row=0, column=0, sticky="w", padx=10, pady=5)
    dropdown = customtkinter.CTkOptionMenu(input_window, variable=dropdown_var, values=["FTP/FTPS", "Local Filesystem"],
        command=lambda dropdown_var=dropdown_var, user_entry=user_entry, password_entry=password_entry,
                        server_path_entry=server_path_entry, install_dir_entry=install_dir_entry:
                        dropdown_changed(dropdown_var, user_entry, password_entry, server_path_entry, install_dir_entry))

    # set dropdown & field defaults
    dropdown_changed(dropdown_var, user_entry, password_entry, server_path_entry, install_dir_entry)
    dropdown.grid(row=0, column=1, padx=10, pady=5)

    # Button to abort & close #
    abort_button = customtkinter.CTkButton(input_window, text="Abort", command=lambda: input_window.quit(), fg_color="red")
    abort_button.grid(row=5, column=0, padx=10, pady=20)

    # Button to save & close #
    save_button = customtkinter.CTkButton(input_window, text="Save & Close", command=lambda: close_input_window(input_window))
    save_button.grid(row=5, column=2, padx=10, pady=20)


    input_window.update()
    input_window.mainloop()

def create_navbar():
    '''Create basic navigation bar'''

    # spawn frame at very top #
    # add "Home" button

def switch_to_main():
    '''Switch back to main view from details'''

    global details_elements
    global last_geometry

    last_geometry = (0,0)

    # destroy details elements #
    for el in details_elements:
        el.destroy()

    details_elements = []
    load_main()

def switch_to_game_details(software):
    '''Switch to the details of the clicked tile'''

    destroy_main()
    load_details(app, software)

def load_main():
    '''Load the main page overview'''

    app.title("Lan Vault: Overview")

    # navbar should not expand when window is resized
    app.grid_rowconfigure(0, weight=0)
    # buttongrid (scrollable frame) should expand when window is resized
    app.grid_rowconfigure(1, weight=1)
    app.grid_columnconfigure(0, weight=1)
    # place scrollable frame
    scrollable_frame.grid(row=1, column=0, sticky="nsew", columnspan=2)


    # create tiles from meta files #
    cache_dir_size = 0
    for software in db.find_all_metadata():
        create_main_window_tile(software, scrollable_frame)

        # retrieve cache dir from any software #
        if not cache_dir_size:
            cache_dir_size = cache_utils.get_cache_size()
            label = customtkinter.CTkLabel(app, text="Cache Size: {:.2f} GB".format(cache_dir_size))
            GITHUB_URL = "https://github.com/FAUSheppy/homelab_gamevault"
            github = customtkinter.CTkButton(app, text="Star on Github",
                            command=lambda: webbrowser.open_new(GITHUB_URL))
            label.grid(row=0, column=0)
            github.grid(row=0, column=1)

    # set update listener & update positions #
    update_button_positions()
    app.bind("<Configure>", update_button_positions)

def destroy_main():
    '''Destroy all elements in the main view'''

    global buttons
    scrollable_frame.grid_remove()
    app.unbind("<Configure>")
    for b in buttons:
        b.destroy()
    
    buttons = []
    app.update()

def load_details(app, software):
    '''Load the details page for a software'''

    global details_elements

    app.title("Lan Vault: {}".format(software.title))
    details_elements = client_details.create_details_page(app, software, switch_to_main)

def create_main_window_tile(software, parent):
    '''Create the main window tile'''

    if software.get_thumbnail():
        try:
            print("Loading thumbnail:",  software.get_thumbnail())
            img = PIL.Image.open(software.get_thumbnail())
            img = imagetools.smart_resize(img, 200, 300)
        except PIL.UnidentifiedImageError:
            print("Failed to load thumbnail:", software.get_thumbnail())
            img = PIL.Image.new('RGB', (200, 300))
    else:
        img = PIL.Image.new('RGB', (200, 300))

    img = PIL.ImageTk.PhotoImage(img)
    button = customtkinter.CTkButton(parent, image=img,
                                        width=200, height=300,
                                        command=lambda: switch_to_game_details(software),
                                        border_width=0, corner_radius=0, border_spacing=0,
                                        text=software.title,
                                        fg_color="transparent", compound="top", anchor="s")
    buttons.append(button)
    return button

def update_button_positions(event=None):
    '''Sets the tile positions initially and on resize'''

    global last_geometry
    # check vs old location #
    new_geometry = app.winfo_geometry()
    if last_geometry[0] == new_geometry[0] and last_geometry[1] == new_geometry[1]:
        return
    else:
        last_geometry = new_geometry
        scrollable_frame.configure(width=app.winfo_width(), height=app.winfo_height())

    # Calculate the number of columns based on the current width of the window #
    num_columns = app.winfo_width() // 201  # Adjust 100 as needed for button width

    # window became too small #
    if num_columns == 0:
        return

    # calculated and set positions
    for i, button in enumerate(buttons):

        grid_info_current = button.grid_info()
        column_new = i % num_columns
        row_new = i // num_columns

        if(grid_info_current.get("row") and grid_info_current["row"] == row_new 
                    and grid_info_current["column"] == column_new):
            continue
        else:
            DOWNSHIFT = 1 # FIXME make real navbar
            button.grid(row=(i // num_columns)+ DOWNSHIFT, column=i % num_columns, sticky="we")


if __name__ == "__main__":

    pgw = pgwrapper.ProgressBarWrapper()
    pgw.new(app)

    # define data backend #
    #db = data_backend.LocalFS(None, None, "./install/", remote_root_dir="example_software_root")
    # db = data_backend.FTP(None, None, "./install/", server="ftp://192.168.1.132:2121",
    #                     remote_root_dir="/", progress_bar_wrapper=pgw, tkinter_root=app)

    if not os.path.isfile(CONFIG_FILE):
        get_config_inputs()
        print("wtf")

    # load config #
    with open(CONFIG_FILE) as f:

        config_loaded = json.load(f)

        # load login & install dir #
        user = config_loaded.get("User:")
        password = config_loaded.get("Password:")
        install_dir = config_loaded["Install dir:"]
        backend_type = config_loaded["Select option:"]

        # fix abs path if not set #
        if os.path.abspath(install_dir):
            install_dir = os.path.join(os.getcwd(), install_dir)

        # get the secod part of the server string, then split once at first / to get path and prepend / again#
        if backend_type == "FTP/FTPS":
            remote_root_dir = "/" + config_loaded["Server/Path:"].split("://")[1].split("/", 1)[1]
            server = config_loaded["Server/Path:"][:-len(remote_root_dir)]
        elif backend_type == "Local Filesystem":
            remote_root_dir = config_loaded["Server/Path:"]
            server = None
        else:
            raise NotImplementedError("Unsupported Backend")

        # debug output #
        print(user, password, install_dir, remote_root_dir, server, config_loaded["Server/Path:"])

        # add db backend #
        if backend_type == "FTP/FTPS":
            db = data_backend.FTP(user, password, install_dir, server=server,
                remote_root_dir=remote_root_dir, progress_bar_wrapper=pgw, tkinter_root=app)
        elif backend_type == "Local Filesystem":
            db = data_backend.LocalFS(user, password, install_dir, server=server,
                remote_root_dir=remote_root_dir, progress_bar_wrapper=pgw, tkinter_root=app)
        else:
            raise NotImplementedError("Unsupported Backend")

    # geometry is set at the very beginning #
    app.update()

    # fill and run app #
    load_main() # TODO add button to reopen config # TODO add button to purge cache/purge cache window # TODO show game size on remote
    app.mainloop()
