make this code graphic and design better and add some motiongraphics thing like waiting "import random
import os
import sys
import tkinter as tk
from tkinter import messagebox, ttk

# ---------- Custom HoverButton Class with Enhanced Features ----------

class HoverButton(tk.Canvas):
    def __init__(self, master, width=600, height=60, corner_radius=15, padding=10,
                 bg_color="#E3F2FD", hover_color="#90CAF9", active_color="#1976D2",
                 text="", font=("Arial", 12, "bold"), command=None, **kwargs):
        super().__init__(master, width=width, height=height, highlightthickness=0, bg=master['bg'], **kwargs)
        self.width = width
        self.height = height
        self.corner_radius = corner_radius
        self.padding = padding
        self.bg_color = bg_color
        self.hover_color = hover_color
        self.active_color = active_color
        self.text = text
        self.font = font
        self.command = command
        self.current_color = bg_color
        self.state = "normal"  # Track the state: "normal" or "disabled"

        # Create rounded rectangle
        self.rect = self.create_round_rect(0, 0, width, height, corner_radius, fill=self.bg_color, outline="")

        # Create text with wraplength to handle longer texts
        self.label = self.create_text(width / 2, height / 2, text=self.text, font=self.font, fill="black",
                                      width=width - 2 * padding)

        # Bind events only if the button is not disabled
        self.bind("<Enter>", self.on_enter)
        self.bind("<Leave>", self.on_leave)
        self.bind("<Button-1>", self.on_click)

    def create_round_rect(self, x1, y1, x2, y2, r=25, **kwargs):
        points = [
            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 self.create_polygon(points, smooth=True, **kwargs)

    def on_enter(self, event):
        if self.state == "normal":
            self.itemconfig(self.rect, fill=self.hover_color)
            self.bounce(True)

    def on_leave(self, event):
        if self.state == "normal":
            self.itemconfig(self.rect, fill=self.bg_color)
            self.scale('all', self.width / 2, self.height / 2, 1, 1)

    def on_click(self, event):
        if self.command and self.state == "normal":
            self.command()

    def bounce(self, hover=True, step=0):
        if step > 5:
            self.scale('all', self.width / 2, self.height / 2, 1, 1)
            return
        if hover:
            scale_factor = 1.05 if step % 2 == 0 else 0.95
        else:
            scale_factor = 1.0
        self.scale('all', self.width / 2, self.height / 2, scale_factor, scale_factor)
        self.after(50, lambda: self.bounce(hover, step + 1))

    def update_text(self, new_text):
        self.itemconfig(self.label, text=new_text)

    def set_state(self, state):
        self.state = state
        if state == "disabled":
            self.itemconfig(self.rect, fill='#E0E0E0')
            self.itemconfig(self.label, fill="#A0A0A0")
        else:
            self.itemconfig(self.rect, fill=self.bg_color)
            self.itemconfig(self.label, fill="black")

    def invoke(self):
        if self.state == "normal" and self.command:
            self.command()

# ---------- Resource Path Helper ----------

def resource_path(relative_path):
    try:
        base_path = sys._MEIPASS  # For PyInstaller
    except Exception:
        base_path = os.path.abspath(".")
    return os.path.join(base_path, relative_path)

# ---------- File Reading Functions ----------

def read_words_meanings(file_path):
    words = []
    meanings = []
    examples = []
    with open(file_path, 'r', encoding='utf-8') as file:
        content = file.read().strip().split('\n\n')
        for entry in content:
            lines = [line.strip() for line in entry.strip().split('\n') if line.strip()]
            if len(lines) < 3:
                print(f"Skipping entry due to incorrect format: {entry}")
                continue
            word = lines[0].replace(":", "").strip()
            meaning_line = lines[1]
            example_line = lines[2]
            if not meaning_line.startswith("Meaning: ") or not example_line.startswith("Example: "):
                print(f"Skipping entry due to incorrect format: {entry}")
                continue
            meaning = meaning_line.replace("Meaning: ", "").strip()
            example = example_line.replace("Example: ", "").strip()
            words.append(word)
            meanings.append(meaning)
            examples.append(example)
    return words, meanings, examples

def read_fun_facts_from_file(file_path):
    with open(file_path, 'r', encoding='utf-8') as file:
        fun_facts = [line.strip().strip('"').strip(',') for line in file if line.strip()]
    return fun_facts

# ---------- Utility Functions ----------

def funny_message(fun_facts):
    return random.choice(fun_facts)

def create_quiz(words, meanings, examples, num_questions):
    question_set = set()
    quiz = []
    while len(quiz) < num_questions and len(question_set) < len(words):
        index = random.randint(0, len(words) - 1)
        if index in question_set:
            continue
        question_set.add(index)
        word = words[index]
        meaning = meanings[index]
        example = examples[index]
        options = [meaning]
        wrong_meanings = random.sample([m for m in meanings if m != meaning], min(2, len(meanings) - 1))
        options.extend(wrong_meanings)
        random.shuffle(options)
        correct_index = options.index(meaning)
        quiz.append((word, options, example, correct_index))
    random.shuffle(quiz)
    return quiz

# ---------- Fade-In Animation Function ----------

def fade_in(widget, target="#1976D2", step=0, max_steps=10):
    if step > max_steps:
        return
    r_target, g_target, b_target = hex_to_rgb(target)
    r_current = int(r_target * (step / max_steps))
    g_current = int(g_target * (step / max_steps))
    b_current = int(b_target * (step / max_steps))
    intermediate_color = rgb_to_hex((r_current, g_current, b_current))
    try:
        widget.config(fg=intermediate_color)
    except:
        pass
    widget.after(50, lambda: fade_in(widget, target, step + 1, max_steps))

def hex_to_rgb(hex_color):
    hex_color = hex_color.lstrip('#')
    h_len = len(hex_color)
    return tuple(int(hex_color[i:i + h_len // 3], 16) for i in range(0, h_len, h_len // 3))

def rgb_to_hex(rgb_tuple):
    return '#%02x%02x%02x' % rgb_tuple

# ---------- Main Interface Functions ----------

def show_quiz_interface():
    global question_label, option1_button, option2_button, option3_button
    global joker_button, progress_bar, score_label, remaining_label, progress_var

    # Clear existing widgets
    for widget in root.winfo_children():
        widget.destroy()

    # Create container frame with padding
    container = tk.Frame(root, bg="#f0f8ff")
    container.pack(expand=True, fill='both', padx=20, pady=20)

    # Create sub-frames for layout management
    top_frame = tk.Frame(container, bg="#f0f8ff")
    top_frame.pack(fill='x', pady=(0, 20))

    middle_frame = tk.Frame(container, bg="#f0f8ff")
    middle_frame.pack(fill='x', pady=20)

    bottom_frame = tk.Frame(container, bg="#f0f8ff")
    bottom_frame.pack(fill='x', pady=(20, 0))

    # Question Label with Fade-In Effect
    question_label = tk.Label(top_frame, text="Press Start to begin the quiz",
                              wraplength=800, justify="center",
                              font=("Helvetica", 18, "bold"), bg="#f0f8ff", fg="#1976D2")
    question_label.pack(pady=10)
    fade_in(question_label)

    # Option Buttons Creation with Dynamic Width
    option1_button = HoverButton(middle_frame, width=800, height=60, corner_radius=15, text="Option 1",
                                 font=("Helvetica", 14), bg_color="#E3F2FD",
                                 hover_color="#90CAF9", active_color="#1976D2",
                                 command=lambda i=0: check_answer(i))
    option1_button.pack(pady=10, fill='x')

    option2_button = HoverButton(middle_frame, width=800, height=60, corner_radius=15, text="Option 2",
                                 font=("Helvetica", 14), bg_color="#FFCDD2",
                                 hover_color="#EF9A9A", active_color="#D32F2F",
                                 command=lambda i=1: check_answer(i))
    option2_button.pack(pady=10, fill='x')

    option3_button = HoverButton(middle_frame, width=800, height=60, corner_radius=15, text="Option 3",
                                 font=("Helvetica", 14), bg_color="#C8E6C9",
                                 hover_color="#A5D6A7", active_color="#388E3C",
                                 command=lambda i=2: check_answer(i))
    option3_button.pack(pady=10, fill='x')

    # Control Buttons Creation
    control_frame = tk.Frame(bottom_frame, bg="#f0f8ff")
    control_frame.pack(fill='x')

    joker_button = HoverButton(control_frame, width=200, height=50, corner_radius=15,
                               text="Use Joker", font=("Helvetica", 12, "bold"),
                               bg_color='#FFC107', hover_color='#FFB300', active_color='#FFA000',
                               command=use_joker)
    joker_button.pack(pady=5, side='left', expand=True, fill='x', padx=(0,10))

    start_button = HoverButton(control_frame, width=200, height=50, corner_radius=15,
                               text="Start Quiz", font=("Helvetica", 12, "bold"),
                               bg_color='#4CAF50', hover_color='#43A047', active_color='#388E3C',
                               command=start_quiz)
    start_button.pack(pady=5, side='right', expand=True, fill='x', padx=(10,0))

    # Progress Bar Styling
    style = ttk.Style()
    style.theme_use('clam')
    style.configure("Custom.Horizontal.TProgressbar",
                    troughcolor='#E3F2FD',
                    background='#2196F3',
                    thickness=20)
    style.configure("TLabel", background="#f0f8ff", foreground="#1976D2", font=("Helvetica", 14, "bold"))

    # Progress Bar with Animation
    progress_var = tk.DoubleVar()
    progress_bar = ttk.Progressbar(bottom_frame,
                                   style="Custom.Horizontal.TProgressbar",
                                   variable=progress_var,
                                   maximum=100,
                                   length=300)
    progress_bar.pack(pady=10)

    # Score Label
    score_label = tk.Label(bottom_frame, text="Score: 0",
                           font=("Helvetica", 14, "bold"),
                           fg="#1976D2",
                           bg="#f0f8ff")
    score_label.pack(pady=5)

    # Remaining Questions Label
    remaining_label = tk.Label(bottom_frame, text="Remaining: 0",
                               font=("Helvetica", 14, "bold"),
                               fg="#1976D2",
                               bg="#f0f8ff")
    remaining_label.pack(pady=5)

def create_question_count_selection():
    # Clear any existing widgets
    for widget in root.winfo_children():
        widget.destroy()

    # Create main container with padding
    container = tk.Frame(root, bg="#f0f8ff")
    container.pack(expand=True, fill='both', padx=40, pady=40)

    # Title Label with Fade-In Effect
    title_label = tk.Label(container,
                           text="How many questions would you like to answer?",
                           font=("Helvetica", 20, "bold"),
                           fg="#1976D2",
                           bg="#f0f8ff",
                           pady=20)
    title_label.pack()
    fade_in(title_label)

    # Frame for Buttons
    button_frame = tk.Frame(container, bg="#f0f8ff")
    button_frame.pack(expand=True)

    # Question Count Options with Enhanced Design
    questions_options = [
        {"count": 5, "bg": "#E3F2FD", "hover": "#90CAF9", "active": "#64B5F6"},
        {"count": 10, "bg": "#FFCDD2", "hover": "#EF9A9A", "active": "#E57373"},
        {"count": 15, "bg": "#C8E6C9", "hover": "#A5D6A7", "active": "#81C784"},
        {"count": 20, "bg": "#FFF9C4", "hover": "#FFF59D", "active": "#FFF176"},
    ]

    for option in questions_options:
        btn = HoverButton(button_frame,
                          width=800,
                          height=60,
                          corner_radius=20,
                          text=f"{option['count']} Questions",
                          font=("Helvetica", 14, "bold"),
                          bg_color=option['bg'],
                          hover_color=option['hover'],
                          active_color=option['active'],
                          command=lambda c=option['count']: select_question_count(c))
        btn.pack(pady=10, fill='x')

# ---------- Quiz Logic Functions ----------

def select_question_count(count):
    global num_questions
    num_questions = count
    print(f"Selected number of questions: {num_questions}")
    show_quiz_interface()

def check_answer(selected_index):
    global question_number, score, quiz
    word, options, example, correct_index = quiz[question_number]
    funny_threshold = 0.5
    funny = random.random() < funny_threshold

    # Disable all options temporarily
    disable_all_options()

    if selected_index == correct_index:
        score += 1
        feedback = "✅ Correct!"
        color = "#C8E6C9"  # Greenish
        print(f"Question {question_number + 1}: Correct")
    else:
        score -= 1
        feedback = f"❌ Wrong! The correct answer was: '{options[correct_index]}'."
        color = "#FFCDD2"  # Reddish
        print(
            f"Question {question_number + 1}: Wrong (Selected: {options[selected_index]}, Correct: {options[correct_index]})")

    # Highlight the selected and correct options
    if selected_index != correct_index:
        highlight_option(selected_index, "#FFCDD2")  # Wrong selection
    highlight_option(correct_index, "#C8E6C9")  # Correct answer

    # Show feedback
    message = f"{feedback}\n\nExample:\n{example}"
    if funny:
        message += f"\n\n💡 Fun Fact: {funny_message(fun_facts)}"
    messagebox.showinfo("Result", message)

    # Update score label
    calculate_score()

    question_number += 1
    administer_quiz()

def calculate_score():
    global score_label
    percentage = max(score, 0)  # Prevent negative scores
    rounded_percentage = round((percentage / len(quiz)) * 100) if len(quiz) > 0 else 0
    score_label.config(text=f"Score: {rounded_percentage}% ({score})")
    print(f"Updated Score: {score_label.cget('text')}")

def use_joker():
    global joker_used
    if not joker_used and question_number < len(quiz):
        word, options, example, correct_index = quiz[question_number]
        print(f"Using Joker on Question {question_number + 1}: {word}")
        incorrect_indices = [i for i in range(len(options)) if i != correct_index]
        if incorrect_indices:
            option_to_remove = random.choice(incorrect_indices)
            disable_option_button(option_to_remove)
            joker_used = True
            joker_button.set_state("disabled")
            print(f"Joker used. Disabled option {option_to_remove + 1}")
    else:
        print("Joker cannot be used.")

def disable_option_button(index):
    if index == 0:
        option1_button.set_state("disabled")
    elif index == 1:
        option2_button.set_state("disabled")
    elif index == 2:
        option3_button.set_state("disabled")

def disable_all_options():
    option1_button.set_state("disabled")
    option2_button.set_state("disabled")
    option3_button.set_state("disabled")

def highlight_option(index, color):
    if index == 0:
        option1_button.itemconfig(option1_button.rect, fill=color)
    elif index == 1:
        option2_button.itemconfig(option2_button.rect, fill=color)
    elif index == 2:
        option3_button.itemconfig(option3_button.rect, fill=color)

def start_quiz():
    global question_number, score, quiz, joker_used
    question_number = 0
    score = 0
    joker_used = False
    print(f"Starting quiz with {num_questions} questions.")
    quiz = create_quiz(words, meanings, examples, num_questions)
    if not quiz:
        messagebox.showerror("Error", "No valid quiz questions available.")
        create_question_count_selection()
        return
    administer_quiz()

def administer_quiz():
    global question_number, score, quiz, joker_used
    if question_number < len(quiz):
        word, options, example, correct_index = quiz[question_number]
        print(f"Administering Question {question_number + 1}: {word}")
        print(f"Options: {options}, Correct Index: {correct_index}")

        # Update Question Label with Fade-In Effect
        question_text = f"🔍 Question {question_number + 1}: What is the meaning of '{word}'?"
        question_label.config(text=question_text)
        fade_in(question_label)

        # Reset button states and update texts
        option1_button.update_text(options[0])
        option2_button.update_text(options[1])
        option3_button.update_text(options[2])

        option1_button.set_state("normal")
        option2_button.set_state("normal")
        option3_button.set_state("normal")

        # Reset button colors
        option1_button.itemconfig(option1_button.rect, fill=option1_button.bg_color)
        option2_button.itemconfig(option2_button.rect, fill=option2_button.bg_color)
        option3_button.itemconfig(option3_button.rect, fill=option3_button.bg_color)

        # Update commands
        option1_button.command = lambda i=0: check_answer(i)
        option2_button.command = lambda i=1: check_answer(i)
        option3_button.command = lambda i=2: check_answer(i)

        # Update Progress Bar with smooth animation
        progress_percentage = ((question_number + 1) / len(quiz)) * 100
        animate_progress_bar(progress_percentage)

        # Update Remaining Questions Label
        remaining_questions = len(quiz) - (question_number + 1)
        remaining_label.config(text=f"Remaining: {remaining_questions}")
        print(f"Progress: {progress_percentage}%, Remaining: {remaining_questions}")

        # Handle Joker Button State
        if not joker_used:
            joker_button.set_state("normal")
            joker_button.itemconfig(joker_button.rect, fill=joker_button.bg_color)
            joker_button.itemconfig(joker_button.label, fill="black")
            print("Joker is available.")
        else:
            joker_button.set_state("disabled")
            joker_button.itemconfig(joker_button.rect, fill='#E0E0E0')
            joker_button.itemconfig(joker_button.label, fill="#A0A0A0")
            print("Joker has been used.")

    else:
        finalize_quiz()

def animate_progress_bar(target):
    current = progress_var.get()
    if current < target:
        current += 1
        progress_var.set(current)
        root.after(10, lambda: animate_progress_bar(target))
    else:
        progress_var.set(target)

def finalize_quiz():
    percentage_score = (score / len(quiz)) * 100 if len(quiz) > 0 else 0
    rounded_percentage = round(percentage_score)
    joker_message = "🎉 You used the joker. 🎉" if joker_used else "👍 You did not use the joker."
    final_message = (
        f"🎉 Congratulations! 🎉\n\n"
        f"Your final score is {score}/{len(quiz)}.\n"
        f"That's {rounded_percentage}%!\n\n"
        f"{joker_message}\n\n"
        f"Thank you for taking the quiz with Sadra!"
    )
    # Add a final fun fact
    if fun_facts:
        final_message += f"\n\n💡 Last Fun Fact: {funny_message(fun_facts)}"
    messagebox.showinfo("Quiz Completed", final_message)
    print("Quiz completed. Exiting application.")
    root.destroy()

# ---------- Main Application Setup ----------

if __name__ == '__main__':
    # Load resources
    funny_message_file_path = resource_path('funny_message.txt')
    words_meanings_file_path = resource_path('words_meanings.txt')

    # Verify resource files
    if not os.path.exists(words_meanings_file_path):
        messagebox.showerror("Error", f"Error: {words_meanings_file_path} does not exist.")
        sys.exit(1)
    if not os.path.exists(funny_message_file_path):
        messagebox.showerror("Error", f"Error: {funny_message_file_path} does not exist.")
        sys.exit(1)

    words, meanings, examples = read_words_meanings(words_meanings_file_path)
    if not words:
        messagebox.showerror("Error", "No valid entries found in the words_meanings.txt file.")
        sys.exit(1)

    fun_facts = read_fun_facts_from_file(funny_message_file_path)

    # Create the main application window
    root = tk.Tk()
    root.title("🧠 Vocabulary Quiz")
    root.geometry("900x700")
    root.config(bg="#f0f8ff")
    root.resizable(True, True)  # Allow window to be resizable

    # Initialize variables
    question_number = 0
    score = 0
    joker_used = False
    quiz = []
    num_questions = 10  # Default value, will be updated by user selection

    # Center the window on the screen
    root.update_idletasks()
    screen_width = root.winfo_screenwidth()
    screen_height = root.winfo_screenheight()
    x = (screen_width - 900) // 2
    y = (screen_height - 700) // 2
    root.geometry(f"900x700+{x}+{y}")

    # Add window icon (if available)
    try:
        root.iconbitmap(resource_path('quiz_icon.ico'))
    except:
        pass  # Skip if icon file is not available

    # Configure custom styles for the application
    style = ttk.Style()
    style.theme_use('clam')

    # Configure a custom style for the progress bar
    style.configure("Custom.Horizontal.TProgressbar",
                    troughcolor='#E3F2FD',
                    background='#2196F3',
                    thickness=20,
                    borderwidth=0)

    # Configure grid weights for responsive layout
    root.grid_rowconfigure(0, weight=1)
    root.grid_rowconfigure(1, weight=1)
    root.grid_rowconfigure(2, weight=1)
    root.grid_columnconfigure(0, weight=1)

    # Create custom fonts for different elements
    title_font = ("Helvetica", 24, "bold")
    button_font = ("Helvetica", 12)
    label_font = ("Helvetica", 14)

    # ---------- Keyboard Shortcuts ----------

    def handle_keyboard_input(event):
        global question_number
        if question_number < len(quiz):
            if event.char in ['1', '2', '3']:
                index = int(event.char) - 1
                # Check if the option button is enabled before invoking
                if index == 0 and option1_button.state != 'disabled':
                    option1_button.invoke()
                elif index == 1 and option2_button.state != 'disabled':
                    option2_button.invoke()
                elif index == 2 and option3_button.state != 'disabled':
                    option3_button.invoke()
            elif event.char.lower() == 'j' and not joker_used and joker_button.state != 'disabled':
                joker_button.invoke()

    root.bind('<Key>', handle_keyboard_input)

    # ---------- Error Handling ----------

    def on_error(exception_type, value, traceback_info):
        messagebox.showerror("Error",
                             "An unexpected error occurred. The application will exit.\n\n"
                             f"Error: {str(value)}")
        root.destroy()
        sys.exit(1)

    root.report_callback_exception = on_error

    # ---------- Start with Question Count Selection ----------

    create_question_count_selection()

    # ---------- Start the Main Event Loop ----------

    try:
        root.mainloop()
    except Exception as e:
        print(f"Error in main loop: {str(e)}")
        sys.exit(1)"