#!/usr/bin/env python3
"""
Enhanced webcam server that provides both RTSP streaming and HTTP snapshot endpoints
Compatible with CMS UI requirements for camera configuration
"""

import cv2
import threading
import time
import logging
import socket
from http.server import BaseHTTPRequestHandler, HTTPServer
import subprocess
import sys
import os

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(name)s: %(message)s"
)
logger = logging.getLogger("webcam_rtsp_server")

# Global webcam capture object
webcam_cap = None
rtsp_process = None

class WebcamHTTPHandler(BaseHTTPRequestHandler):
    """HTTP handler for snapshot requests"""
    
    def do_GET(self):
        if self.path == '/snapshot' or self.path == '/snapshot.jpg':
            try:
                # Capture fresh frame from webcam for each request
                ret, frame = webcam_cap.read()
                if ret and frame is not None:
                    # Encode as JPEG
                    success, buffer = cv2.imencode('.jpg', frame, [cv2.IMWRITE_JPEG_QUALITY, 85])
                    if success:
                        self.send_response(200)
                        self.send_header('Content-Type', 'image/jpeg')
                        self.send_header('Content-Length', str(len(buffer)))
                        self.send_header('Cache-Control', 'no-cache, no-store, must-revalidate')
                        self.send_header('Pragma', 'no-cache')
                        self.send_header('Expires', '0')
                        self.end_headers()
                        self.wfile.write(buffer.tobytes())
                        logger.debug(f"Served webcam snapshot, size: {len(buffer)} bytes")
                        return
                    else:
                        logger.error("Failed to encode frame as JPEG")
                else:
                    logger.error("Failed to capture frame from webcam")
                
                # Send error response
                self.send_response(500)
                self.send_header('Content-Type', 'text/plain')
                self.end_headers()
                self.wfile.write(b'Failed to capture webcam frame')
                
            except Exception as e:
                logger.error(f"Error serving snapshot: {e}")
                self.send_response(500)
                self.send_header('Content-Type', 'text/plain')
                self.end_headers()
                self.wfile.write(f'Error: {str(e)}'.encode())
        
        elif self.path == '/status':
            # Status endpoint for health checking
            self.send_response(200)
            self.send_header('Content-Type', 'application/json')
            self.end_headers()
            
            width = int(webcam_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
            height = int(webcam_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
            fps = webcam_cap.get(cv2.CAP_PROP_FPS)
            
            status = f'{{"status": "online", "width": {width}, "height": {height}, "fps": {fps}}}'
            self.wfile.write(status.encode())
        
        else:
            # 404 for other paths
            self.send_response(404)
            self.send_header('Content-Type', 'text/plain')
            self.end_headers()
            self.wfile.write(b'Not Found - Available endpoints: /snapshot, /snapshot.jpg, /status')
    
    def log_message(self, format, *args):
        # Suppress default HTTP server logging to avoid spam
        pass

def check_ffmpeg():
    """Check if FFmpeg is available for RTSP streaming"""
    try:
        result = subprocess.run(['ffmpeg', '-version'], 
                              capture_output=True, text=True, timeout=5)
        if result.returncode == 0:
            logger.info("FFmpeg found and working")
            return True
    except (subprocess.TimeoutExpired, FileNotFoundError, subprocess.SubprocessError):
        pass
    
    logger.warning("FFmpeg not found. RTSP streaming will not be available.")
    logger.info("To enable RTSP streaming, install FFmpeg:")
    logger.info("  Windows: Download from https://ffmpeg.org/download.html")
    logger.info("  Linux: sudo apt install ffmpeg")
    logger.info("  macOS: brew install ffmpeg")
    return False

def get_windows_camera_name():
    """Get the actual camera device name on Windows"""
    try:
        # List video devices using FFmpeg with proper encoding handling
        result = subprocess.run(['ffmpeg', '-f', 'dshow', '-list_devices', 'true', '-i', 'dummy'], 
                              capture_output=True, text=True, timeout=10, encoding='utf-8', errors='ignore')
        output = result.stderr  # FFmpeg outputs device list to stderr
        
        # Look for video devices in the output
        lines = output.split('\n')
        video_devices = []
        
        # Parse the output - look for lines with (video) that contain device names in quotes
        for line in lines:
            if '[dshow @' in line and '(video)' in line and '"' in line:
                # Extract device name between first pair of quotes
                start = line.find('"') + 1
                end = line.find('"', start)
                if start > 0 and end > start:
                    device_name = line[start:end]
                    video_devices.append(device_name)
        
        logger.info(f"Found Windows video devices: {video_devices}")
        if video_devices:
            # Force use the first device (index 0) which is the Logitech HD webcam
            return video_devices[0]  # This will be "罗技高清网络摄像机 C930c"
        else:
            logger.info("No devices found via FFmpeg detection, using fallback")
            # Fall through to fallback names
        
    except Exception as e:
        logger.debug(f"Failed to get Windows camera name: {e}")
    
    # Try common camera device names as fallback
    # Prioritize Integrated Camera since that's what's working now
    common_names = [
        "Integrated Camera",  # This is working for the current setup
        "USB Video Device",  # Common name for USB cameras
        "USB2.0 Camera",
        "C930c",  # Direct model name
        "HD Pro Webcam C930c",  # Full Logitech name
        "Logitech",  # Brand name
        "USB Camera",
        "Webcam"
    ]
    logger.info(f"Using fallback camera names: {common_names}")
    return common_names[0]  # Return "Integrated Camera" first

def start_rtsp_stream(webcam_index=0, rtsp_port=8554):
    """Start RTSP streaming using FFmpeg"""
    global rtsp_process
    
    if not check_ffmpeg():
        return None
    
    try:
        # Get the actual camera device name for Windows
        if sys.platform.startswith('win'):
            camera_name = get_windows_camera_name()
            logger.info(f"Using Windows camera device: {camera_name}")
        
        # FFmpeg command to stream webcam via RTSP
        if sys.platform.startswith('win'):
            cmd = [
                'ffmpeg',
                '-f', 'dshow',
                '-i', f'video={camera_name}',  # Use detected camera name
                '-c:v', 'libx264',
                '-preset', 'veryfast',
                '-tune', 'zerolatency',
                '-r', '30',
                '-s', '1280x720',
                '-f', 'rtsp',
                f'rtsp://localhost:{rtsp_port}/stream'
            ]
        elif sys.platform.startswith('linux'):
            cmd = [
                'ffmpeg',
                '-f', 'v4l2',
                '-i', f'/dev/video{webcam_index}',
                '-c:v', 'libx264',
                '-preset', 'veryfast',
                '-tune', 'zerolatency',
                '-r', '30',
                '-s', '1280x720',
                '-f', 'rtsp',
                f'rtsp://localhost:{rtsp_port}/stream'
            ]
        else:  # macOS
            cmd = [
                'ffmpeg',
                '-f', 'avfoundation',
                '-i', f'{webcam_index}:',
                '-c:v', 'libx264',
                '-preset', 'veryfast',
                '-tune', 'zerolatency',
                '-r', '30',
                '-s', '1280x720',
                '-f', 'rtsp',
                f'rtsp://localhost:{rtsp_port}/stream'
            ]
        
        logger.info(f"Starting RTSP stream on rtsp://localhost:{rtsp_port}/stream")
        logger.info(f"FFmpeg command: {' '.join(cmd)}")
        
        rtsp_process = subprocess.Popen(
            cmd,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )
        
        # Give FFmpeg a moment to start
        time.sleep(2)
        
        # Check if process is still running
        if rtsp_process.poll() is None:
            logger.info("RTSP streaming started successfully")
            return rtsp_process
        else:
            # Get error output if process failed
            stdout, stderr = rtsp_process.communicate(timeout=2)
            logger.error("RTSP streaming failed to start")
            logger.error(f"FFmpeg stdout: {stdout}")
            logger.error(f"FFmpeg stderr: {stderr}")
            return None
            
    except Exception as e:
        logger.error(f"Failed to start RTSP stream: {e}")
        return None

def get_local_ip():
    """Get the Wireguard IP address for external access"""
    # Use Wireguard IP for external access
    return "10.101.1.4"

def main():
    global webcam_cap, rtsp_process
    
    # Configuration - Force use index 0 for Logitech HD webcam
    webcam_index = 0  # Logitech HD webcam C930c (1920x1080@30fps)
    http_port = 8080
    rtsp_port = 8554
    
    logger.info("=== Webcam RTSP & HTTP Server ===")
    
    # Initialize webcam
    logger.info("Initializing webcam...")
    webcam_cap = cv2.VideoCapture(webcam_index)
    
    if not webcam_cap.isOpened():
        logger.error(f"Failed to open webcam at index {webcam_index}")
        logger.info("Try different webcam indices (0, 1, 2, etc.)")
        return
    
    # Set webcam properties - Use high resolution for Logitech HD webcam
    webcam_cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
    webcam_cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)
    webcam_cap.set(cv2.CAP_PROP_FPS, 30)
    
    width = int(webcam_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(webcam_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    fps = webcam_cap.get(cv2.CAP_PROP_FPS)
    
    logger.info(f"Webcam initialized: {width}x{height} @ {fps}fps")
    
    # Get local IP for CMS configuration
    local_ip = get_local_ip()
    
    # Start RTSP streaming (optional, requires FFmpeg)
    rtsp_process = start_rtsp_stream(webcam_index, rtsp_port)
    
    # Start HTTP server for snapshots
    server_address = ('0.0.0.0', http_port)  # Bind to all interfaces
    http_server = HTTPServer(server_address, WebcamHTTPHandler)
    
    logger.info("\n=== Server URLs for CMS Configuration ===")
    logger.info(f"HTTP Snapshot URL: http://{local_ip}:{http_port}/snapshot")
    
    if rtsp_process:
        logger.info(f"RTSP Stream URL: rtsp://{local_ip}:{rtsp_port}/stream")
    else:
        logger.info("RTSP Stream: Not available (FFmpeg not found)")
        logger.info("HTTP-only mode: Use Snapshot URL for camera input")
    
    logger.info(f"Status URL: http://{local_ip}:{http_port}/status")
    logger.info("\n=== CMS Configuration Suggestions ===")
    logger.info(f"Camera Identifier: webcam-local-01")
    logger.info(f"RTSP Stream URL: rtsp://{local_ip}:{rtsp_port}/stream")
    logger.info(f"Snapshot URL: http://{local_ip}:{http_port}/snapshot")
    logger.info(f"Snapshot Interval: 2000 (ms)")
    logger.info("\nPress Ctrl+C to stop all servers")
    
    try:
        # Start HTTP server
        http_server.serve_forever()
    except KeyboardInterrupt:
        logger.info("Shutting down servers...")
    finally:
        # Clean up
        if webcam_cap:
            webcam_cap.release()
        
        if rtsp_process:
            logger.info("Stopping RTSP stream...")
            rtsp_process.terminate()
            try:
                rtsp_process.wait(timeout=5)
            except subprocess.TimeoutExpired:
                rtsp_process.kill()
        
        http_server.server_close()
        logger.info("All servers stopped")

if __name__ == "__main__":
    main()