import tkinter as tk
from tkinter import messagebox, ttk
import networkx as nx
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

class AppExplorateurArbre:
    def __init__(self, root):
        self.root = root
        self.root.title("Constructeur d'Arbre Dynamique")
        self.root.geometry("1200x800")
        
        # Graphe orienté pour représenter l'arbre
        self.G = nx.DiGraph()

        # --- Interface de Saisie ---
        frame_input = tk.Frame(root, pady=15, bg="#ecf0f1")
        frame_input.pack(fill=tk.X)

        tk.Label(frame_input, text="Parent :", bg="#ecf0f1").pack(side=tk.LEFT, padx=5)
        self.ent_p = tk.Entry(frame_input, width=15)
        self.ent_p.pack(side=tk.LEFT, padx=5)

        tk.Label(frame_input, text="Enfant :", bg="#ecf0f1").pack(side=tk.LEFT, padx=5)
        self.ent_e = tk.Entry(frame_input, width=15)
        self.ent_e.pack(side=tk.LEFT, padx=5)

        tk.Button(frame_input, text="Ajouter Branche", command=self.ajouter, bg="#27ae60", fg="white").pack(side=tk.LEFT, padx=10)
        tk.Button(frame_input, text="Effacer tout", command=self.reset, bg="#c0392b", fg="white").pack(side=tk.LEFT, padx=5)

        # --- Zone des Statistiques ---
        self.frame_stats = tk.Frame(root, pady=10)
        self.frame_stats.pack(fill=tk.X, padx=20)
        
        self.res_label = tk.Label(self.frame_stats, text="Ajoutez une racine pour commencer", font=("Arial", 11, "bold"), fg="#2980b9")
        self.res_label.pack()

        # --- Affichage (Tableau + Graphe) ---
        self.pw = tk.PanedWindow(root, orient=tk.HORIZONTAL)
        self.pw.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        # Tableau des profondeurs
        self.tree_view = ttk.Treeview(self.pw, columns=("Noeud", "Type", "Profondeur"), show='headings')
        self.tree_view.heading("Noeud", text="Nom du Nœud")
        self.tree_view.heading("Type", text="Rôle")
        self.tree_view.heading("Profondeur", text="Profondeur")
        self.pw.add(self.tree_view)

        # Graphique
        self.frame_graph = tk.Frame(self.pw, bg="white")
        self.pw.add(self.frame_graph)
        
        self.canvas = None

    def ajouter(self):
        p, e = self.ent_p.get().strip().upper(), self.ent_e.get().strip().upper()
        
        if not p or not e:
            messagebox.showwarning("Saisie", "Veuillez remplir Parent et Enfant.")
            return

        # Vérification anti-cycle (un arbre ne peut pas boucler)
        if p == e:
            messagebox.showerror("Erreur", "Un nœud ne peut pas être son propre enfant.")
            return
        
        if e in self.G and self.G.in_degree(e) > 0:
            messagebox.showerror("Structure", f"Le nœud '{e}' a déjà un parent. Un arbre n'autorise qu'un seul parent par enfant.")
            return

        self.G.add_edge(p, e)
        self.ent_e.delete(0, tk.END)
        self.ent_e.focus()
        self.maj_analyse()

    def reset(self):
        self.G.clear()
        self.maj_analyse()

    def maj_analyse(self):
        # 1. Nettoyage
        for i in self.tree_view.get_children(): self.tree_view.delete(i)
        if self.canvas: self.canvas.get_tk_widget().destroy()
        
        if len(self.G.nodes) == 0:
            self.res_label.config(text="Arbre vide")
            return

        # 2. Identification de la racine et calculs
        # La racine est le nœud avec 0 parent
        racines = [n for n, d in self.G.in_degree() if d == 0]
        
        if not racines:
            self.res_label.config(text="Erreur : Structure cyclique (pas d'arbre)")
            return
        
        racine_principale = racines[0]
        profondeurs = nx.single_source_shortest_path_length(self.G, racine_principale)
        
        nb_noeuds = self.G.number_of_nodes()
        nb_feuilles = sum(1 for n in self.G.nodes() if self.G.out_degree(n) == 0)
        hauteur = max(profondeurs.values())

        # 3. Remplissage du Tableau
        for n in self.G.nodes():
            role = "Racine" if n == racine_principale else ("Feuille" if self.G.out_degree(n) == 0 else "Interne")
            prof = profondeurs.get(n, "Inaccessible")
            self.tree_view.insert("", tk.END, values=(n, role, prof))

        self.res_label.config(text=f"Noeuds: {nb_noeuds} | Feuilles: {nb_feuilles} | Hauteur: {hauteur} | Racine: {racine_principale}")

        # 4. Dessin
        fig, ax = plt.subplots(figsize=(6, 5))
        # Layout hiérarchique
        pos = self.hierarchy_pos(self.G, racine_principale)
        
        nx.draw(self.G, pos, with_labels=True, node_color='#3498db', node_size=1500, 
                font_color='white', font_weight='bold', edge_color='#95a5a6', arrows=True, ax=ax)
        
        self.canvas = FigureCanvasTkAgg(fig, master=self.frame_graph)
        self.canvas.draw()
        self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)

    def hierarchy_pos(self, G, root, width=1., vert_gap = 0.2, vert_loc = 0, xcenter = 0.5):
        """Fonction utilitaire pour positionner les nœuds de façon hiérarchique"""
        pos = {root: (xcenter, vert_loc)}
        children = list(G.neighbors(root))
        if children:
            dx = width / len(children) 
            nextx = xcenter - width/2 - dx/2
            for child in children:
                nextx += dx
                pos.update(self.hierarchy_pos(G, child, width=dx, vert_gap=vert_gap, 
                                            vert_loc=vert_loc-vert_gap, xcenter=nextx))
        return pos

if __name__ == "__main__":
    root = tk.Tk()
    app = AppExplorateurArbre(root)
    root.mainloop()