import sounddevice as sd
import queue
import sys
import json
import threading
from vosk import Model, KaldiRecognizer
import tkinter as tk
from tkinter import ttk
import sv_ttk
import cv2
import pytesseract
from PIL import Image, ImageTk
import time
import board
import busio
from PIL import Image, ImageDraw, ImageFont
import adafruit_ssd1306
import RPi.GPIO as GPIO

pytesseract.pytesseract.tesseract_cmd = r'/usr/bin/tesseract'  # adjust if needed
model = Model("model")  # path to Vosk model folder
SWITCH_PIN = 17
OLED_WIDTH = 128
OLED_HEIGHT = 64

GPIO.setmode(GPIO.BCM)
GPIO.setup(SWITCH_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)

# Functions:
# code for clever cap mode
def cc_script():
    GPIO.cleanup()

    i2c = busio.I2C(board.SCL, board.SDA)
    display = adafruit_ssd1306.SSD1306_I2C(OLED_WIDTH, OLED_HEIGHT, i2c)
    display.fill(0)
    display.show()

    image = Image.new("1", (OLED_WIDTH, OLED_HEIGHT))
    draw = ImageDraw.Draw(image)
    font = ImageFont.load_default()

    def wrap_text(text, font, max_width):
        words = text.split()
        lines = []
        current_line = ""
        for word in words:
            test_line = current_line + word + " "
            bbox = draw.textbbox((0, 0), test_line, font=font)
            width = bbox[2] - bbox[0]
            if width <= max_width:
                current_line = test_line
            else:
                lines.append(current_line.strip())
                current_line = word + " "
        if current_line:
            lines.append(current_line.strip())
        return lines

    def update_display(text):
        draw.rectangle((0, 0, OLED_WIDTH, OLED_HEIGHT), outline=0, fill=0)
        lines = wrap_text(text or "No text detected", font, OLED_WIDTH)
        for i, line in enumerate(lines):
            if i * 10 > OLED_HEIGHT - 10:
                break
            draw.text((0, i * 10), line, font=font, fill=255)
        display.image(image)
        display.show()

    def text_recognition_once():
        cap = cv2.VideoCapture(0)
        ret, frame = cap.read()
        text = ""
        if ret:
            gray = cv2.cvtColor(cv2.resize(frame, None, fx=0.5, fy=0.5), cv2.COLOR_BGR2GRAY)
            text = pytesseract.image_to_string(gray).strip()
        cap.release()
        update_display(text)
        print("[Text] Detected:", text)

    def speech_recognition_once():
        samplerate = 16000
        q = queue.Queue()

        def callback(indata, frames, time, status):
            if status:
                print(status, file=sys.stderr)
            q.put(bytes(indata))

        rec = KaldiRecognizer(model, samplerate)

        with sd.RawInputStream(samplerate=samplerate, blocksize=8000, dtype='int16',
                               channels=1, callback=callback):
            print("🎤 Listening... Flip switch to stop.")
            try:
                while GPIO.input(SWITCH_PIN) == GPIO.LOW:  # Loop until switch opens
                    data = q.get(timeout=5)
                    if rec.AcceptWaveform(data):
                        result = json.loads(rec.Result())
                        print("✔", result["text"])
                        final_text = result["text"]
                        update_display(final_text)
                    else:
                        partial = json.loads(rec.PartialResult())
                        print("…", partial["partial"])
            except queue.Empty:
                print("⚠️ No speech detected.")
            print("\n🛑 Switch OFF — Stopping speech recognition.")

    last_state = None
    try:
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(SWITCH_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)

        while True:
            if GPIO.input(SWITCH_PIN) == GPIO.LOW:
                print("Switch is ON (Closed)")
                speech_recognition_once()
            else:
                print("Switch is OFF (Open)")
                text_recognition_once()
            time.sleep(0.1)
    except KeyboardInterrupt:
        print("Interrupted. Exiting...")
    finally:
        GPIO.cleanup()


