import tkinter as tk

import subprocess
import webbrowser
import os
import re

ROOT   = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
APPS   = os.path.join(ROOT, "apps")
JEUX   = os.path.join(ROOT, "jeux")
ASSETS = os.path.join(APPS, "assets")

# Palette Nuit du Code
BG           = "#ffffff"
NDC_PURPLE   = "#7048C8"
NDC_PURPLE_H = "#5C38B0"
NDC_GREEN    = "#2CB55E"
NDC_RED      = "#cc2222"
NDC_RED_H    = "#aa1111"
NDC_GREY     = "#aaaac0"
NDC_GREY_H   = "#8e8ea8"
TEXT_DARK    = "#1a1a2a"
TEXT_MED     = "#555566"
TEXT_LIGHT   = "#888899"
SEP          = "#e0e0e8"
ROW_BG       = "#f5f4fb"
ROW_HOVER    = "#ede9f8"

FONT_SUB     = ("Segoe UI", 9)
FONT_BTN     = ("Segoe UI", 11, "bold")
FONT_BTN_SUB = ("Segoe UI", 8)
FONT_SEC     = ("Segoe UI", 8, "bold")
FONT_FILE    = ("Segoe UI", 10)

RADIUS       = 8
POLL_MS      = 2000  # intervalle de scan du dossier jeux/


def make_env():
    e = os.environ.copy()
    e["APPDATA"]         = APPS
    e["HOME"]            = os.path.join(APPS, "workspace")
    e["THONNY_USER_DIR"] = os.path.join(APPS, "thonny")
    e["PATH"] = ";".join([ROOT, APPS,
                          os.path.join(APPS, "DLLs"),
                          os.path.join(APPS, "Lib"),
                          os.path.join(APPS, "Scripts"),
                          os.path.join(APPS, "Lib", "site-packages", "PyQt5")])
    return e


def run_hidden(cmd, cwd=None):
    subprocess.Popen(cmd, cwd=cwd or ROOT, shell=True,
                     creationflags=subprocess.CREATE_NO_WINDOW)

def open_documentation():
    path = os.path.join(ASSETS, "documentation.html")
    webbrowser.open(f"file:///{path.replace(os.sep, '/')}")

def open_consignes():
    path = os.path.join(ASSETS, "index.html")
    webbrowser.open(f"file:///{path.replace(os.sep, '/')}")

def open_thonny():
    run_hidden(f'"{os.path.join(APPS, "Scripts", "thonny.bat")}"')

def open_python():
    subprocess.Popen([os.path.join(APPS, "python.exe")], cwd=JEUX,
                     env=make_env(), creationflags=subprocess.CREATE_NEW_CONSOLE)

def open_cmd():
    subprocess.Popen([os.path.join(APPS, "cmdp", "cmdp.exe")], cwd=JEUX,
                     env=make_env())

def open_pyxres(filepath):
    subprocess.Popen(
        [os.path.join(APPS, "pythonw.exe"), "-m", "pyxel", "edit", filepath],
        cwd=JEUX, env=make_env(), creationflags=subprocess.CREATE_NO_WINDOW)

def quitter():
    root.destroy()

def scan_app_py():
    """Retourne (exists, liste_de_refs_pyxres)"""
    path = os.path.join(JEUX, "app.py")
    if not os.path.isfile(path):
        return False, []
    try:
        with open(path, encoding="utf-8", errors="ignore") as f:
            content = f.read()

        # Supprimer les commentaires Python (# ... jusqu'à fin de ligne)
        # en évitant les # à l'intérieur des chaînes (approximation conservatrice)
        content_no_comments = re.sub(r'(?m)(?:^|(?<=\n))[ \t]*#[^\n]*', '', content)

        # Chercher les .pyxres dans les chaînes (triples d'abord pour éviter les faux positifs)
        patterns = [
            r'"""([^"]*?\.pyxres[^"]*?)"""',
            r"'''([^']*?\.pyxres[^']*?)'''",
            r'"([^"\n]*?\.pyxres[^"\n]*?)"',
            r"'([^'\n]*?\.pyxres[^'\n]*?)'",
        ]
        seen, refs = set(), []
        for pat in patterns:
            for m in re.findall(pat, content_no_comments):
                basename = os.path.basename(m)
                if basename not in seen:
                    seen.add(basename)
                    refs.append(basename)

        return True, refs
    except Exception:
        return True, []

