import tkinter as tk
import time
import random
import string

SENTENCES = [
    "The quick brown fox jumps over the lazy dog.",
    "Pack my box with five dozen liquor jugs.",
    "Sphinx of black quartz, judge my vow.",
    "How vexingly quick daft zebras jump!",
    "Typing practice helps improve speed and accuracy.",
    "Practice makes perfect.",
    "Hello, world!",
    "Python is a great programming language.",
    "Stay focused and keep practicing."
]

class TypingPracticeApp:
    def __init__(self, master):
        self.master = master
        master.title("Typing Practice")

        self.target = tk.StringVar()
        self.target.set(random.choice(SENTENCES))

        self.start_time = None
        self.end_time = None
        self.finished = False

        # Target label (display each char with coloring using Text widget)
        self.label_frame = tk.Frame(master)
        self.label_frame.pack(padx=10, pady=(10, 5), fill="x")

        self.target_text = tk.Text(self.label_frame, height=3, wrap="word", state="disabled", bg="white")
        self.target_text.pack(fill="x", expand=True)

        # Entry for typing
        self.entry = tk.Text(master, height=3, wrap="word")
        self.entry.pack(padx=10, pady=(0, 5), fill="x", expand=True)
        self.entry.bind("<KeyRelease>", self.on_key_release)

        # Stats
        self.stats_var = tk.StringVar()
        self.stats_var.set("Time: 0.00 s    Accuracy: 0%    CPM: 0    WPM: 0")
        self.stats_label = tk.Label(master, textvariable=self.stats_var)
        self.stats_label.pack(padx=10, pady=(0, 5), anchor="w")

        # Buttons
        self.buttons_frame = tk.Frame(master)
        self.buttons_frame.pack(padx=10, pady=(0, 10), fill="x")
        self.restart_btn = tk.Button(self.buttons_frame, text="Restart", command=self.restart)
        self.restart_btn.pack(side="left")
        self.next_btn = tk.Button(self.buttons_frame, text="Next Sentence", command=self.next_sentence)
        self.next_btn.pack(side="left", padx=5)

        self.draw_target()

    def draw_target(self):
        self.target_text.config(state="normal")
        self.target_text.delete("1.0", "end")
        text = self.target.get()
        # insert each char as tag so we can color later
        for i, ch in enumerate(text):
            tag = f"c{i}"
            self.target_text.insert("end", ch, tag)
            # default color: black
            self.target_text.tag_config(tag, foreground="black")
        self.target_text.config(state="disabled")

    def on_key_release(self, event=None):
        if self.finished:
            return
        text = self.target.get()
        typed = self.entry.get("1.0", "end-1c")

        if self.start_time is None and typed:
            self.start_time = time.time()

        # compare and color target
        self.target_text.config(state="normal")
        for i, ch in enumerate(text):
            tag = f"c{i}"
            if i < len(typed):
                if typed[i] == ch:
                    self.target_text.tag_config(tag, foreground="green")
                else:
                    self.target_text.tag_config(tag, foreground="red")
            else:
                # not typed yet
                self.target_text.tag_config(tag, foreground="black")
        self.target_text.config(state="disabled")

        # finished?
        if typed == text:
            self.finished = True
            self.end_time = time.time()
            self.update_stats(final=True)
            return

        self.update_stats(final=False)

    def update_stats(self, final=False):
        text = self.target.get()
        typed = self.entry.get("1.0", "end-1c")

        # time
        if self.start_time is None:
            elapsed = 0.0
        else:
            elapsed = (self.end_time - self.start_time) if final and self.end_time else (time.time() - self.start_time)

        # accuracy
        correct = sum(1 for i, ch in enumerate(typed) if i < len(text) and ch == text[i])
        total_typed = len(typed)
        accuracy = (correct / total_typed * 100) if total_typed > 0 else 0.0

        # CPM and WPM (based on characters and words per minute)
        minutes = elapsed / 60 if elapsed > 0 else 1e-6
        cpm = int((total_typed / minutes)) if elapsed > 0 else 0
        # approximate words = characters / 5
        wpm = int((total_typed / 5) / minutes) if elapsed > 0 else 0

        self.stats_var.set(f"Time: {elapsed:.2f} s    Accuracy: {accuracy:.1f}%    CPM: {cpm}    WPM: {wpm}")

    def restart(self):
        self.entry.delete("1.0", "end")
        self.start_time = None
        self.end_time = None
        self.finished = False
        self.update_stats()
        self.draw_target()

    def next_sentence(self):
        self.target.set(random.choice(SENTENCES))
        self.restart()

if __name__ == "__main__":
    root = tk.Tk()
    app = TypingPracticeApp(root)
    root.mainloop()