def desktop_mode():
    dp2 = tk.Toplevel(dp)
    dp2.title("Desktop Script")

    canvas = tk.Canvas(dp2, width=640, height=480)
    canvas.grid(row=0, column=0, padx=10, pady=10)

    def stop_dp2():
        cap.release()
        dp2.destroy()

    def speech_recognition():
        samplerate = 16000
        q = queue.Queue()

        def callback(indata, frames, time, status):
            if status:
                print(status, file=sys.stderr)
            q.put(bytes(indata))

        rec = KaldiRecognizer(model, samplerate)

        with sd.RawInputStream(samplerate=samplerate, blocksize=8000, dtype='int16',
                               channels=1, callback=callback):
            print("🎤 Listening... Press Ctrl+C to stop.")
            try:
                while True:
                    data = q.get()
                    if rec.AcceptWaveform(data):
                        result = json.loads(rec.Result())
                        print("✔", result["text"])
                        final_text = result["text"]
                        label1.config(text=final_text)
                    else:
                        partial = json.loads(rec.PartialResult())
                        print("…", partial["partial"])
            except KeyboardInterrupt:
                print("\n🛑 Done.")

    text_label = ttk.Label(dp2, text="Detected text will appear here", wraplength=220, justify="left")
    text_label.grid(row=0, column=1, padx=10, pady=10, sticky="nw")

    label = ttk.Label(dp2, text="Speech recognition System is also available, so to use click Start Speech recognition: (Warning: Will use more system resources)")
    label.grid(row=2, column=0, padx=10, pady=10, sticky="ne")

    button0 = ttk.Button(dp2, text="Stop Camera", command=stop_dp2)
    button0.grid(row=1, column=0, padx=10, pady=10, sticky="nw")

    button1 = ttk.Button(
        dp2,
        text="Start Speech Recognition",
        command=lambda: threading.Thread(target=speech_recognition, daemon=True).start(),
        style="Accent.TButton"
    )
    button1.grid(row=2, column=1, padx=10, pady=10, sticky="ne")

    label1 = ttk.Label(dp2, text="Recognized text will appear here:")
    label1.grid(row=3, column=0, padx=10, pady=10, sticky="nw")

    cap = cv2.VideoCapture(0)

    def update_frame():
        ret, frame = cap.read()
        if ret:
            rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            img = Image.fromarray(rgb)
            imgtk = ImageTk.PhotoImage(image=img)
            canvas.imgtk = imgtk
            canvas.create_image(0, 0, anchor="nw", image=imgtk)
            small = cv2.resize(frame, None, fx=0.5, fy=0.5)
            gray = cv2.cvtColor(small, cv2.COLOR_BGR2GRAY)
            text = pytesseract.image_to_string(gray).strip()
            if not text:
                text = "No text detected:"
            text_label.config(text=text)
        dp2.after(100, update_frame)

    def on_close():
        cap.release()
        dp2.destroy()

    dp2.protocol("WM_DELETE_WINDOW", on_close)
    update_frame()


# Main GUI
dp = tk.Tk()
sv_ttk.set_theme("dark")
dp.title("InVision 2.0")

label = ttk.Label(dp, text="Welcome to InVision 2.0")
label.grid(row=0, column=0, padx=10, pady=10, sticky="nw")

label2 = ttk.Label(dp, text="by- DarkFLAME")
label2.grid(row=0, column=1, padx=(10, 10), pady=10, sticky="ne")

label3 = ttk.Label(dp, text="This software features 2 modes, one for use on computers like laptops and other mode (headless which will be used on the raspberry pi.)")
label3.grid(row=1, column=0, padx=(10, 10), pady=(50, 10), sticky="nw")

label4 = ttk.Label(dp, text="Mode : 1) Desktop mode: To be used on computers which have displays for GUI.")
label4.grid(row=2, column=0, padx=(10, 10), pady=(50, 10), sticky="nw")

button = ttk.Button(dp, text="Desktop Mode", style="Accent.TButton", command=desktop_mode)
button.grid(row=3, column=0, padx=(10, 10), pady=(10, 10), sticky="nw")

label5 = ttk.Label(dp, text="Mode : 2) Raspberry pi Headless mode: To be used on the project, press and enjoy your clever summer cap.")
label5.grid(row=4, column=0, padx=(10, 10), pady=(20, 10), sticky="nw")

button2 = ttk.Button(dp, text="Clever Cap Mode", style="Accent.TButton", command=cc_script)
button2.grid(row=5, column=0, padx=(10, 10), pady=(10, 10), sticky="nw")

dp.mainloop()
