import tkinter as tk
from tkinter import messagebox, filedialog, Menu
import json

class CalculateurApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Calculateur de Dépenses et Revenus")

        # Menu bar (unchanged)
        menubar = Menu(root)
        filemenu = Menu(menubar, tearoff=0)
        filemenu.add_command(label="Sauvegarder", command=self.sauvegarder)
        filemenu.add_command(label="Charger", command=self.charger)
        filemenu.add_separator()
        filemenu.add_command(label="Quitter", command=root.quit)
        menubar.add_cascade(label="Fichier", menu=filemenu)

        helpmenu = Menu(menubar, tearoff=0)
        helpmenu.add_command(label="Aide", command=self.afficher_aide)
        menubar.add_cascade(label="Aide", menu=helpmenu)

        root.config(menu=menubar)

        self.revenus_data = []
        self.depenses_data = []

        # Frames with sticky for expansion
        self.cadre_revenus = tk.LabelFrame(root, text="Revenus - Montant & Description", padx=10, pady=10)
        self.cadre_revenus.grid(row=0, column=0, sticky="nsew", padx=10, pady=10)

        self.cadre_depenses = tk.LabelFrame(root, text="Dépenses - Montant & Description", padx=10, pady=10)
        self.cadre_depenses.grid(row=0, column=1, sticky="nsew", padx=10, pady=10)

        # Configure columns to expand
        root.columnconfigure(0, weight=1)
        root.columnconfigure(1, weight=1)

        # Canvas and scrollbars (improved layout)
        self.create_canvas_and_scrollbar(self.cadre_revenus, self.frame_revenus, self.canvas_revenus, self.scrollbar_revenus)
        self.create_canvas_and_scrollbar(self.cadre_depenses, self.frame_depenses, self.canvas_depenses, self.scrollbar_depenses)

        self.ajouter_champ_revenu()
        self.ajouter_champ_depense()

        # Total frame (unchanged)
        self.cadre_total = tk.LabelFrame(root, text="Total", padx=10, pady=10)
        self.cadre_total.grid(row=1, column=0, columnspan=2, padx=10, pady=10, sticky="ew") #sticky="ew" added
        self.solde_label_texte = tk.Label(self.cadre_total, text="Solde restant : ")
        self.solde_label_texte.grid(row=0, column=0, sticky="w")
        self.solde_label_montant = tk.Label(self.cadre_total, text="0.00 $", fg="black")
        self.solde_label_montant.grid(row=0, column=1)


        self.canvas_revenus.bind_all("<MouseWheel>", self.scroll_with_mouse)
        self.canvas_depenses.bind_all("<MouseWheel>", self.scroll_with_mouse)


    def create_canvas_and_scrollbar(self, parent_frame, inner_frame, canvas, scrollbar):
        canvas.grid(row=0, column=0, sticky="nsew")  # sticky added
        scrollbar.grid(row=0, column=1, sticky="ns")
        canvas.config(yscrollcommand=scrollbar.set)
        canvas.create_window((0, 0), window=inner_frame, anchor="nw")
        parent_frame.rowconfigure(0, weight=1) # Makes the canvas expandable
        parent_frame.columnconfigure(0, weight=1)


    def ajouter_champ_revenu(self):
        self.ajouter_champ(self.frame_revenus, self.revenus_data, "revenus")

    def ajouter_champ_depense(self):
        self.ajouter_champ(self.frame_depenses, self.depenses_data, "depenses")

    def ajouter_champ(self, frame, data_list, entry_type):
        champ_montant = tk.Entry(frame, width=15)
        champ_description = tk.Entry(frame, width=30)
        delete_button = tk.Button(frame, text="Supprimer", command=lambda index=len(data_list): self.supprimer_entree(index, entry_type))

        champ_montant.grid(row=len(data_list), column=0, pady=5, sticky="ew") #sticky added
        champ_description.grid(row=len(data_list), column=1, pady=5, sticky="ew") #sticky added
        delete_button.grid(row=len(data_list), column=2, pady=5, sticky="e") #sticky added

        entry_data = (champ_montant, champ_description, delete_button)
        data_list.append(entry_data)

        champ_montant.bind("<Return>", lambda event, t=entry_type: self.creer_nouveau_champ(t))
        champ_description.bind("<Return>", lambda event, t=entry_type: self.creer_nouveau_champ(t))
        champ_montant.bind("<KeyRelease>", self.calculer)
        champ_description.bind("<KeyRelease>", self.calculer)
        champ_montant.focus()

        frame.update_idletasks()
        if entry_type == "revenus":
            self.canvas_revenus.config(scrollregion=self.canvas_revenus.bbox("all"))
        else:
            self.canvas_depenses.config(scrollregion=self.canvas_depenses.bbox("all"))


    def creer_nouveau_champ(self, entry_type):
        if entry_type == "revenus":
            self.ajouter_champ_revenu()
        elif entry_type == "depenses":
            self.ajouter_champ_depense()

    def calculer(self, event=None):
        try:
            total_revenus = sum(float(e.get()) for e, _, _ in self.revenus_data if e.get())
            total_depenses = sum(float(e.get()) for e, _, _ in self.depenses_data if e.get())
            solde = total_revenus - total_depenses
            self.solde_label_montant.config(text=f"{solde:.2f} $", fg="green" if solde >= 0 else "red")
        except ValueError:
            messagebox.showerror("Erreur", "Veuillez entrer des nombres valides.")
        except Exception as e:
            messagebox.showerror("Erreur de calcul", f"Une erreur inattendue s'est produite: {e}")

    # ... (afficher_aide, scroll_with_mouse remain unchanged)

    def sauvegarder(self):
        try:
            fichier = filedialog.asksaveasfilename(defaultextension=".json", filetypes=[("Fichiers JSON", "*.json")])
            if fichier:
                data = self.get_data()
                with open(fichier, "w") as f:
                    json.dump(data, f, indent=4)
                messagebox.showinfo("Sauvegarde", "Données sauvegardées.")
        except Exception as e:
            messagebox.showerror("Erreur", f"Erreur de sauvegarde: {e}")

    def charger(self):
        try:
            fichier = filedialog.askopenfilename(filetypes=[("Fichiers JSON", "*.json")])
            if fichier:
                with open(fichier, "r") as f:
                    data = json.load(f)
                self.clear_entries()
                self.load_data(data)
                messagebox.showinfo("Chargement", "Données chargées.")
        except Exception as e:
            messagebox.showerror("Erreur", f"Erreur de chargement: {e}")


    def get_data(self):
      return {
          "revenus": [{"montant": e.get(), "description": d.get()} for e, d, _ in self.revenus_data if e.get()],
          "depenses": [{"montant": e.get(), "description": d.get()} for e, d, _ in self.depenses_data if e.get()],
      }

    def load_data(self, data):
        for item_type, data_list in [("revenus", data.get("revenus", [])), ("depenses", data.get("depenses", []))]:
            for item in data_list:
                montant = item.get("montant")
                description = item.get("description")
                if montant and description:
                    if item_type == "revenus":
                        self.ajouter_champ_revenu()
                        self.revenus_data[-1][0].insert(0, montant)
                        self.revenus_data[-1][1].insert(0, description)
                    else:
                        self.ajouter_champ_depense()
                        self.depenses_data[-1][0].insert(0, montant)
                        self.depenses_data[-1][1].insert(0, description)
        self.calculer()

    def supprimer_entree(self, index, type_entree):
        if type_entree == "revenus":
            data = self.revenus_data
            frame = self.frame_revenus
        elif type_entree == "depenses":
            data = self.depenses_data
            frame = self.frame_depenses
        else:
            return

        if 0 <= index < len(data):
            entree, description, bouton = data.pop(index)
            entree.destroy()
            description.destroy()
            bouton.destroy()
            self.reorganiser_grid(type_entree)
            self.calculer()

    def reorganiser_grid(self, type_entree):
        if type_entree == "revenus":
            data = self.revenus_data
            canvas = self.canvas_revenus
        elif type_entree == "depenses":
            data = self.depenses_data
            canvas = self.canvas_depenses
        else:
            return

        for i, (entree, description, bouton) in enumerate(data):
            entree.grid(row=i, column=0, pady=5, sticky="ew")
            description.grid(row=i, column=1, pady=5, sticky="ew")
            bouton.grid(row=i, column=2, pady=5, sticky="e")
            bouton.config(command=lambda index=i, t=type_entree: self.supprimer_entree(index, t))
        
        self.frame_revenus.update_idletasks() # Added to correct sizing issues
        self.frame_depenses.update_idletasks() # Added to correct sizing issues
        canvas.config(scrollregion=canvas.bbox("all"))

    def clear_entries(self):
        for widget in self.frame_revenus.winfo_children():
            widget.destroy()
        for widget in self.frame_depenses.winfo_children():
            widget.destroy()
        self.revenus_data = []
        self.depenses_data = []
        self.ajouter_champ_revenu() # Adds a default entry
        self.ajouter_champ_depense() # Adds a default entry


root = tk.Tk()
app = CalculateurApp(root)
root.mainloop()



make it verticaly instead of horizontaly