def scan_pyxres():
    if not os.path.isdir(JEUX):
        return []
    return sorted(f for f in os.listdir(JEUX) if f.lower().endswith(".pyxres"))


# ── Dessin arrondi ────────────────────────────────────────
def round_rect(canvas, x1, y1, x2, y2, r, **kw):
    pts = (x1+r, y1,  x2-r, y1,  x2, y1,   x2, y1+r,
           x2, y2-r,  x2, y2,    x2-r, y2,  x1+r, y2,
           x1, y2,    x1, y2-r,  x1, y1+r,  x1, y1)
    return canvas.create_polygon(pts, smooth=True, **kw)


def _make_canvas_button(parent, label, sublabel, command,
                        color=None, hover_color=None, grid_col=None, small=False,
                        sublabel_color="#d0c8f0"):
    color       = color       or NDC_PURPLE
    hover_color = hover_color or NDC_PURPLE_H
    h = (48 if sublabel else 36) if small else (52 if sublabel else 40)

    cv = tk.Canvas(parent, bg=BG, highlightthickness=0, height=h, cursor="hand2")
    font_btn = (FONT_BTN[0], 9, "bold") if small else FONT_BTN
    font_sub = (FONT_BTN_SUB[0], 7)    if small else FONT_BTN_SUB

    if grid_col is None:
        cv.pack(fill="x")
    else:
        padx = (0, 3) if grid_col == 0 else (3, 0)
        cv.grid(row=0, column=grid_col, sticky="ew", padx=padx)

    state = {"color": color}

    def redraw(col=None):
        col = col or state["color"]
        cv.delete("all")
        cw, ch = cv.winfo_width(), cv.winfo_height()
        if cw <= 1:
            return
        round_rect(cv, 0, 0, cw, ch, RADIUS, fill=col, outline="")
        y_main = ch // 2 - (8 if sublabel else 0)
        cv.create_text(16, y_main, text=label, font=font_btn,
                       fill="#ffffff", anchor="w")
        if sublabel:
            cv.create_text(16, ch // 2 + 10, text=sublabel, font=font_sub,
                           fill=sublabel_color, anchor="w")

    cv.bind("<Configure>", lambda e: redraw())
    cv.bind("<Enter>",     lambda e: [state.update(color=hover_color), redraw(hover_color)])
    cv.bind("<Leave>",     lambda e: [state.update(color=color),       redraw(color)])
    cv.bind("<Button-1>",  lambda e: command())


def make_button(parent, label, sublabel, command, color=None, hover_color=None):
    wrapper = tk.Frame(parent, bg=BG)
    wrapper.pack(fill="x", padx=24, pady=4)
    _make_canvas_button(wrapper, label, sublabel, command, color, hover_color)


def make_button_pair(parent, specs, color=None, hover_color=None, small=False,
                     sublabel_color="#d0c8f0"):
    """specs : liste de 2 tuples (label, sublabel, command)"""
    row = tk.Frame(parent, bg=BG)
    row.pack(fill="x", padx=24, pady=(8, 12) if small else 4)
    row.grid_columnconfigure(0, weight=1)
    row.grid_columnconfigure(1, weight=1)
    for col, (label, sublabel, command) in enumerate(specs):
        _make_canvas_button(row, label, sublabel, command,
                            color=color, hover_color=hover_color,
                            grid_col=col, small=small,
                            sublabel_color=sublabel_color)


