import cv2
import serial
import time
import random
import pygame
from pathlib import Path

CAMERA_ID = 1
ARDUINO_PORT = "/dev/cu.usbmodem101"
BAUD = 115200

CENTRE_X = 90
CENTRE_Y = 90

AMPLITUDE_X = 120
AMPLITUDE_Y = 180

SOUNDS_DIR = Path.home() / "Desktop" / "sounds_wav"

ser = serial.Serial(ARDUINO_PORT, BAUD, timeout=1)
time.sleep(2)

pygame.mixer.init()
pygame.mixer.set_num_channels(16)

all_sounds = list(SOUNDS_DIR.glob("*.wav"))

idle_sounds = [f for f in all_sounds if "nard" in f.stem.lower()]
curious_sounds = [f for f in all_sounds if "questionnement" in f.stem.lower()]
fear_sounds = [f for f in all_sounds if "peur" in f.stem.lower()]
panic_sounds = [f for f in all_sounds if "horrible" in f.stem.lower()]

print("Sons trouvés :")
print("Idle :", idle_sounds)
print("Curious :", curious_sounds)
print("Fear :", fear_sounds)
print("Panic :", panic_sounds)

current_channel = None
current_state = None

face_x_smooth = None
face_y_smooth = None

def play_random(state, sounds, volume=1.0):
    global current_channel, current_state

    if not sounds:
        print("Aucun son pour :", state)
        return

    if (
        current_state == state
        and current_channel is not None
        and current_channel.get_busy()
    ):
        current_channel.set_volume(volume)
        return

    if current_channel is not None:
        current_channel.fadeout(700)

    sound_path = random.choice(sounds)
    print("Lecture :", state, sound_path.name)

    sound = pygame.mixer.Sound(str(sound_path))
    channel = sound.play(loops=-1, fade_ms=700)

    if channel is not None:
        channel.set_volume(volume)
        current_channel = channel
        current_state = state
    else:
        print("Impossible de jouer :", sound_path.name)

def smooth(old, new, amount):
    return old + (new - old) * amount

def send_servos(x, y):
    ser.write(f"{int(y)},{int(x)}\n".encode())

cap = cv2.VideoCapture(CAMERA_ID)

face_cascade = cv2.CascadeClassifier(
    cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
)

servo_x = CENTRE_X
servo_y = CENTRE_Y

idle_target_x = CENTRE_X
idle_target_y = CENTRE_Y
last_idle_change = time.time()

try:
    while True:
        ret, frame = cap.read()
        if not ret:
            continue

        h, w, _ = frame.shape
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

        faces = face_cascade.detectMultiScale(
            gray,
            scaleFactor=1.2,
            minNeighbors=3,
            minSize=(100, 100)
        )

        if len(faces) > 0:
            x, y, fw, fh = max(faces, key=lambda f: f[2] * f[3])

            cx = x + fw / 2
            cy = y + fh / 2

            if face_x_smooth is None:
                face_x_smooth = cx
                face_y_smooth = cy
            else:
                face_x_smooth = smooth(face_x_smooth, cx, 0.28)
                face_y_smooth = smooth(face_y_smooth, cy, 0.28)

            offset_x = (face_x_smooth - w / 2) / (w / 2)
            offset_y = (face_y_smooth - h / 2) / (h / 2)

            target_x = CENTRE_X - offset_x * AMPLITUDE_X
            target_y = CENTRE_Y - offset_y * AMPLITUDE_Y

            servo_x = smooth(servo_x, target_x, 0.28)
            servo_y = smooth(servo_y, target_y, 0.28)

            servo_x = max(5, min(175, servo_x))
            servo_y = max(5, min(175, servo_y))

            face_size = fw * fh

            if face_size < 60000:
                play_random("curious", curious_sounds, 0.15)
            elif face_size < 140000:
                play_random("fear", fear_sounds, 0.30)
            else:
                play_random("panic", panic_sounds, 0.45)

            cv2.rectangle(frame, (x, y), (x + fw, y + fh), (0, 255, 0), 2)
            cv2.circle(frame, (int(face_x_smooth), int(face_y_smooth)), 8, (0, 0, 255), -1)

        else:
            play_random("idle", idle_sounds, 0.35)

            face_x_smooth = None
            face_y_smooth = None

            if time.time() - last_idle_change > random.uniform(1.0, 3.5):
                idle_target_x = CENTRE_X + random.randint(-50, 50)
                idle_target_y = CENTRE_Y + random.randint(-25, 25)
                last_idle_change = time.time()

            servo_x = smooth(servo_x, idle_target_x, 0.10)
            servo_y = smooth(servo_y, idle_target_y, 0.10)

        send_servos(servo_x, servo_y)

        cv2.imshow("Eyes tracking", frame)

        if cv2.waitKey(1) & 0xFF == 27:
            break

finally:
    send_servos(CENTRE_X, CENTRE_Y)
    time.sleep(1)

    if current_channel is not None:
        current_channel.fadeout(500)

    cap.release()
    cv2.destroyAllWindows()
    ser.close()