import tkinter as tk
from tkinter import ttk
from tkinter import filedialog
import tkinter.font as tkFont
import os
import subprocess
import re
import winreg


def get_steam_path_from_registry():
    try:
        with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\WOW6432Node\Valve\Steam") as key:
            steam_path, _ = winreg.QueryValueEx(key, "InstallPath")
            return steam_path
    except FileNotFoundError:
        print("Steam not found in registry.")
        return None

def parse_libraryfolders_vdf(vdf_path):
    try:
        with open(vdf_path, 'r', encoding='utf-8') as f:
            lines = f.readlines()
        
        folders = []
        for line in lines:
            if '"path"' in line:
                path = re.findall(r'"path"\s+"(.+?)"', line)
                if path:
                    folders.append(path[0].replace('\\\\', '\\'))
        return folders
    except Exception as e:
        print(f"Error parsing {vdf_path}: {e}")
        return []

def get_game_name(appid, steamcmd_path):
    login_cmd = ["+login", steam_username] if steam_password == "" else ["+login", steam_username, steam_password]
    command = [
        steamcmd_path,
        *login_cmd,
        "+app_info_update", "1",
        "+app_info_print", str(appid),
        "+quit"
    ]
    try:
        result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=60)
        output = result.stdout.decode('utf-8', errors='replace')
        match = re.search(r'"name"\s+"([^"]+)"', output)
        if match:
            return match.group(1)
    except Exception as e:
        print(f"Error getting game name: {e}")
    return None

def get_install_dir(appid, steamcmd_path):
    login_cmd = ["+login", steam_username] if steam_password == "" else ["+login", steam_username, steam_password]
    command = [
        steamcmd_path,
        *login_cmd,
        "+app_info_update", "1",
        "+app_info_print", str(appid),
        "+quit"
    ]
    try:
        result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=60)
        output = result.stdout.decode('utf-8', errors='replace')
        match = re.search(r'"installdir"\s+"([^"]+)"', output)
        if match:
            return match.group(1)
    except Exception as e:
        print(f"Error getting install dir: {e}")
    return None

script_dir = os.path.dirname(os.path.abspath(__file__))
steamcmd_path = os.path.join(script_dir, "data", "steamcmd", "steamcmd.exe")
steam_username = "anonymous"
steam_password = ""  # Leave blank if not needed

def update_credentials_from_gui():
    global steam_username, steam_password
    steam_username = username_entry.get().strip()
    steam_password = password_entry.get().strip()

steam_path = get_steam_path_from_registry()
libraries = []
if steam_path:
    vdf_path = os.path.join(steam_path, "config", "libraryfolders.vdf")
    libraries = parse_libraryfolders_vdf(vdf_path)
default_common_paths = [os.path.join(lib, "steamapps", "common") for lib in libraries]

rows = []