def make_pyxres_row(parent, filename, is_last=False):
    filepath = os.path.join(JEUX, filename)
    h = 36

    wrapper = tk.Frame(parent, bg=BG)
    wrapper.pack(fill="x", pady=(0, 0 if is_last else 2))

    cv = tk.Canvas(wrapper, bg=BG, highlightthickness=0, height=h, cursor="hand2")
    cv.pack(fill="x")

    state = {"color": ROW_BG}

    def redraw(col=None):
        col = col or state["color"]
        cv.delete("all")
        cw, ch = cv.winfo_width(), cv.winfo_height()
        if cw <= 1:
            return
        cv.create_rectangle(0, 0, cw, ch, fill=col, outline="")
        cv.create_text(14, ch // 2, text=filename, font=FONT_FILE,
                       fill=TEXT_DARK, anchor="w")
        bw, bh = 52, 22
        bx, by = cw - bw - 10, (ch - bh) // 2
        round_rect(cv, bx, by, bx+bw, by+bh, 5, fill=NDC_GREEN, outline="")
        cv.create_text(bx + bw//2, by + bh//2, text="éditer",
                       font=FONT_BTN_SUB, fill="#ffffff")

    cv.bind("<Configure>", lambda e: redraw())
    cv.bind("<Enter>",     lambda e: [state.update(color=ROW_HOVER), redraw(ROW_HOVER)])
    cv.bind("<Leave>",     lambda e: [state.update(color=ROW_BG),    redraw(ROW_BG)])
    cv.bind("<Button-1>",  lambda e: open_pyxres(filepath))
    cv.bind("<MouseWheel>", lambda e: _on_mousewheel(e))


# ── Fenêtre ───────────────────────────────────────────────
root = tk.Tk()
root.title("Nuit du Code")
root.configure(bg=BG)
root.resizable(False, False)

w = 400
root.geometry(f"{w}x200")  # provisoire, ajusté après construction
root.update_idletasks()

# ── Logo ──────────────────────────────────────────────────
logo_frame = tk.Frame(root, bg=BG)
logo_frame.pack(pady=(10, 10))

try:
    ndc_img = tk.PhotoImage(file=os.path.join(ASSETS, "ndc-pyxel-app.png"))
    tk.Label(logo_frame, image=ndc_img, bg=BG).pack()
except Exception:
    tk.Label(logo_frame, text="NDC", font=("Arial", 28, "bold"),
             bg=BG, fg="#F5C400").pack()

# ── Boutons principaux ────────────────────────────────────
make_button(root, "À lire impérativement le jour de la NDC", None, open_consignes,
            color=NDC_RED, hover_color=NDC_RED_H)
make_button_pair(root, [
    ("Créer un jeu",   "Éditeur de code",        open_thonny),
    ("Documentation",  "Documentation Pyxel",    open_documentation),
])
make_button_pair(root, [
    ("Console Python", "Interpréteur interactif", open_python),
    ("Console CMD",    "Terminal de commandes",   open_cmd),
], color=NDC_GREY, hover_color=NDC_GREY_H, small=True, sublabel_color="#e8e8f4")

# ── Section app.py (mise à jour en temps réel) ───────────
tk.Frame(root, bg=BG, height=10).pack()
tk.Frame(root, bg=SEP, height=1).pack(fill="x", padx=24)

sec_app_frame = tk.Frame(root, bg=BG, padx=24, pady=6)
sec_app_frame.pack(fill="x")
tk.Label(sec_app_frame, text="FICHIERS À DÉPOSER LE JOUR DE LA NDC",
         font=FONT_SEC, bg=BG, fg=TEXT_LIGHT).pack(anchor="w")

app_container = tk.Frame(root, bg=BG)
app_container.pack(fill="x", padx=24)

STATUS_GREEN = "#e8f7ee"
STATUS_RED   = "#fdecea"
STATUS_GRAY  = "#f5f5f8"

current_app_state = [None]


def open_in_explorer(filepath):
    subprocess.Popen(["explorer", "/select,", os.path.normpath(filepath)])


