import cv2
import numpy as np
from ultralytics import YOLO
import cv2
import mediapipe as mp

# Cargar el modelo YOLO entrenado para lentes
modelo_lentes = YOLO("src/train/best.pt")


face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_eye.xml")
smile_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_smile.xml")

def detectar_rostro(gray):
    faces = face_cascade.detectMultiScale(gray, scaleFactor=1.3, minNeighbors=5)
    if len(faces) == 0:
        return None
    # Elegir el rostro más grande
    return max(faces, key=lambda r: r[2] * r[3])

def detectar_ojos(gray, rostro):
    x, y, w, h = rostro
    roi_gray = gray[y:y+h, x:x+w]
    ojos = eye_cascade.detectMultiScale(roi_gray)
    return ojos if len(ojos) >= 1 else None

def evaluar_iluminacion(gray):
    brillo = np.mean(gray)
    return brillo > 50  # Umbral ajustable

def detectar_labios(gray, rostro):
    x, y, w, h = rostro
    roi_gray = gray[y+int(h*0.6):y+h, x:x+w]  # Parte inferior del rostro
    labios = smile_cascade.detectMultiScale(roi_gray, 1.8, 20)
    return roi_gray if labios is not None else None

def estimar_parpadeo(status_list):
    transiciones = sum(1 for i in range(1, len(status_list)) if status_list[i] != status_list[i-1])
    return (transiciones / len(status_list)) * 100 if status_list else 0

def estimar_mov_labios(frames_labios):
    if len(frames_labios) < 2:
        return 0

    cambios = 0
    tamaño_estandar = (100, 50)  # Ancho x Alto

    for i in range(1, len(frames_labios)):
        try:
            anterior = cv2.resize(frames_labios[i - 1], tamaño_estandar)
            actual = cv2.resize(frames_labios[i], tamaño_estandar)

            diff = cv2.absdiff(actual, anterior)
            cambio = np.sum(diff > 25)

            if cambio > 100:
                cambios += 1
        except Exception as e:
            print(f"[WARN] Error al comparar labios en frame {i}: {e}")
            continue

    return (cambios / len(frames_labios)) * 100


def estimar_lentes(frame: np.ndarray,rostro, debug=False) -> bool:
    mp_face_mesh = mp.solutions.face_mesh
    face_mesh = mp_face_mesh.FaceMesh(static_image_mode=True, max_num_faces=1, refine_landmarks=True)

    h, w, _ = frame.shape
    img_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    resultados = face_mesh.process(img_rgb)

    if not resultados.multi_face_landmarks:
        return False

    total_contornos = 0
    ojos_indices = {
        "izquierdo": [33, 133],
        "derecho": [362, 263]
    }

    for face_landmarks in resultados.multi_face_landmarks:
        for lado, (p_ini, p_fin) in ojos_indices.items():
            x1 = int(face_landmarks.landmark[p_ini].x * w)
            x2 = int(face_landmarks.landmark[p_fin].x * w)
            y1 = int(min(face_landmarks.landmark[p_ini].y, face_landmarks.landmark[p_fin].y) * h) - 25
            y2 = int(max(face_landmarks.landmark[p_ini].y, face_landmarks.landmark[p_fin].y) * h) + 25

            x1, x2 = max(x1, 0), min(x2, w)
            y1, y2 = max(y1, 0), min(y2, h)

            if x2 <= x1 or y2 <= y1:
                continue

            zona_lentes = frame[y1:y2, x1:x2]
            gray = cv2.cvtColor(zona_lentes, cv2.COLOR_BGR2GRAY)

            _, binarizada = cv2.threshold(gray, 60, 255, cv2.THRESH_BINARY_INV)
            kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
            procesada = cv2.morphologyEx(binarizada, cv2.MORPH_OPEN, kernel)

            contornos, _ = cv2.findContours(procesada, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
            contornos_validos = [cnt for cnt in contornos if cv2.contourArea(cnt) > 30]

            if debug:
                print(f"[INFO] {lado} -> contornos: {len(contornos_validos)}")

            total_contornos += len(contornos_validos)

    return total_contornos >= 10

def estimar_auriculares(frame: np.ndarray, rostro: tuple, debug=False) -> bool:
    x, y, w, h = rostro
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # === Laterales del rostro donde suelen estar auriculares ===
    margen = int(w * 0.35)
    lado_izq = gray[y:y+h, max(x - margen, 0):x]
    lado_der = gray[y:y+h, x+w:min(x+w+margen, frame.shape[1])]

    # === Preprocesamiento morfológico ===
    def detectar_bordes_morf(region, nombre):
        # Binarizar: resaltar objetos oscuros sobre fondo claro
        _, binaria = cv2.threshold(region, 80, 255, cv2.THRESH_BINARY_INV)
        kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
        apertura = cv2.morphologyEx(binaria, cv2.MORPH_OPEN, kernel)

        # Contornos
        contornos, _ = cv2.findContours(apertura, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        area_total = sum(cv2.contourArea(c) for c in contornos)

        if debug:
            cv2.imshow(f"debug_{nombre}", apertura)
            print(f"Contornos {nombre}: {len(contornos)}, Área total: {area_total}")

        return area_total, len(contornos)

    area_izq, cont_izq = detectar_bordes_morf(lado_izq, "izq")
    area_der, cont_der = detectar_bordes_morf(lado_der, "der")

    # === Inferencia: si hay masa sólida en los lados => auriculares ===
    usa_auriculares = (area_izq > 1000 or area_der > 1000)

    return usa_auriculares
    