# Importar las bibliotecas necesarias
import cv2  # OpenCV para procesamiento de imágenes
import json  # Para manejo de datos JSON
from flask import Flask, Response, render_template  # Framework web Flask
from flask_cors import CORS  # Manejo de CORS para peticiones web
import mediapipe as mp  # Biblioteca para detección de manos
import numpy as np  # Procesamiento numérico

# Inicializar la aplicación Flask
app = Flask(__name__)
CORS(app)  # Habilitar CORS

# Inicialización de MediaPipe Hands y Drawing
mp_hands = mp.solutions.hands  # Módulo de detección de manos
mp_drawing = mp.solutions.drawing_utils  # Utilidades de dibujo
mp_drawing_styles = mp.solutions.drawing_styles  # Estilos de dibujo

# Configurar el detector de manos
hands = mp_hands.Hands(
    static_image_mode=False,  # Modo de video (no imagen estática)
    max_num_hands=2,  # Detectar hasta 2 manos
    min_detection_confidence=0.7,  # Confianza mínima para detección
    min_tracking_confidence=0.5  # Confianza mínima para seguimiento
)

# Configuración de estilos personalizados para el dibujo (usando formato BGR)
hand_landmark_style = mp_drawing.DrawingSpec(
    color=(255, 144, 30),  # Color para los puntos de referencia
    thickness=4,  # Grosor de los puntos
    circle_radius=3  # Radio de los círculos
)
hand_connection_style = mp_drawing.DrawingSpec(
    color=(255, 100, 0),  # Color para las conexiones
    thickness=2,  # Grosor de las líneas
    circle_radius=1  # Radio de los círculos de conexión
)

# Inicializar la captura de video
cap = cv2.VideoCapture(0)  # Usar la cámara predeterminada (0)

# Función principal para detectar y procesar las manos
def detect_hand():
    while True:
        # Capturar frame de la cámara
        success, frame = cap.read()
        if not success:
            break

        # Voltear el frame horizontalmente para una visualización más natural
        frame = cv2.flip(frame, 1)

        # Convertir la imagen a RGB (MediaPipe requiere RGB)
        frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        
        # Procesar el frame para detectar manos
        results = hands.process(frame_rgb)
        
        # Lista para almacenar datos de las manos detectadas
        hand_data = []
        
        # Crear un frame negro para el efecto de realidad virtual
        frame_drawing = np.zeros(frame.shape, dtype=np.uint8)
        
        # Si se detectan manos
        if results.multi_hand_landmarks:
            for hand_landmarks in results.multi_hand_landmarks:
                # Dibujar los landmarks y conexiones con estilo personalizado
                mp_drawing.draw_landmarks(
                    frame_drawing,
                    hand_landmarks,
                    mp_hands.HAND_CONNECTIONS,
                    hand_landmark_style,
                    hand_connection_style
                )
                
                # Extraer los puntos de referencia de la mano
                points = []
                for landmark in hand_landmarks.landmark:
                    points.append({
                        'x': landmark.x,
                        'y': landmark.y,
                        'z': landmark.z
                    })
                hand_data.append(points)

        # Aplicar efectos visuales avanzados
        # Agregar resplandor para efecto futurista
        frame_drawing = cv2.GaussianBlur(frame_drawing, (7,7), 0)
        kernel = np.ones((3,3), np.uint8)
        frame_drawing = cv2.dilate(frame_drawing, kernel, iterations=1)
        
        # Agregar efecto de brillo y resplandor neón
        frame_drawing = cv2.addWeighted(frame_drawing, 1.5, np.zeros_like(frame_drawing), 0, 15)
        
        # Aplicar un segundo resplandor más suave
        glow = cv2.GaussianBlur(frame_drawing, (15,15), 0)
        frame_drawing = cv2.addWeighted(frame_drawing, 1, glow, 0.3, 0)
        
        # Ajustar los canales de color para enfatizar los tonos azules
        b, g, r = cv2.split(frame_drawing)  # Separar canales
        b = cv2.add(b, 30)  # Aumentar el canal azul
        g = cv2.subtract(g, 20)  # Reducir el canal verde
        r = cv2.subtract(r, 30)  # Reducir el canal rojo
        frame_drawing = cv2.merge([b, g, r])  # Combinar canales
        
        # Convertir el frame a JPEG
        _, buffer = cv2.imencode('.jpg', frame_drawing)
        frame_bytes = buffer.tobytes()
        
        # Enviar datos como Server-Sent Events con la imagen y los puntos de referencia
        data = {
            'landmarks': hand_data,
            'image': frame_bytes.hex()
        }
        
        yield f"data: {json.dumps(data)}\n\n"

# Ruta principal que renderiza la plantilla HTML
@app.route('/')
def index():
    return render_template('index.html')

# Ruta para el streaming de datos de tracking de manos
@app.route('/hand-tracking')
def hand_tracking():
    return Response(detect_hand(), mimetype='text/event-stream')

# Punto de entrada principal de la aplicación
if __name__ == '__main__':
    try:
        app.run(debug=True, threaded=True)  # Iniciar servidor Flask
    finally:
        # Liberar recursos al terminar
        cap.release()  # Liberar la cámara
        hands.close()  # Cerrar el detector de manos