def make_file_block(parent, title, bg, status_text, status_color,
                    filepath=None):
    """Un bloc avec titre, statut et optionnellement un lien 'télécharger'."""
    block = tk.Frame(parent, bg=bg)
    block.pack(fill="x", pady=3)

    inner = tk.Frame(block, bg=bg, padx=12, pady=8)
    inner.pack(fill="x")

    top = tk.Frame(inner, bg=bg)
    top.pack(fill="x", pady=(4, 0))
    tk.Label(top, text=title, font=(FONT_FILE[0], FONT_FILE[1], "bold"),
             bg=bg, fg=TEXT_DARK).pack(side="left")

    if filepath:
        bw, bh = 60, 22
        cv_btn = tk.Canvas(top, bg=bg, width=bw, height=bh,
                           highlightthickness=0, cursor="hand2")
        cv_btn.pack(side="right")

        def redraw_btn(col="#6b6b80", cv=cv_btn, w=bw, h=bh):
            cv.delete("all")
            round_rect(cv, 0, 0, w, h, 5, fill=col, outline="")
            cv.create_text(w//2, h//2, text="dossier",
                           font=FONT_BTN_SUB, fill="#ffffff")

        cv_btn.bind("<Configure>", lambda e: redraw_btn())
        cv_btn.bind("<Enter>",     lambda e: redraw_btn("#555568"))
        cv_btn.bind("<Leave>",     lambda e: redraw_btn())
        cv_btn.bind("<Button-1>",  lambda e: open_in_explorer(filepath))

    tk.Label(inner, text=status_text, font=FONT_BTN_SUB,
             bg=bg, fg=status_color, wraplength=320, justify="left").pack(anchor="w")


def make_status_row(parent, exists, refs):
    for w in parent.winfo_children():
        w.destroy()

    app_path = os.path.join(JEUX, "app.py")

    # ── Bloc app.py ──
    if exists:
        make_file_block(parent, "app.py", STATUS_GREEN,
                        "Fichier présent", NDC_GREEN, filepath=app_path)
    else:
        make_file_block(parent, "app.py", STATUS_RED,
                        'Fichier manquant. Déposez app.py dans le dossier "jeux"',
                        NDC_RED)

    # ── Bloc .pyxres ──
    if refs:
        pyxres_path = os.path.join(JEUX, refs[0])
        pyxres_exists = os.path.isfile(pyxres_path)
        if pyxres_exists:
            make_file_block(parent, refs[0], STATUS_GREEN,
                            "Fichier présent", NDC_GREEN, filepath=pyxres_path)
        else:
            make_file_block(parent, refs[0], STATUS_RED,
                            f'Fichier manquant. Déposez {refs[0]} dans "jeux"',
                            NDC_RED)
    else:
        make_file_block(parent, ".pyxres", STATUS_GRAY,
                        "Aucun .pyxres référencé dans app.py",
                        TEXT_LIGHT)


def refresh_app_py():
    exists, refs = scan_app_py()
    pyxres_present = [os.path.isfile(os.path.join(JEUX, r)) for r in refs]
    state = (exists, tuple(refs), tuple(pyxres_present))
    if state != current_app_state[0]:
        current_app_state[0] = state
        make_status_row(app_container, exists, refs)
    root.after(POLL_MS, refresh_app_py)

refresh_app_py()

# ── Section .pyxres (mise à jour en temps réel) ───────────
tk.Frame(root, bg=BG, height=10).pack()
tk.Frame(root, bg=SEP, height=1).pack(fill="x", padx=24)

sec_label_frame = tk.Frame(root, bg=BG, padx=24)
sec_label_frame.pack(fill="x", pady=(6, 10))
tk.Label(sec_label_frame, text="ÉDITEUR D'IMAGES  —  fichiers .pyxres",
         font=FONT_SEC, bg=BG, fg=TEXT_LIGHT).pack(anchor="w")

ROW_H      = 36
ROW_GAP    = 2
MAX_ROWS   = 3  # scroll dès le 4e .pyxres
SCROLL_H   = MAX_ROWS * ROW_H + (MAX_ROWS - 1) * ROW_GAP

SBAR_W = 4

scroll_outer = tk.Frame(root, bg=BG)
scroll_outer.pack(fill="x", padx=24)
scroll_outer.grid_columnconfigure(0, weight=1)

scroll_canvas    = tk.Canvas(scroll_outer, bg=BG, highlightthickness=0, height=SCROLL_H)
scrollbar_canvas = tk.Canvas(scroll_outer, bg=BG, width=SBAR_W, height=1, highlightthickness=0)
pyxres_container = tk.Frame(scroll_canvas, bg=BG)