def create_row(container, prefill_appid="", prefill_path=""):
    row_num = len(rows)
    print(f"Creating row {row_num} with appid={prefill_appid}, path={prefill_path}")  # Debug print
    frame = tk.Frame(container)
    frame.grid(row=row_num, column=0, columnspan=5, pady=2, sticky="w")

    validated = tk.BooleanVar(value=False)

    check_btn = tk.Button(frame, text="✔", width=3)
    check_btn.grid(row=0, column=0, padx=2)

    appid_entry = tk.Entry(frame, width=10)
    appid_entry.grid(row=0, column=1, padx=2)
    if prefill_appid:
        appid_entry.insert(0, prefill_appid)

    game_label = tk.Label(frame, text="Game Name", width=30, anchor="w")
    game_label.grid(row=0, column=2, padx=2)

    # Add "Custom..." option
    custom_option = "Custom..."
    combo_paths = default_common_paths + [custom_option]
    location_combo = ttk.Combobox(frame, width=60, values=combo_paths, state=tk.DISABLED)  # Initially disabled

    # Handle selection of "Custom..."
    def on_location_select(event):
        if location_combo.get() == custom_option:
            custom_path = filedialog.askdirectory(title="Select Custom Install Location")
            if custom_path:
                # If user picks a folder, insert it into the combobox
                if custom_path not in location_combo['values']:
                    location_combo['values'] = (*location_combo['values'][:-1], custom_path, custom_option)
                location_combo.set(custom_path)
            else:
                # Revert to first path if cancelled
                location_combo.set(default_common_paths[0] if default_common_paths else "")

    location_combo.bind("<<ComboboxSelected>>", on_location_select)
    if prefill_path:
        location_combo.set(prefill_path)
    elif default_common_paths:
        location_combo.set(default_common_paths[0])
    location_combo.grid(row=0, column=3, padx=2)

    def on_check():
        appid = appid_entry.get().strip()

        # Special case for Minecraft app IDs (without SteamCMD)
        if appid in ['2927300', '3049290', 'Minecraft']:
            game_label.config(text="Minecraft (No SteamCMD required)")
            minecraft_path = os.path.join(os.getenv('APPDATA'), '.minecraft')
            location_combo['values'] = [minecraft_path]
            location_combo.set(minecraft_path)
            validated.set(True)
            update_button_states()
            location_combo.config(state=tk.NORMAL)
            return

        # Special case for Halo 3 app ID (without SteamCMD)
        elif appid in ['1064271']:
            game_label.config(text="Halo 3 Online - ElDewrito")
            subfolder = "ElDewrito"
            eldewrito_paths = [os.path.join(lib, "steamapps", "common", subfolder) for lib in libraries]

            # Add custom option if not already included
            if custom_option not in eldewrito_paths:
                eldewrito_paths.append(custom_option)

            location_combo['values'] = eldewrito_paths
            location_combo.set(eldewrito_paths[0] if eldewrito_paths else "")
            validated.set(True)
            update_button_states()
            location_combo.config(state=tk.NORMAL)
            return


        # Proceed with normal validation for valid app IDs
        if not appid.isdigit():
            game_label.config(text="Invalid App ID!")
            validated.set(False)
            update_button_states()
            location_combo.config(state=tk.DISABLED)  # Disable the combobox if invalid
            return

        if not os.path.isfile(steamcmd_path):
            game_label.config(text="steamcmd.exe missing!")
            validated.set(False)
            update_button_states()
            location_combo.config(state=tk.DISABLED)  # Disable the combobox if steamcmd is missing
            return

        name = get_game_name(appid, steamcmd_path)
        if name:
            game_label.config(text=name)
        else:
            game_label.config(text=f"App ID {appid} not found.")
            validated.set(False)
            update_button_states()
            location_combo.config(state=tk.DISABLED)  # Disable the combobox if game not found
            return

        subfolder = get_install_dir(appid, steamcmd_path)
        if subfolder:
            new_paths = [os.path.join(lib, "steamapps", "common", subfolder) for lib in libraries]
            # Only update if it's not a custom path
            if location_combo.get() != custom_option:
                location_combo['values'] = new_paths + [custom_option]  # Ensure custom option remains
                location_combo.set(new_paths[0] if new_paths else "")
            validated.set(True)
            location_combo.config(state=tk.NORMAL)  # Enable the combobox after validation
        else:
            location_combo.set("Game not downloadable outside Steam client")
            game_label.config(text="Requires Steam Client")
            validated.set(False)
            location_combo.config(state=tk.DISABLED)  # Disable the combobox if not downloadable

        update_button_states()

    check_btn.config(command=on_check)

    def remove_row():
        frame.destroy()
        rows.remove(row)
        # Re-grid all rows to correct positions
        for idx, (f, *_rest) in enumerate(rows):
            f.grid(row=idx, column=0, columnspan=5, pady=2, sticky="w")
        update_button_states()


    remove_btn = tk.Button(frame, text="–", width=3, command=remove_row)
    remove_btn.grid(row=0, column=4, padx=2)

    row = (frame, appid_entry, game_label, location_combo, validated, check_btn)
    rows.append(row)

    if prefill_path:
        folder_name = os.path.basename(prefill_path)
        game_label.config(text=folder_name)

    update_button_states()

    
# Ensure the custom_option is defined before using it
custom_option = "Custom Path"  # Globally accessible custom option

def validate_row(row):
    frame, appid_entry, game_label, location_combo, validated = row
    appid = appid_entry.get().strip()

    # If there's a valid appid, perform the validation
    if appid.isdigit():
        name = get_game_name(appid, steamcmd_path)
        if name:
            game_label.config(text=name)
            subfolder = get_install_dir(appid, steamcmd_path)
            if subfolder:
                new_paths = [os.path.join(lib, "steamapps", "common", subfolder) for lib in libraries]
                # Only update if it's not a custom path
                if location_combo.get() != custom_option:
                    location_combo['values'] = new_paths + [custom_option]  # Ensure custom option remains
                    location_combo.set(new_paths[0] if new_paths else "")
                validated.set(True)  # Mark this row as validated
            else:
                location_combo.set("Game not downloadable outside Steam client")
                game_label.config(text="Requires Steam Client")
                validated.set(False)  # Mark as invalid
        else:
            game_label.config(text=f"App ID {appid} not found.")
            validated.set(False)  # Mark as invalid
    else:
        validated.set(False)  # Mark as invalid if App ID is not a number
        
