import cv2
import numpy as np
import os
import datetime
import csv

# Load face cascade
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')

# Ensure OpenCV contrib package is installed for face recognition
try:
    recognizer = cv2.face.LBPHFaceRecognizer_create()
except AttributeError:
    print("Error: OpenCV face module not found. Install opencv-contrib-python with:")
    print("pip install opencv-contrib-python")
    exit()

MODEL_FILE = "face_recognizer.yml"

# Load model only once if available
if os.path.exists(MODEL_FILE):
    recognizer.read(MODEL_FILE)
else:
    print("Error: Model file not found. Train the model first.")
    exit()

# Create LBPH face recognizer
recognizer = cv2.face.LBPHFaceRecognizer_create()

# Directory where face data is stored
FACE_DIR = "registered_faces"
LOG_FILE = "attendance_log.csv"
MODEL_FILE = "face_recognizer.yml"
USER_ID_FILE = "last_user_id.txt"

def save_last_user_id(user_id):
    """Saves the last registered user ID."""
    with open(USER_ID_FILE, "w") as file:
        file.write(str(user_id))

def get_last_user_id():
    """Retrieves the last registered user ID."""
    if os.path.exists(USER_ID_FILE):
        with open(USER_ID_FILE, "r") as file:
            return file.read().strip()
    return None

def count_checkins_today():
    """Counts the total check-ins for the current day."""
    today = datetime.datetime.now().strftime("%Y-%m-%d")
    checkin_count = 0

    if os.path.exists(LOG_FILE):
        with open(LOG_FILE, "r") as file:
            reader = csv.reader(file)
            next(reader, None)  # Skip header
            for row in reader:
                if row[0].startswith(today) and row[2] == "Checkin":
                    checkin_count += 1

    return checkin_count

def collect_training_data(user_id):
    """Captures face images for training."""
    if not user_id.isdigit():
        print("Error: User ID must be a number.")
        return
    
    user_folder = os.path.join(FACE_DIR, user_id)
    os.makedirs(user_folder, exist_ok=True)
    
    cap = cv2.VideoCapture(0)
    count = 0
    
    while count < 20:  # Capture 20 images per user
        ret, frame = cap.read()
        if not ret:
            break

        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(50, 50))
        
        for (x, y, w, h) in faces:
            face_img = gray[y:y + h, x:x + w]
            cv2.imwrite(os.path.join(user_folder, f"{count}.jpg"), face_img)
            count += 1
        
        cv2.imshow("Face Registration", frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    
    cap.release()
    cv2.destroyAllWindows()
    save_last_user_id(user_id)
    print(f"Training data collected for user {user_id}.")

def train_face_recognizer():
    """Trains LBPH face recognizer with collected face images."""
    faces = []
    labels = []
    
    for user_id in os.listdir(FACE_DIR):
        if not user_id.isdigit():
            continue  # Skip invalid folder names
        user_folder = os.path.join(FACE_DIR, user_id)
        for filename in os.listdir(user_folder):
            img_path = os.path.join(user_folder, filename)
            img = cv2.imread(img_path, 0)
            if img is None:
                continue  # Skip unreadable files
            faces.append(img)
            labels.append(int(user_id))
    
    if not faces:
        print("No training data found.")
        return
    
    recognizer.train(faces, np.array(labels))
    recognizer.save(MODEL_FILE)
    print("Training complete.")

def recognize_user():
    """Recognizes a user using the trained model and displays total check-ins."""
    if not os.path.exists(MODEL_FILE):
        print("Error: Model file not found. Train the model first.")
        return
    
    recognizer = cv2.face.LBPHFaceRecognizer_create()
    recognizer.read(MODEL_FILE)
    face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")

    cap = cv2.VideoCapture(0)
    
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(50, 50))
        
        for (x, y, w, h) in faces:
            face_img = gray[y:y + h, x:x + w]
            user_id, confidence = recognizer.predict(face_img)
            
            if confidence < 70:
                text = f"EMP #: {user_id}"
                color = (0, 255, 0)
                log_attendance(user_id)
            else:
                text = "NEED TO REGISTER"
                color = (0, 0, 255)
            
            cv2.rectangle(frame, (x, y), (x + w, y + h), color, 2)
            cv2.putText(frame, text, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 1, color, 2, cv2.LINE_AA)
        
        # Get today's check-in count
        total_checkins = count_checkins_today()
        checkin_text = f"Total Check-ins Today: {total_checkins}"
        developer_text = f"Develop by Alejo"
        
        # Overlay total check-ins on preview
        cv2.putText(frame, checkin_text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2, cv2.LINE_AA)
        # Get the height of the frame
        height, width, _ = frame.shape

# Position the developer text at the bottom (10 pixels from the bottom)
        cv2.putText(frame, developer_text, (10, height - 30), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2, cv2.LINE_AA)

        # Resize the frame to a desired size
        frame = cv2.resize(frame, (1200, 900))  # Resize to 800x600

        cv2.imshow("Face Recognition", frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    
    cap.release()
    cv2.destroyAllWindows()

def log_attendance(user_id):
    """Logs attendance for recognized users in CSV format."""
    now = datetime.datetime.now()
    today = now.strftime("%Y-%m-%d")
    timestamp = now.strftime("%Y-%m-%d %H:%M:%S")
    log_data = []

    # Ensure CSV file exists
    file_exists = os.path.exists(LOG_FILE)

    if not file_exists:
        with open(LOG_FILE, "w", newline="") as file:
            writer = csv.writer(file)
            writer.writerow(["Timestamp", "User ID", "Attendance"])  # Write header

    # Read existing logs
    with open(LOG_FILE, "r") as file:
        reader = csv.reader(file)
        next(reader, None)  # Skip header
        log_data = list(reader)

    # Filter records for today's date and this user
    user_logs_today = [log for log in log_data if log[1] == str(user_id) and log[0].startswith(today)]

    if not user_logs_today:
        # First log of the day - CHECKIN
        log_data.append([timestamp, user_id, "Checkin"])
        print(f"✅ Check-in logged for User {user_id}")
    elif len(user_logs_today) == 1 and user_logs_today[0][2] == "Checkin":
        # Second log of the day - CHECKOUT
        log_data.append([timestamp, user_id, "Checkout"])
        print(f"✅ Checkout logged for User {user_id}")
    else:
       #print(f"⚠️ User {user_id} already has both Check-in and Check-out for today. No changes made.")
        return

    # Write the updated log data back to the CSV
    with open(LOG_FILE, "w", newline="") as file:
        writer = csv.writer(file)
        writer.writerow(["Timestamp", "User ID", "Attendance"])  # Re-write header
        writer.writerows(log_data)

    print("✅ Attendance log updated successfully.")



if __name__ == "__main__":
    last_user_id = get_last_user_id()
    
    while True:
        choice = input("1: Register User\n2: Train Model\n3: Recognize Face\n4: Exit\nChoose an option: ")
        if choice == "1":
            user_id = input("Enter user ID for registration: ")
            collect_training_data(user_id)
        elif choice == "2":
            train_face_recognizer()
        elif choice == "3":
            print(f"Last recognized user: {last_user_id}" if last_user_id else "No user recognized yet.")
            recognize_user()
        elif choice == "4":
            break
        else:
            print("Invalid choice. Try again.")