scroll_canvas.grid(row=0, column=0, sticky="ew", padx=(0, 6))
scrollbar_canvas.grid(row=0, column=1, sticky="ns")

_win_id = scroll_canvas.create_window((0, 0), window=pyxres_container, anchor="nw")

pyxres_container.bind("<Configure>", lambda e: scroll_canvas.configure(
    scrollregion=(0, 0, e.width, e.height)))
scroll_canvas.bind("<Configure>", lambda e: scroll_canvas.itemconfig(_win_id, width=e.width))

_sbar_state = {"lo": 0.0, "hi": 1.0, "hover": False}

def _draw_thumb():
    scrollbar_canvas.delete("thumb")
    lo, hi = _sbar_state["lo"], _sbar_state["hi"]
    if lo <= 0.0 and hi >= 1.0:
        return
    h = scrollbar_canvas.winfo_height()
    if h <= 1:
        return
    w = scrollbar_canvas.winfo_width()
    y0 = max(0, int(lo * h))
    y1 = min(h, int(hi * h))
    if y1 - y0 < 12:
        y1 = min(h, y0 + 12)
    col = NDC_PURPLE if _sbar_state["hover"] else NDC_GREY
    scrollbar_canvas.create_rectangle(0, y0, w, y1, fill=col, outline="", tags="thumb")

def _sbar_set(first, last):
    _sbar_state["lo"] = float(first)
    _sbar_state["hi"] = float(last)
    _draw_thumb()

def _sbar_click(event):
    h = scrollbar_canvas.winfo_height()
    if h > 0:
        scroll_canvas.yview_moveto(event.y / h)

def _sbar_enter(e):
    _sbar_state["hover"] = True
    _draw_thumb()

def _sbar_leave(e):
    _sbar_state["hover"] = False
    _draw_thumb()

scrollbar_canvas.bind("<Button-1>",  _sbar_click)
scrollbar_canvas.bind("<B1-Motion>", _sbar_click)
scrollbar_canvas.bind("<Enter>",     _sbar_enter)
scrollbar_canvas.bind("<Leave>",     _sbar_leave)
scrollbar_canvas.bind("<Configure>", lambda e: _draw_thumb())

scroll_canvas.configure(yscrollcommand=_sbar_set)

def _on_mousewheel(event):
    scroll_canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")

scroll_canvas.bind("<MouseWheel>", _on_mousewheel)
pyxres_container.bind("<MouseWheel>", _on_mousewheel)

current_pyxres = None

def refresh_pyxres():
    global current_pyxres
    files = scan_pyxres()
    if files == current_pyxres and current_pyxres is not None:
        root.after(POLL_MS, refresh_pyxres)
        return
    current_pyxres = files
    for w in pyxres_container.winfo_children():
        w.destroy()
    if files:
        n = len(files)
        for i, f in enumerate(files):
            make_pyxres_row(pyxres_container, f, is_last=(i == n - 1))
        content_h = n * ROW_H + max(0, n - 1) * ROW_GAP
        if content_h > SCROLL_H:
            scroll_canvas.config(height=SCROLL_H)
            scrollbar_canvas.grid()
        else:
            scroll_canvas.config(height=content_h)
            scrollbar_canvas.grid_remove()
    else:
        tk.Label(pyxres_container,
                 text='Déposez vos fichiers .pyxres dans le dossier "jeux"',
                 font=FONT_BTN_SUB, bg=BG, fg=TEXT_LIGHT,
                 wraplength=320, justify="left").pack(anchor="w", pady=4)
        scroll_canvas.config(height=30)
        scrollbar_canvas.grid_remove()
    root.after(POLL_MS, refresh_pyxres)

refresh_pyxres()

tk.Frame(root, bg=BG, height=20).pack()

# Ajuster la hauteur et centrer après construction complète
root.update_idletasks()
h = root.winfo_reqheight()
x = (root.winfo_screenwidth()  - w) // 2
y = (root.winfo_screenheight() - h) // 2
root.geometry(f"{w}x{h}+{x}+{y}")

root.mainloop()