def validate_all_rows():
    all_validated = True  # This will be updated based on each row's validated status

    for row in rows:
        # Unpack the check_btn from each row
        *_, validated, check_btn = row
        check_btn.invoke()  # Simulate pressing the check mark button

        # After invoking, check if validation was successful
        if not validated.get():
            all_validated = False

    # Final result
    if all_validated:
        print("All rows validated successfully!")
    else:
        print("Some rows failed validation.")

    update_button_states()
   
def update_button_states():
    # Check the validation status of each row and update the button states
    all_valid = all(row[4].get() for row in rows)  # Check if all rows are validated
    
    # Enable or disable buttons based on validation status
    add_btn.config(state=tk.NORMAL if all_valid else tk.DISABLED)
    save_btn.config(state=tk.NORMAL if all_valid else tk.DISABLED)
    
    # Also enable 'validate all' button if all rows are validated
    if all_valid:
        validate_all_btn.config(state=tk.NORMAL)
    else:
        validate_all_btn.config(state=tk.DISABLED)
   
def update_ui():
    root.update_idletasks()  # This will process the events and refresh the UI

def update_button_states():
    all_valid = all(row[4].get() for row in rows)
    add_btn.config(state=tk.NORMAL if all_valid else tk.DISABLED)
    save_btn.config(state=tk.NORMAL if all_valid else tk.DISABLED)

def add_new_row():
    create_row(row_container)
    row_container.update_idletasks()  # Make sure the UI is updated
    update_button_states()

def save_to_bat():
    # steam_username, steam_password, and auth_code are assumed to be defined elsewhere
    login_command = f'+login {steam_username} {steam_password}'

    bat_lines = [
        '@echo off',
        'REM Auto-generated SteamCMD downloader',
        'python.exe -m pip install --upgrade pip',
        'cd /d "%~dp0"'
    ]

    for _, appid_entry, _, location_combo, validated, *_ in rows:
        appid = appid_entry.get().strip()
        path = location_combo.get().strip()

        if appid.isdigit() and path and validated.get():
            steamcmd = f'"%~dp0steamcmd\\steamcmd.exe" +force_install_dir "{path}" {login_command} +app_update {appid} +quit'

            if appid in ['3049290', '2927300']:  # Minecraft IDs
                bat_lines.append('python "%~dp0update_minecraft.py"')
            elif appid == '10180':  # IW4x
                bat_lines.append(steamcmd)
                bat_lines.append('pip install pywin32 winshell')
                bat_lines.append(f'python "%~dp0update_iw4x.py" "{path}"')
            elif appid == '1064271':  # ElDewrito
                bat_lines.append(steamcmd)
                bat_lines.append('pip install pywin32 winshell')
                bat_lines.append(f'python "%~dp0update_eldewrito.py" "{path}"')
            elif appid == '9420':  # Supcom:FA
                bat_lines.append(steamcmd)
                bat_lines.append('pip install pywin32 winshell')
                bat_lines.append(f'python "%~dp0update_faf.py" "{path}"')
            elif appid == '2229890':  # Renegade
                bat_lines.append(steamcmd)
                bat_lines.append('pip install pywin32 winshell')
                bat_lines.append(f'python "%~dp0update_renegade.py" "{path}"')
            elif appid == '2229850':  # RA2
                bat_lines.append(steamcmd)
                bat_lines.append('pip install pywin32 winshell')
                bat_lines.append(f'python "%~dp0update_ra2.py" "{path}"')
            elif appid == '1399780':  # Spellbreak
                bat_lines.append(steamcmd)
                bat_lines.append('pip install pywin32 winshell')
                bat_lines.append(f'python "%~dp0update_spellbreak.py" "{path}"')
            else:
                bat_lines.append(steamcmd)

    bat_file = os.path.join(script_dir, 'data', 'download_games.bat')
    with open(bat_file, 'w', encoding='utf-8') as f:
        f.write('\n'.join(bat_lines))
    print(f"Saved to {bat_file}")




