#!/usr/bin/env python3
"""
Stable Standalone Face Recognition App
Fixed version that won't crash on macOS
"""

import cv2
import numpy as np
import os
import json
import csv
import threading
import time
from datetime import datetime, date
import tkinter as tk
from tkinter import ttk, messagebox
from PIL import Image, ImageTk

class StableFaceRecognitionApp:
    def __init__(self):
        # Initialize main window
        self.root = tk.Tk()
        self.root.title("Stable Face Recognition System")
        self.root.geometry("1000x700")
        self.root.resizable(True, True)
        
        # Initialize variables
        self.camera = None
        self.is_running = False
        self.recognition_active = False
        
        # Face recognition settings
        self.confidence_threshold = 60  # LBPH: lower is better
        
        # Data storage
        self.attendance_data = {}
        self.user_data = {}
        
        # Statistics
        self.total_detections = 0
        self.successful_recognitions = 0
        
        # Initialize components
        self.load_user_data()
        self.load_recognizer()
        self.create_ui()
        self.start_camera()
        
        # Handle window closing
        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
        
        print("✅ Stable Face Recognition App initialized")
        print("🖥️ GUI window should be visible now")
        
        # Force window to front and focus
        self.root.lift()
        self.root.attributes('-topmost', True)
        self.root.after_idle(lambda: self.root.attributes('-topmost', False))

    def load_user_data(self):
        """Load user data from user_data.json"""
        try:
            if os.path.exists("user_data.json"):
                with open("user_data.json", "r") as f:
                    self.user_data = json.load(f)
                print(f"✅ Loaded {len(self.user_data)} users from user_data.json")
            else:
                print("⚠️ user_data.json not found")
                self.user_data = {}
        except Exception as e:
            print(f"❌ Error loading user data: {e}")
            self.user_data = {}

    def load_recognizer(self):
        """Load the face recognizer model"""
        try:
            self.recognizer = cv2.face.LBPHFaceRecognizer_create()
            model_file = "face_recognizer.yml"
            
            if os.path.exists(model_file):
                self.recognizer.read(model_file)
                print("✅ Loaded face recognition model")
            else:
                print("⚠️ Face recognition model not found")
                self.recognizer = None
                
        except Exception as e:
            print(f"❌ Error loading recognizer: {e}")
            self.recognizer = None

    def start_camera(self):
        """Initialize camera with error handling"""
        try:
            # Try camera 0 first (MacBook built-in)
            self.camera = cv2.VideoCapture(0)
            if not self.camera.isOpened():
                print("❌ Camera 0 failed, trying camera 1")
                self.camera = cv2.VideoCapture(1)
            
            if not self.camera.isOpened():
                print("❌ All cameras failed")
                return
            
            # Set camera properties
            self.camera.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
            self.camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
            self.camera.set(cv2.CAP_PROP_FPS, 30)
            
            print("✅ Camera started successfully")
            
        except Exception as e:
            print(f"❌ Error starting camera: {e}")

    def create_ui(self):
        """Create the user interface"""
        # Main container
        main_frame = tk.Frame(self.root)
        main_frame.pack(fill="both", expand=True, padx=10, pady=10)
        
        # Title
        title_label = tk.Label(main_frame, text="Stable Face Recognition System", 
                              font=("Arial", 16, "bold"))
        title_label.pack(pady=(0, 20))
        
        # Top row: Controls
        control_frame = tk.Frame(main_frame)
        control_frame.pack(fill="x", pady=(0, 10))
        
        # Start/Stop buttons
        self.start_button = tk.Button(control_frame, text="Start Recognition", 
                                     command=self.start_recognition, bg="green", fg="white")
        self.start_button.pack(side="left", padx=(0, 10))
        
        self.stop_button = tk.Button(control_frame, text="Stop Recognition", 
                                    command=self.stop_recognition, bg="red", fg="white", state="disabled")
        self.stop_button.pack(side="left", padx=(0, 10))
        
        # Settings
        settings_frame = tk.Frame(control_frame)
        settings_frame.pack(side="right")
        
        tk.Label(settings_frame, text="Confidence Threshold:").pack(side="left")
        self.threshold_var = tk.StringVar(value=str(self.confidence_threshold))
        threshold_entry = tk.Entry(settings_frame, textvariable=self.threshold_var, width=5)
        threshold_entry.pack(side="left", padx=(5, 0))
        
        # Middle row: Camera feed and info
        middle_frame = tk.Frame(main_frame)
        middle_frame.pack(fill="both", expand=True, pady=(0, 10))
        
        # Camera feed
        camera_frame = tk.Frame(middle_frame)
        camera_frame.pack(side="left", padx=(0, 20))
        
        tk.Label(camera_frame, text="Camera Feed:", font=("Arial", 12, "bold")).pack(pady=(0, 5))
        
        self.camera_label = tk.Label(camera_frame, text="Camera not started", width=80, height=20, bg="black", fg="white")
        self.camera_label.pack()
        
        # Info frame
        info_frame = tk.Frame(middle_frame)
        info_frame.pack(side="right", fill="both", expand=True)
        
        tk.Label(info_frame, text="System Status:", font=("Arial", 12, "bold")).pack(pady=(0, 10))
        
        # Status labels
        self.status_label = tk.Label(info_frame, text="Status: Ready", font=("Arial", 10))
        self.status_label.pack(pady=(0, 5))
        
        self.stats_label = tk.Label(info_frame, text="Detections: 0 | Recognitions: 0", font=("Arial", 10))
        self.stats_label.pack(pady=(0, 5))
        
        # User info
        tk.Label(info_frame, text="Registered Users:", font=("Arial", 10, "bold")).pack(pady=(10, 5))
        self.users_text = tk.Text(info_frame, height=8, width=30)
        self.users_text.pack(fill="both", expand=True)
        
        # Populate users
        self.update_users_display()
        
        # Bottom row: Recognition history
        bottom_frame = tk.Frame(main_frame)
        bottom_frame.pack(fill="x")
        
        tk.Label(bottom_frame, text="Recognition History:", font=("Arial", 12, "bold")).pack(pady=(0, 5))
        
        # History tree
        tree_frame = tk.Frame(bottom_frame)
        tree_frame.pack(fill="both", expand=True)
        
        self.history_tree = ttk.Treeview(tree_frame, columns=("Name", "Time", "Status"), show="headings", height=6)
        self.history_tree.heading("Name", text="Name")
        self.history_tree.heading("Time", text="Time")
        self.history_tree.heading("Status", text="Status")
        
        self.history_tree.column("Name", width=150)
        self.history_tree.column("Time", width=150)
        self.history_tree.column("Status", width=100)
        
        # Scrollbar for history
        history_scrollbar = ttk.Scrollbar(tree_frame, orient="vertical", command=self.history_tree.yview)
        self.history_tree.configure(yscrollcommand=history_scrollbar.set)
        
        self.history_tree.pack(side="left", fill="both", expand=True)
        history_scrollbar.pack(side="right", fill="y")

    def update_users_display(self):
        """Update the users display"""
        self.users_text.delete(1.0, tk.END)
        for user_id, user_info in self.user_data.items():
            self.users_text.insert(tk.END, f"ID: {user_id}\n")
            self.users_text.insert(tk.END, f"Name: {user_info.get('name', 'Unknown')}\n")
            self.users_text.insert(tk.END, f"Department: {user_info.get('department', 'N/A')}\n")
            self.users_text.insert(tk.END, "─" * 20 + "\n")

    def start_recognition(self):
        """Start face recognition"""
        if not self.recognition_active:
            self.recognition_active = True
            self.is_running = True
            self.start_button.configure(state="disabled")
            self.stop_button.configure(state="normal")
            
            # Start recognition loop in separate thread
            self.recognition_thread = threading.Thread(target=self.recognition_loop, daemon=True)
            self.recognition_thread.start()
            
            self.status_label.configure(text="Status: Recognition Active")
            print("🚀 Face recognition started")

    def stop_recognition(self):
        """Stop face recognition"""
        self.recognition_active = False
        self.is_running = False
        self.start_button.configure(state="normal")
        self.stop_button.configure(state="disabled")
        self.status_label.configure(text="Status: Stopped")
        print("⏹️ Face recognition stopped")

    def recognition_loop(self):
        """Main recognition loop with error handling"""
        while self.recognition_active and self.is_running:
            try:
                if not self.camera or not self.camera.isOpened():
                    time.sleep(0.1)
                    continue
                
                ret, frame = self.camera.read()
                if not ret:
                    continue
                
                # Resize frame for display
                display_frame = cv2.resize(frame, (640, 480))
                
                # Convert to RGB for display
                rgb_frame = cv2.cvtColor(display_frame, cv2.COLOR_BGR2RGB)
                
                # Face detection
                gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
                faces = face_cascade.detectMultiScale(gray, 1.1, 4)
                
                # Process each face
                for (x, y, w, h) in faces:
                    self.total_detections += 1
                    
                    # Draw rectangle around face
                    cv2.rectangle(display_frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
                    
                    # Face recognition
                    if self.recognizer:
                        face_roi = gray[y:y + h, x:x + w]
                        face_roi = cv2.resize(face_roi, (100, 100))
                        
                        try:
                            label, confidence = self.recognizer.predict(face_roi)
                            
                            if confidence < self.confidence_threshold:
                                # Face recognized
                                user_name = self.user_data.get(str(label), {}).get("name", f"User_{label}")
                                
                                # Add label
                                cv2.putText(display_frame, f"User: {user_name}", (x, y - 10), 
                                          cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
                                cv2.putText(display_frame, f"Confidence: {confidence:.1f}", (x, y + h + 20), 
                                          cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
                                
                                # Log attendance
                                self.log_attendance(label, user_name)
                                self.add_to_history(user_name, datetime.now().strftime("%H:%M:%S"), "Recognized")
                                self.successful_recognitions += 1
                                
                                print(f"✅ Recognized {user_name} with confidence {confidence:.1f}")
                                
                            else:
                                # Face not recognized
                                cv2.putText(display_frame, "Unknown", (x, y - 10), 
                                          cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
                                
                        except Exception as e:
                            print(f"Recognition error: {e}")
                            cv2.putText(display_frame, "Error", (x, y - 10), 
                                      cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
                
                # Update statistics
                self.root.after(0, lambda: self.stats_label.configure(
                    text=f"Detections: {self.total_detections} | Recognitions: {self.successful_recognitions}"))
                
                # Convert frame to PIL Image for display
                rgb_frame = cv2.cvtColor(display_frame, cv2.COLOR_BGR2RGB)
                pil_image = Image.fromarray(rgb_frame)
                photo = ImageTk.PhotoImage(pil_image)
                
                # Update camera display
                self.root.after(0, lambda: self.camera_label.configure(image=photo, text=""))
                self.root.after(0, lambda: setattr(self.camera_label, 'image', photo))
                
                time.sleep(0.03)  # ~30 FPS
                
            except Exception as e:
                print(f"❌ Error in recognition loop: {e}")
                time.sleep(0.1)

    def log_attendance(self, user_id, user_name):
        """Log attendance with check-in/check-out logic"""
        try:
            today = date.today().strftime("%Y-%m-%d")
            
            if today not in self.attendance_data:
                self.attendance_data[today] = {}
            
            if user_name not in self.attendance_data[today]:
                # First time today - check-in
                self.attendance_data[today][user_name] = {
                    "checkin": datetime.now().strftime("%H:%M:%S"),
                    "checkout": "Not checked out"
                }
                print(f"✅ {user_name} checked in at {self.attendance_data[today][user_name]['checkin']}")
            else:
                # Already checked in today - check-out
                if self.attendance_data[today][user_name]["checkout"] == "Not checked out":
                    self.attendance_data[today][user_name]["checkout"] = datetime.now().strftime("%H:%M:%S")
                    print(f"✅ {user_name} checked out at {self.attendance_data[today][user_name]['checkout']}")
                else:
                    # Update existing checkout time
                    self.attendance_data[today][user_name]["checkout"] = datetime.now().strftime("%H:%M:%S")
                    print(f"✅ {user_name} checkout updated to {self.attendance_data[today][user_name]['checkout']}")
            
            # Save to CSV
            self.save_attendance_to_csv()
            
        except Exception as e:
            print(f"❌ Error logging attendance: {e}")

    def save_attendance_to_csv(self):
        """Save attendance data to CSV file"""
        try:
            with open("attendance_log.csv", "w", newline="") as csvfile:
                fieldnames = ["Date", "Name", "Check-in", "Check-out"]
                writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
                
                writer.writeheader()
                for date_str, users in self.attendance_data.items():
                    for user_name, times in users.items():
                        writer.writerow({
                            "Date": date_str,
                            "Name": user_name,
                            "Check-in": times["checkin"],
                            "Check-out": times["checkout"]
                        })
                        
        except Exception as e:
            print(f"❌ Error saving attendance to CSV: {e}")

    def add_to_history(self, user_name, timestamp, status):
        """Add recognition event to history"""
        try:
            self.history_tree.insert("", "end", values=(user_name, timestamp, status))
            
            # Keep only last 50 entries
            items = self.history_tree.get_children()
            if len(items) > 50:
                self.history_tree.delete(items[0])
            
        except Exception as e:
            print(f"❌ Error adding to history: {e}")

    def on_closing(self):
        """Handle application closing"""
        try:
            self.is_running = False
            self.recognition_active = False
            
            if self.camera:
                self.camera.release()
            
            print("👋 Application closing...")
            self.root.destroy()
            
        except Exception as e:
            print(f"❌ Error during shutdown: {e}")

    def run(self):
        """Start the application"""
        try:
            # Small delay to ensure GUI is ready
            self.root.after(100, lambda: print("🖥️ GUI is ready - window should be visible"))
            self.root.mainloop()
        except Exception as e:
            print(f"❌ Error in main loop: {e}")

if __name__ == "__main__":
    try:
        app = StableFaceRecognitionApp()
        app.run()
    except Exception as e:
        print(f"❌ Fatal error: {e}")
        input("Press Enter to exit...")
