import tkinter as tk
from tkinter import StringVar, simpledialog, messagebox, scrolledtext, filedialog
import json
import os

class NoteApp:
    def __init__(self, master):
        super().__init__()
        self.master = master
        self.master.title("Your Note App")
        self.master.geometry("600x500")

        self.notes = {}
        self.current_note_id = None
        self.load_notes()

        # Creating the GUI layout
        self.create_widgets()

    def create_widgets(self):
        self.list_frame = tk.Frame(self.master)
        self.list_frame.grid(row=0, column=0, sticky="ns")

        self.text_frame = tk.Frame(self.master)
        self.text_frame.grid(row=0, column=1, sticky="nsew")

        self.master.grid_columnconfigure(1, weight=1)
        self.master.grid_rowconfigure(0, weight=1)

        self.notes_listbox = tk.Listbox(self.list_frame, width=40)
        self.notes_listbox.pack(side="left", fill="y")

        self.scrollbar = tk.Scrollbar(self.list_frame, orient="vertical")
        self.scrollbar.config(command=self.notes_listbox.yview)
        self.scrollbar.pack(side="right", fill="y")

        self.notes_listbox.config(yscrollcommand=self.scrollbar.set)
        self.notes_listbox.bind("<<ListboxSelect>>", self.load_note)

        self.note_text = scrolledtext.ScrolledText(self.text_frame)
        self.note_text.pack(expand=True, fill="both")
        
    

        self.menu_bar = tk.Menu(self.master)
        self.file_menu = tk.Menu(self.menu_bar, tearoff=0)
        self.file_menu.add_command(label="New", command=self.new_note)
        self.file_menu.add_command(label="Save", command=self.save_note)
        self.file_menu.add_command(label="Delete", command=self.delete_note)

        self.file_menu.add_separator()
        self.file_menu.add_command(label="Exit", command=self.master.quit)
        self.menu_bar.add_cascade(label="File", menu=self.file_menu)

        self.format_menu = tk.Menu(self.menu_bar, tearoff=0)
        self.format_menu.add_command(label="Bold", command=self.toggle_bold)
        self.format_menu.add_command(label="Italic", command=self.toggle_italic)
        self.format_menu.add_command(label="Underline", command=self.toggle_underline)
        self.menu_bar.add_cascade(label="Format", menu=self.format_menu)
        self.format_menu.add_command(label="Bullet Point", command=self.insert_bullet_points)

        self.search_menu = tk.Menu(self.menu_bar, tearoff=0)
        self.search_menu.add_command(label="Search", command=self.search_notes)
        self.menu_bar.add_cascade(label="Search", menu=self.search_menu)

        self.master.config(menu=self.menu_bar)

        self.refresh_listbox()

    def load_notes(self):
        try:
            with open("notes.json", "r") as file:
                self.notes = json.load(file)
        except (FileNotFoundError, json.JSONDecodeError):
            self.notes = {}

    def save_notes(self):
        with open("notes.json", "w") as file:
            json.dump(self.notes, file, indent=4)

    def refresh_listbox(self):
        self.notes_listbox.delete(0, tk.END)
        for title in self.notes.keys():
            self.notes_listbox.insert(tk.END, title)

    def new_note(self):
        title = simpledialog.askstring("Note title", "Enter the title of the note:")
        if title:
            self.notes[title] = ""
            self.refresh_listbox()

    def load_note(self, event):
        try:
            index = self.notes_listbox.curselection()[0]
            title = self.notes_listbox.get(index)
            self.note_text.delete('1.0', tk.END)
            self.note_text.insert('1.0', self.notes[title])
            self.current_note_id = title
        except IndexError:
            pass

    def save_note(self):
        if self.current_note_id:
            self.notes[self.current_note_id] = self.note_text.get('1.0', tk.END)
            self.save_notes()

    def delete_note(self):
        if self.current_note_id and messagebox.askyesno("Delete Note", "Are you sure?"):
            del self.notes[self.current_note_id]
            self.current_note_id = None
            self.note_text.delete('1.0', tk.END)
            self.refresh_listbox()
            self.save_notes()
    def toggle_bold(self):
        current_tags = self.note_text.tag_names("sel.first")
        if "bold" in current_tags:
            self.note_text.tag_remove("bold", "sel.first", "sel.last")
        else:
            self.note_text.tag_add("bold", "sel.first", "sel.last")
            self.note_text.tag_configure("bold", font=('Arial', 10, 'bold'))

    def toggle_italic(self):
        current_tags = self.note_text.tag_names("sel.first")
        if "italic" in current_tags:
            self.note_text.tag_remove("italic", "sel.first", "sel.last")
        else:
            self.note_text.tag_add("italic", "sel.first", "sel.last")
            self.note_text.tag_configure("italic", font=('Arial', 10, 'italic'))

    def toggle_underline(self):
        current_tags = self.note_text.tag_names("sel.first")
        if "underline" in current_tags:
            self.note_text.tag_remove("underline", "sel.first", "sel.last")
        else:
            self.note_text.tag_add("underline", "sel.first", "sel.last")
            self.note_text.tag_configure("underline", font=('Arial', 10, 'underline'))
    def insert_bullet_points(self):
        try:
        # Get the index of the start and end of the selected text
            start_idx = self.note_text.index("sel.first")
            end_idx = self.note_text.index("sel.last")
        except tk.TclError:
        # If no text is selected, insert a bullet at the current cursor position
            current_idx = self.note_text.index("insert")
            line_start_idx = f"{current_idx}.0"
            self.note_text.insert(line_start_idx, "* ")
            return
            # Splitting the selection into lines
        start_line = int(start_idx.split('.')[0])
        end_line = int(end_idx.split('.')[0])

    # Adding a bullet to each line in the selected range
        for line in range(start_line, end_line + 1):
            self.note_text.insert(f"{line}.0", "* ")
       
    def search_notes(self):        
        query = simpledialog.askstring("Search", "Enter search query:")
       
        if query:  
                
                self.notes_listbox.delete(0, tk.END)  # Clear the listbox before displaying notes
                k=list(self.notes.keys())
                for i in k:
                    matched=[n for n in k if query in n]
                for i in matched: 
                    self.notes_listbox.insert(tk.END,i)
        else:
            messagebox.showinfo("Error", "You must enter a query !")
                

if __name__ == "__main__":
    root = tk.Tk()
    app = NoteApp(root)
    root.mainloop()
       
    