def load_from_bat():
    bat_file = os.path.join(script_dir, 'data', 'download_games.bat')
    if not os.path.isfile(bat_file):
        print("No .bat file found to load.")
        return

    with open(bat_file, 'r', encoding='utf-8') as f:
        lines = f.readlines()

    print(f"Loaded lines from .bat: {lines}")  # Debug print

    pattern = re.compile(r'\+force_install_dir\s+"([^"]+)"\s+\+login\s+anonymous\s+\+app_update\s+(\d+)')

    for line in lines:
        line = line.strip()  # Remove extra whitespace
        print(f"Processing line: {line}")  # Debug print

        match = pattern.search(line)
        if match:
            path, appid = match.groups()
            print(f"Match found: path={path}, appid={appid}")  # Debug print
            create_row(row_container, prefill_appid=appid, prefill_path=path)
        else:
            # This handles non-matching lines (like Minecraft or other custom commands)
            if 'python "%~dp0update_minecraft.py"' in line:
                create_row(row_container, prefill_appid="3049290", prefill_path="%appdata%\.minecraft")
            else:
                print(f"No match for line: {repr(line)}")  # Debug print for non-matching lines

# Add a variable to store custom paths for each row
custom_paths = {}

# Dictionary to store the custom paths for each row
custom_paths = {}


def make_custom_option_clickable(location_combo):
    """Ensure 'Custom' remains a clickable option in the drop-down."""
    if "Custom" not in location_combo["values"]:
        location_combo["values"] = list(location_combo["values"]) + ["Custom"]

    # When 'Custom' is selected, open the file explorer
    def on_custom_selected(event):
        selected_folder = filedialog.askdirectory()  # Open file explorer for folder selection
        if selected_folder:
            location_combo.set(selected_folder)  # Set the selected path in the drop-down
            custom_paths[row] = selected_folder  # Store the custom path after selection

    # Bind the 'Custom' option to open the file explorer
    location_combo.bind("<FocusIn>", on_custom_selected)


def add_custom_option_to_combo(location_combo):
    """Ensure 'Custom' remains an interactive option in the drop-down."""
    # Check if the "Custom" option is present
    if "Custom" not in location_combo["values"]:
        # Add "Custom" option if not present
        location_combo["values"] = list(location_combo["values"]) + ["Custom"]
    
    # Ensure the "Custom" option is clickable (in case it's lost)
    # This depends on your implementation, so you may need to manually make it interactive
    location_combo.bind("<FocusIn>", lambda event: show_custom_location_explorer(location_combo))

def show_custom_location_explorer(location_combo):
    """Function to open the file explorer for selecting the custom path."""
    from tkinter import filedialog
    selected_folder = filedialog.askdirectory()  # Open file explorer for folder selection
    if selected_folder:
        location_combo.set(selected_folder)  # Set the selected path in the drop-down

# GUI
root = tk.Tk()
root.title("Steam App Downloader")
root.geometry("1000x600")

# Scrollable area for rows
main_frame = tk.Frame(root)
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=(10, 0))

canvas = tk.Canvas(main_frame)
scrollbar = tk.Scrollbar(main_frame, orient="vertical", command=canvas.yview)
row_container = tk.Frame(canvas)

row_container.bind(
    "<Configure>",
    lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
)

canvas.create_window((0, 0), window=row_container, anchor="nw")
canvas.configure(yscrollcommand=scrollbar.set)

canvas.pack(side="left", fill="both", expand=True)
scrollbar.pack(side="right", fill="y")

# Optional mouse wheel scrolling
def _on_mousewheel(event):
    canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")
canvas.bind_all("<MouseWheel>", _on_mousewheel)
canvas.bind_all("<Button-4>", lambda e: canvas.yview_scroll(-1, "units"))  # Linux
canvas.bind_all("<Button-5>", lambda e: canvas.yview_scroll(1, "units"))   # Linux

def open_steam_credentials_window():
    # Create popup window
    cred_win = tk.Toplevel()
    cred_win.title("Steam Credentials")
    cred_win.geometry("300x180")
    cred_win.resizable(False, False)

    # Username label and entry
    tk.Label(cred_win, text="Steam Username:").pack(pady=(10, 0))
    username_var = tk.StringVar(value=steam_username)
    username_entry = tk.Entry(cred_win, textvariable=username_var, width=30)
    username_entry.pack(pady=5)

    # Password label and entry (masked)
    tk.Label(cred_win, text="Steam Password:").pack(pady=(10, 0))
    password_var = tk.StringVar(value=steam_password)
    password_entry = tk.Entry(cred_win, textvariable=password_var, show="*", width=30)
    password_entry.pack(pady=5)

    # Button frame
    btn_frame = tk.Frame(cred_win)
    btn_frame.pack(pady=15)

    def save_credentials():
        global steam_username, steam_password
        steam_username = username_var.get().strip()
        steam_password = password_var.get().strip()
        print(f"Saved Steam credentials: username='{steam_username}' password='{steam_password}'")
        # Optionally update any GUI parts or save to file here

    def reset_credentials():
        username_var.set("anonymous")
        password_var.set("")
        save_credentials()

    def close_window():
        cred_win.destroy()

    # Save, Reset, Close buttons
    tk.Button(btn_frame, text="Save", width=8, command=save_credentials).pack(side=tk.LEFT, padx=5)
    tk.Button(btn_frame, text="Reset", width=8, command=reset_credentials).pack(side=tk.LEFT, padx=5)
    tk.Button(btn_frame, text="Close", width=8, command=close_window).pack(side=tk.LEFT, padx=5)

