#LIBRARIES
import serial
import pytesseract
import cv2
import re
import time
import os
import pygame
from gtts import gTTS

# CONFUGURATION

#windows
pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe"# x windows
PORTA = 'COM6' #'/dev/ttyACM0'   # Serial port where Arduino is connected
BAUD_RATE = 9600         # Communication speed
pygame.mixer.pre_init(44100, -16, 2, 4096) 
pygame.mixer.init()
# Camera initialization
cap = cv2.VideoCapture(0)
if not cap.isOpened():
    print("Error: Camera not found")
    exit()


# CORE FUNCTIONS   

def speak(testo, lingua='it'):
    """
    Convert text to speech and play it.
    
    testo: string to pronounce
    lingua: language code (default Italian)
    """
    # Create gTTS object
    tts = gTTS(text=testo, lang=lingua, slow=False)
    
    # Save temporary audio file
    filename = "test_audio.mp3"
    tts.save(filename)
    
    # Initialize pygame mixer for playback
    
    pygame.mixer.music.load(filename)
    pygame.mixer.music.play()

    # Wait until playback finishes
    while pygame.mixer.music.get_busy():
        time.sleep(0.1)
        
    pygame.mixer.music.unload()
    # Free audio resources
    print(f"Audio played: '{testo}'")


def Boss_Delle_Torte():
    """
    Capture an image from the camera,
    process it, extract text via OCR,
    and read it aloud.
    """
    ret, frame = cap.read()
    print("Capturing image...")
    speak("ho fatto la foto sto per leggere")
    
    if not ret:
        print("Capture error")
        return

    # Pre-processing
    cv2.imwrite("foto_scattata.jpg", frame)
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    #gray = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
   

    # Simplified multi-scale OCR for speed
    testo_finale = "" # final_text
    
    # Different resize scales to improve OCR accuracy
    scales = [1.0, 1.5, 0.8, 0.5, 0.2]
    for s in scales:
        img_temp = cv2.resize(gray, None, fx=s, fy=s, interpolation=cv2.INTER_CUBIC)
        testo = pytesseract.image_to_string(img_temp, lang="ita").strip()
        
        # Accept text only if it has a minimum length
        if len(testo) > 2:
            # Clean unwanted characters
            testo_finale = re.sub(r'[^a-zA-Z0-9 àèéìòù?!]', ' ', testo)
            break

    if testo_finale:
        print("Text found:", testo_finale)
        speak(testo_finale)
    else:
        speak("Non sono riuscito a leggere")


# MAIN LOOP

cheese = False   # First trigger flag (used for synchronization with Arduino)
cheesep = False  # Second step flag (used to confirm trigger sequence)

# Open serial communication with Arduino
ser = serial.Serial(PORTA, BAUD_RATE, timeout=1)
time.sleep(2)
print(f"Connected to {PORTA}")

modalita = "foto"  # Current operating mode: "albero" (obstacle) or "foto" (OCR)
lineap = None
Hspk = False  # Flag to track if the system has spoken about the current obstacle 

while True:
    stacolop = False  
    if ser.in_waiting > 0:
        linea = ser.readline().decode('utf-8').strip()
        if not linea:
            continue

        # MODE SWITCHING COMMANDS
        
        if linea == "-4":
            modalita = "albero"  # Obstacle detection mode
            print("Mode: Obstacle Sensor")
            lineap = None  # Reset previous distance for new mode
            Hspk = False  # Reset speech flag for new mode
            continue
        
        elif linea == "-8":
            modalita = "foto"  # Text reading mode
            print("Mode: Text Reading")
            cheese = False  # Reset trigger flags for new mode
            cheesep = False
            continue
        
        elif linea == "-3680":
            # Special trigger signal from Arduino
            cheese = True
            cheesep = False
            continue

        # OBSTACLE MODE
        
        if modalita == "albero":
            # Remove negative sign if present
            if linea[0] == '-':
                linea = linea[1:]
            
            # If distance is below threshold, warn
            if float(linea) < 50:
                
                if  lineap is None:
                    lineap = float(linea)

                differenza = float(linea) - lineap

                if (differenza > 5 or differenza < -5) or not Hspk: 
                    cose = ("attenzione ostacolo! a ", str(linea), "centiemetri")
                    voce = "".join(cose)
                    speak(voce)
                    Hspk = True # Block all the next allerts until the distance remain stable 
                    lineap = float(linea)
            else:
                # If distance >50, reset for the next obstacle
                Hspk = False
                lineap = None
       
       
        # PHOTO / OCR MODE
        
        elif modalita == "foto":
            print(linea)

            # Remove negative sign if present
            if linea[0] == '-':
                linea = linea[1:]

            if cheese and not cheesep:
                cheesep = True
                
                # verify wether the distance is in range in order to take the picture
                if 15 < float(linea) < 100:
                    print("Distanza valida! Avvio OCR...")
                    Boss_Delle_Torte()
                else:
                    print(f"Distanza {float(linea)} fuori range (15-100). Scatto annullato.")
                
                # Complete reset of flag for the next photo
                cheese = False
                cheesep = False