"""
Face Detection Module using OpenCV DNN Face Detector
Uses pre-trained models for accurate face detection.
"""

import cv2
import numpy as np
import os


class FaceDetector:
    def __init__(self, model_dir="models", confidence_threshold=0.5):
        """
        Initialize the face detector with OpenCV DNN models.
        
        Args:
            model_dir: Directory containing the model files
            confidence_threshold: Minimum confidence for face detection (0.0-1.0)
        """
        self.confidence_threshold = confidence_threshold
        self.model_dir = model_dir
        
        # Paths to model files
        self.prototxt_path = os.path.join(model_dir, "deploy.prototxt")
        self.model_path = os.path.join(model_dir, "res10_300x300_ssd_iter_140000.caffemodel")
        
        # Load the DNN face detector
        self.net = None
        self._load_model()
    
    def _load_model(self):
        """Load the OpenCV DNN face detection model."""
        if not os.path.exists(self.prototxt_path):
            raise FileNotFoundError(
                f"Model prototxt file not found: {self.prototxt_path}\n"
                "Please download the model files first."
            )
        if not os.path.exists(self.model_path):
            raise FileNotFoundError(
                f"Model weights file not found: {self.model_path}\n"
                "Please download the model files first."
            )
        
        self.net = cv2.dnn.readNetFromCaffe(self.prototxt_path, self.model_path)
    
    def detect_faces(self, frame):
        """
        Detect faces in a frame.
        
        Args:
            frame: BGR image frame from OpenCV
            
        Returns:
            List of tuples (x, y, w, h, confidence) for each detected face
            where (x, y) is top-left corner, w and h are width and height
        """
        if self.net is None:
            return []
        
        # Get frame dimensions
        (h, w) = frame.shape[:2]
        
        # Create blob from frame (preprocessing for DNN)
        blob = cv2.dnn.blobFromImage(
            cv2.resize(frame, (300, 300)),
            1.0,
            (300, 300),
            (104.0, 177.0, 123.0)
        )
        
        # Pass blob through network
        self.net.setInput(blob)
        detections = self.net.forward()
        
        faces = []
        
        # Process detections
        for i in range(0, detections.shape[2]):
            confidence = detections[0, 0, i, 2]
            
            # Filter weak detections
            if confidence > self.confidence_threshold:
                # Get bounding box coordinates
                box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
                (x1, y1, x2, y2) = box.astype("int")
                
                # Ensure coordinates are within frame bounds
                x1 = max(0, x1)
                y1 = max(0, y1)
                x2 = min(w, x2)
                y2 = min(h, y2)
                
                # Convert to (x, y, w, h) format
                faces.append((x1, y1, x2 - x1, y2 - y1, confidence))
        
        return faces
    
    def draw_faces(self, frame, faces, color=(0, 255, 0), thickness=2):
        """
        Draw bounding boxes around detected faces.
        
        Args:
            frame: Frame to draw on
            faces: List of face detections from detect_faces()
            color: BGR color tuple for bounding boxes
            thickness: Line thickness
            
        Returns:
            Frame with bounding boxes drawn
        """
        result_frame = frame.copy()
        for (x, y, w, h, confidence) in faces:
            cv2.rectangle(result_frame, (x, y), (x + w, y + h), color, thickness)
            # Optionally draw confidence score
            label = f"{confidence:.2f}"
            cv2.putText(result_frame, label, (x, y - 10),
                       cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)
        return result_frame
