import tkinter as tk
from tkinter import scrolledtext, messagebox, filedialog, ttk
import threading
import sounddevice as sd
import numpy as np
from scipy.io.wavfile import write
from faster_whisper import WhisperModel
import os, time

# Configurazione Percorsi
MODEL_PATH = "/opt/fuss-speechtotext/models"
TEMP_AUDIO = "/tmp/fuss_stt.wav"

class FussListenGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("FUSS SpeechToText")
        self.root.geometry("700x820") 
        self.root.resizable(False, False)
        self.root.configure(bg="#e1f3fe")
        
        self.header_bg = "#bdcdd7"
        self.font_bold = ("Arial", 11, "bold")
        
        self.is_recording = False
        self.audio_data = []
        self.model = None
        self.selected_lang = tk.StringVar(value="it")

        # --- HEADER ---
        self.header = tk.Frame(root, bg=self.header_bg, pady=15)
        self.header.pack(fill=tk.X)
        tk.Label(self.header, text="FUSS SPEECHTOTEXT", bg=self.header_bg, 
                 fg="#4a5a6a", font=("Arial", 16, "bold")).pack()
        tk.Label(self.header, text="TRASCRIZIONE VOCALE A RETI NEURALI LOCALI", 
                 bg=self.header_bg, fg="#4a5a6a", font=("Arial", 9, "bold")).pack()

        # --- BARRA LINGUE ---
        self.lang_container = tk.Frame(root, bg="#e1f3fe", pady=10)
        self.lang_container.pack(fill=tk.X)
        self.lang_inner = tk.Frame(self.lang_container, bg="#e1f3fe")
        self.lang_inner.pack()
        tk.Label(self.lang_inner, text="LINGUA:", bg="#e1f3fe", font=self.font_bold, fg="#4a5a6a").pack(side=tk.LEFT, padx=(0, 15))
        for text, val in [("Italiano", "it"), ("Deutsch", "de"), ("English", "en"), ("Français", "fr"), ("Auto", None)]:
            tk.Radiobutton(self.lang_inner, text=text, variable=self.selected_lang, value=val, 
                           bg="#e1f3fe", activebackground="#e1f3fe", font=("Arial", 10), padx=5).pack(side=tk.LEFT)

        # --- BARRA MODALITÀ (SLIDER) ---
        self.slider_container = tk.Frame(root, bg="#e1f3fe", pady=5)
        self.slider_container.pack(fill=tk.X)
        self.slider_inner = tk.Frame(self.slider_container, bg="#e1f3fe")
        self.slider_inner.pack()
        tk.Label(self.slider_inner, text="MODALITÀ:", bg="#e1f3fe", font=self.font_bold, fg="#4a5a6a").pack(side=tk.LEFT, padx=(0, 15))
        self.beam_slider = tk.Scale(self.slider_inner, from_=1, to=5, orient=tk.HORIZONTAL, 
                                    length=350, bg="#e1f3fe", highlightthickness=0,
                                    showvalue=True, troughcolor="#bdcdd7", activebackground="#6189a6",
                                    command=self.aggiorna_descrizione)
        self.beam_slider.set(2)
        self.beam_slider.pack(side=tk.LEFT)
        self.desc_slider = tk.Label(root, text="", bg="#e1f3fe", font=("Arial", 9, "italic"), fg="#7f8c8d")
        self.desc_slider.pack(pady=(0, 5))

        # --- STATO E PROGRESS BAR ---
        self.status_label = tk.Label(root, text="Inizializzazione AI...", bg="#e1f3fe", 
                                     fg="#d35400", font=self.font_bold)
        self.status_label.pack(pady=5)
        
        self.progress = ttk.Progressbar(root, orient=tk.HORIZONTAL, length=600, mode='determinate')
        self.progress.pack(pady=5)

        # --- AREA TESTO ---
        self.text_frame = tk.Frame(root, bg="#e1f3fe", padx=40)
        self.text_frame.pack(fill=tk.BOTH, expand=True)
        self.text_area = scrolledtext.ScrolledText(self.text_frame, wrap=tk.WORD, font=("Arial", 12), height=10, bd=1, relief="solid")
        self.text_area.pack(fill=tk.BOTH, expand=True)
        
        # Menu contestuale
        self.context_menu = tk.Menu(self.text_area, tearoff=0)
        self.context_menu.add_command(label="Taglia", command=lambda: self.text_area.event_generate("<<Cut>>"))
        self.context_menu.add_command(label="Copia", command=lambda: self.text_area.event_generate("<<Copy>>"))
        self.context_menu.add_command(label="Incolla", command=lambda: self.text_area.event_generate("<<Paste>>"))
        self.context_menu.add_separator()
        self.context_menu.add_command(label="Seleziona tutto", command=self.select_all)
        self.text_area.bind("<Button-3>", lambda e: self.context_menu.tk_popup(e.x_root, e.y_root))