# --- Button section below scrollable area ---
btn_frame = tk.Frame(root)
btn_frame.pack(fill="x", pady=10)

steam_cred_btn = tk.Button(btn_frame, text="🔒 Steam Credentials", command=open_steam_credentials_window)
steam_cred_btn.pack(side=tk.LEFT, padx=5)

validate_all_btn = tk.Button(btn_frame, text="✔ Validate All", command=validate_all_rows)
validate_all_btn.pack(side=tk.LEFT, padx=10, pady=10)

add_btn = tk.Button(btn_frame, text="Add Game", command=add_new_row)
add_btn.pack(side=tk.LEFT, padx=10, pady=10)

save_btn = tk.Button(btn_frame, text="💾 Save to .bat", command=save_to_bat)
save_btn.pack(side=tk.LEFT, padx=10, pady=10)

def run_bat():
    import subprocess
    bat_file = os.path.join(script_dir, 'data', 'download_games.bat')
    subprocess.run([bat_file], shell=True)

run_bat_btn = tk.Button(btn_frame, text="🚀 Run download_games.bat", command=run_bat)
run_bat_btn.pack(side="left", pady=10)

link_label = tk.Label(btn_frame, text="Steam Game ID Lookup", fg="blue", cursor="hand2")
link_label.pack(side="left", padx=5, pady=5)
link_label.bind("<Button-1>", lambda e: webbrowser.open("https://steamdb.info/search/?a=app&q=&type=-1&category=0"))

# Load saved data and initialize
load_from_bat()
if not rows:
    create_row(row_container)  # Use scrollable container now
update_button_states()

# --- SanDiegoLAN.net Link ---
import ctypes

def load_font(font_path):
    if os.name == "nt":
        # Windows-only: register font file temporarily
        FR_PRIVATE  = 0x10
        FR_NOT_ENUM = 0x20
        path = os.path.abspath(font_path)
        if ctypes.windll.gdi32.AddFontResourceExW(path, FR_PRIVATE, 0):
            print("Font loaded:", font_path)
        else:
            print("Failed to load font:", font_path)

font_path = os.path.join(script_dir, "data", "Capture it.ttf")
load_font(font_path)

font_path = os.path.join(script_dir, "data", "Capture it.ttf")
try:
    root.tk.call('font', 'create', 'CaptureItFont', '-family', 'Capture it', '-size', 23)
    root.tk.call('font', 'configure', 'CaptureItFont', '-family', 'Capture it', '-size', 23)
    root.tk.call('font', 'configure', 'CaptureItFont', '-weight', 'normal')
    root.tk.call('font', 'configure', 'CaptureItFont', '-underline', '0')
except tk.TclError:
    print("Could not register custom font.")
tkFont.Font(root=root, name='CaptureItFont', exists=True)

custom_link_frame = tk.Frame(btn_frame)
custom_link_frame.pack(side="right", padx=10)

styled_link = tk.Frame(custom_link_frame)
styled_link.pack()

def open_sdl_link(event=None):
    import webbrowser
    webbrowser.open("https://SanDiegoLAN.net")

letter_labels = []
def make_letter(parent, char, color):
    lbl = tk.Label(
        parent,
        text=char,
        fg=color,
        cursor="hand2",
        font=("Capture it", 23),
        bd=0,
        padx=0,
        pady=0
    )
    lbl.pack(side="left", padx=0)
    lbl.bind("<Button-1>", open_sdl_link)
    letter_labels.append((lbl, char))

for char in "SanDiegoLAN.net":
    color = "red" if char in "SDL" else "black"
    make_letter(styled_link, char, color)

def on_enter(event):
    for lbl, char in letter_labels:
        lbl.config(fg="red" if char in "SDL" else "navy")
def on_leave(event):
    for lbl, char in letter_labels:
        lbl.config(fg="red" if char in "SDL" else "black")

styled_link.bind("<Enter>", on_enter)
styled_link.bind("<Leave>", on_leave)

root.update_idletasks()
root.mainloop()