import cv2
import math
import mediapipe as mp

class FaceMeshTracker:
    """
    Module 3.5: Advanced Spatial Face Engine (Enterprise Edition)
    Features: 468-point tracking, Micro-Expressions, and Attention Tracking.
    """
    def __init__(self, max_faces=1):
        print("[Aelion Vision] Booting Advanced Spatial Face Mesh Engine...")
        self.mp_face_mesh = mp.solutions.face_mesh
        self.mp_draw = mp.solutions.drawing_utils
        self.mp_drawing_styles = mp.solutions.drawing_styles
        
        self.face_mesh = self.mp_face_mesh.FaceMesh(
            max_num_faces=max_faces,
            refine_landmarks=True, 
            min_detection_confidence=0.7,
            min_tracking_confidence=0.7
        )
        self.lm_list = []

    def find_faces(self, frame, draw=True):
        """Applies the 468-point mesh and extracts coordinate data."""
        img_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        results = self.face_mesh.process(img_rgb)
        self.lm_list = []
        
        if results.multi_face_landmarks:
            for face_lms in results.multi_face_landmarks:
                if draw:
                    # Draw the futuristic tessellation web
                    self.mp_draw.draw_landmarks(
                        image=frame,
                        landmark_list=face_lms,
                        connections=self.mp_face_mesh.FACEMESH_TESSELATION,
                        landmark_drawing_spec=None,
                        connection_drawing_spec=self.mp_drawing_styles.get_default_face_mesh_tesselation_style()
                    )
                    # Draw the exact outlines of the eyes and lips
                    self.mp_draw.draw_landmarks(
                        image=frame,
                        landmark_list=face_lms,
                        connections=self.mp_face_mesh.FACEMESH_CONTOURS,
                        landmark_drawing_spec=None,
                        connection_drawing_spec=self.mp_drawing_styles.get_default_face_mesh_contours_style()
                    )
                
                h, w, c = frame.shape
                face_points = []
                for id, lm in enumerate(face_lms.landmark):
                    cx, cy = int(lm.x * w), int(lm.y * h)
                    face_points.append([id, cx, cy])
                self.lm_list.append(face_points)
                
        return frame

    def get_eye_state(self, face_index=0):
        """Advanced Anti-Spoofing: Checks both eyes to ensure a genuine blink."""
        if len(self.lm_list) <= face_index:
            return False
            
        points = self.lm_list[face_index]
        
        # Right Eye (points 159 top, 145 bottom)
        r_dist = math.hypot(points[159][1] - points[145][1], points[159][2] - points[145][2])
        
        # Left Eye (points 386 top, 374 bottom)
        l_dist = math.hypot(points[386][1] - points[374][1], points[386][2] - points[374][2])
        
        return r_dist < 12 and l_dist < 12

    def get_mouth_state(self, face_index=0):
        """Calculates distance between lips. Returns True if mouth is open."""
        if len(self.lm_list) <= face_index:
            return False
            
        points = self.lm_list[face_index]
        
        dist = math.hypot(points[13][1] - points[14][1], points[13][2] - points[14][2])
        return dist > 15 

    def get_head_orientation(self, face_index=0):
        """Attention Tracking: Uses geometric ratios to determine look direction."""
        if len(self.lm_list) <= face_index:
            return "Unknown"
        
        points = self.lm_list[face_index]
        nose = points[1]
        left_temple = points[234]
        right_temple = points[454]
        chin = points[152]
        forehead = points[10]

        dist_left = math.hypot(nose[1] - left_temple[1], nose[2] - left_temple[2])
        dist_right = math.hypot(nose[1] - right_temple[1], nose[2] - right_temple[2])
        dist_up = math.hypot(nose[1] - forehead[1], nose[2] - forehead[2])
        dist_down = math.hypot(nose[1] - chin[1], nose[2] - chin[2])

        ratio_x = dist_left / (dist_right + 0.0001)
        ratio_y = dist_up / (dist_down + 0.0001)

        if ratio_x > 1.6: return "Right"
        if ratio_x < 0.6: return "Left"
        if ratio_y > 1.3: return "Down"
        if ratio_y < 0.7: return "Up"
        
        return "Center"