# --- AREA BOTTONI (Griglia 2x2 Simmetrica al Pixel) ---
        self.footer = tk.Frame(root, bg="#e1f3fe", padx=40, pady=25)
        self.footer.pack(fill=tk.X, side=tk.BOTTOM)

        # Configuriamo le due colonne del footer per essere identiche (uniform)
        # weight=1 le fa espandere, uniform="gruppo1" le rende larghe uguali
        self.footer.columnconfigure(0, weight=1, uniform="gruppo1")
        self.footer.columnconfigure(1, weight=1, uniform="gruppo1")

        # Pulsante REGISTRA (Riga 0, Colonna 0)
        self.btn_start = tk.Button(self.footer, text="▶ REGISTRA", bg="#569d6b", fg="white",
                                   font=self.font_bold, relief="flat", command=self.start_recording, state=tk.DISABLED)
        self.btn_start.grid(row=0, column=0, sticky="nsew", padx=30, pady=10, ipady=5)

        # Pulsante STOP (Riga 0, Colonna 1)
        self.btn_stop = tk.Button(self.footer, text="■ STOP", bg="#bc6c63", fg="white",
                                  font=self.font_bold, relief="flat", command=self.stop_recording, state=tk.DISABLED)
        self.btn_stop.grid(row=0, column=1, sticky="nsew", padx=30, pady=10, ipady=5)

        # Pulsante CARICA (Riga 1, Colonna 0)
        self.btn_load = tk.Button(self.footer, text="📁 CARICA FILE", bg="#7a8a99", fg="white",
                                  font=self.font_bold, relief="flat", command=self.load_file_dialog, state=tk.DISABLED)
        self.btn_load.grid(row=1, column=0, sticky="nsew", padx=30, pady=10, ipady=5)

        # Pulsante PULISCI (Riga 1, Colonna 1)
        self.btn_clear = tk.Button(self.footer, text="🗑  PULISCI TESTO", bg="#bdcdd7", fg="#4a5a6a",
                                   font=self.font_bold, relief="flat", command=self.clear_text)
        self.btn_clear.grid(row=1, column=1, sticky="nsew", padx=30, pady=10, ipady=5)

        
        threading.Thread(target=self.load_model, daemon=True).start()

    # --- Metodi funzionali (Invariati) ---
    def select_all(self):
        self.text_area.tag_add("sel", "1.0", "end")
        self.text_area.mark_set("insert", "1.0")
        self.text_area.see("insert")
        return "break"

    def clear_text(self):
        if messagebox.askyesno("Pulisci", "Cancellare tutto il testo trascritto?"):
            self.text_area.delete(1.0, tk.END)

    def aggiorna_descrizione(self, valore):
        v = int(valore)
        descrizioni = {
            1: "⚡ Velocità: Risultati immediati.",
            2: "⚖ Equilibrato: Buon compromesso (Default).",
            3: "📝 Standard: Alta precisione grammaticale.",
            4: "🔍 Qualità: Analisi approfondita (Testi complessi).",
            5: "🏆 Ultra: Massima fedeltà letteraria."
        }
        self.desc_slider.config(text=descrizioni.get(v, ""))

    def load_model(self):
        try:
            self.model = WhisperModel(MODEL_PATH, device="cpu", compute_type="int8", cpu_threads=0, num_workers=2)
            self.root.after(0, self.model_ready)
        except Exception as e:
            self.status_label.config(text=f"Errore AI: {e}", fg="red")

    def model_ready(self):
        self.status_label.config(text="✔ SISTEMA PRONTO", fg="#27ae60")
        self.btn_start.config(state=tk.NORMAL)
        self.btn_load.config(state=tk.NORMAL)

    def start_recording(self):
        self.audio_data = []
        self.is_recording = True
        self.btn_start.config(state=tk.DISABLED, bg="#95a5a6")
        self.btn_stop.config(state=tk.NORMAL)
        self.status_label.config(text="🔴 IN ASCOLTO...", fg="#c0392b")
        self.progress['value'] = 0
        threading.Thread(target=self.record_loop, daemon=True).start()

    def record_loop(self):
        try:
            with sd.InputStream(device=None, samplerate=16000, channels=1, callback=lambda i,f,t,s: self.audio_data.append(i.copy())):
                while self.is_recording: sd.sleep(100)
        except Exception as e:
            self.is_recording = False
            self.root.after(0, lambda: messagebox.showerror("Errore Audio", str(e)))

    def stop_recording(self):
        self.is_recording = False
        self.btn_stop.config(state=tk.DISABLED)
        self.status_label.config(text="⌛ ELABORAZIONE AI...", fg="#2980b9")
        self.progress.start(10)
        threading.Thread(target=self.process_audio, daemon=True).start()

    def process_audio(self):
        if not self.audio_data: return
        audio = np.concatenate(self.audio_data, axis=0)
        if np.max(np.abs(audio)) < 0.01:
            self.root.after(0, self.audio_muto)
            return
        write(TEMP_AUDIO, 16000, audio)
        self.run_transcription(TEMP_AUDIO)
        if os.path.exists(TEMP_AUDIO): os.remove(TEMP_AUDIO)

    def audio_muto(self):
        self.status_label.config(text="❌ AUDIO NON RILEVATO", fg="red")
        self.btn_start.config(state=tk.NORMAL, bg="#569d6b")
        self.progress.stop()

    def load_file_dialog(self):
        path = filedialog.askopenfilename(filetypes=[("Audio", "*.mp3 *.wav *.m4a *.flac *.ogg"), ("Tutti i file", "*.*")])
        if path:
            self.status_label.config(text="⌛ ANALISI FILE...", fg="#2980b9")
            self.progress.start(10)
            threading.Thread(target=self.run_transcription, args=(path,), daemon=True).start()

    def run_transcription(self, path):
        try:
            lang = self.selected_lang.get()
            v = self.beam_slider.get()
            b_size = v if v <= 3 else (5 if v == 4 else 10)
            patience = 1.0 if v <= 3 else 2.0
            segments, _ = self.model.transcribe(path, language=lang, beam_size=b_size, patience=patience, condition_on_previous_text=False, initial_prompt="Trascrizione scolastica precisa.")
            text = " ".join([s.text for s in segments]).strip()
            for h in ["Grazie a tutti", "Sottotitoli", "Grazie per la visione"]:
                if h.lower() in text.lower() and len(text) < 25: text = ""
            self.root.after(0, lambda: self.update_ui_post(text))
        except Exception as e:
            self.root.after(0, lambda: self.error_transc(e))

    def update_ui_post(self, text):
        if text:
            current_content = self.text_area.get(1.0, tk.END).strip()
            prefix = "\n\n" if current_content else ""
            self.text_area.insert(tk.END, prefix + text)
            self.text_area.see(tk.END)
            self.root.clipboard_clear()
            self.root.clipboard_append(text)
            self.status_label.config(text="✔ COPIATO NEGLI APPUNTI", fg="#27ae60")
        else:
            self.status_label.config(text="✔ NESSUN TESTO RILEVATO", fg="#d35400")
        self.progress.stop()
        self.progress['value'] = 100
        self.btn_start.config(state=tk.NORMAL, bg="#569d6b")
        self.btn_load.config(state=tk.NORMAL)

    def error_transc(self, e):
        self.status_label.config(text="❌ ERRORE", fg="red")
        self.progress.stop()
        messagebox.showerror("Errore AI", str(e))

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