import cv2
import os
import numpy as np
import uuid
import time
import logging
from retinaface import RetinaFace
from typing import List, Tuple




def calculate_sharpness(image: np.array) -> float:
    """Calculate the sharpness of an image using Laplacian variance."""
    return cv2.Laplacian(image, cv2.CV_64F).var()

def rename_files_randomly(folder_path: str):
    """Rename all image files in the folder to random names."""
    start_time = time.time()
    for filename in os.listdir(folder_path):
        if filename.lower().endswith(('.png', '.jpg', '.jpeg')):
            extension = os.path.splitext(filename)[1]
            new_filename = str(uuid.uuid4()) + extension
            os.rename(os.path.join(folder_path, filename), os.path.join(folder_path, new_filename))
    duration = time.time() - start_time
    logging.info(f"rename_files_randomly duration: {duration} seconds")

def process_images(folder_path: str) -> List[Tuple[str, float]]:
    start_time = time.time()
    images = []
    failed_images = []
    total_files = len([f for f in os.listdir(folder_path) if f.lower().endswith(('.png', '.jpg', '.jpeg'))])
    processed_count = 0

    for filename in os.listdir(folder_path):
        if filename.lower().endswith(('.png', '.jpg', '.jpeg')):
            path = os.path.join(folder_path, filename)
            try:
                image = cv2.imread(path)
                if image is None:
                    raise ValueError("Unable to read image")

                faces = RetinaFace.detect_faces(image)

                if faces:
                    face_data = list(faces.values())[0]
                    if 'facial_area' in face_data:
                        x1, y1, x2, y2 = face_data['facial_area']
                        face_area = image[y1:y2, x1:x2]
                        sharpness = calculate_sharpness(face_area)
                        images.append((path, sharpness))
                        logging.info(f"Image: {path}, Sharpness: {sharpness}")
            except Exception as e:
                failed_images.append((path, -1))
                logging.error(f"Error processing {filename}: {e}")

            processed_count += 1
            print(f"Processed: {processed_count}/{total_files} images, Sharpness: {sharpness if faces else 'N/A'}")

    duration = time.time() - start_time
    logging.info(f"process_images duration: {duration} seconds")
    return images + failed_images

def sort_and_rename_images(images: List[Tuple[str, float]], folder_path: str):
    start_time = time.time()
    images.sort(key=lambda x: x[1], reverse=True)
    with open(log_file_path_sharpness, 'w') as log_file:
        for i, (path, sharpness) in enumerate(images, start=10001):
            extension = os.path.splitext(path)[1]  # Extracting the file extension
            new_name = os.path.join(folder_path, f"{sorting_image_prefix}_{i}{extension}")
            os.rename(path, new_name)
            log_file.write(f"{new_name}: Sharpness = {sharpness}\n")
    duration = time.time() - start_time
    logging.info(f"sort_and_rename_images duration: {duration} seconds")


# Setup paths
images_folder_path = r"G:\gender_classifier\blur_test"
log_file_path_sharpness = r"G:\gender_classifier\sharpness_logs.txt"
log_file_path = r"G:\gender_classifier\processing_log.txt"  
sorting_image_prefix="man"

os.makedirs(os.path.dirname(log_file_path_sharpness), exist_ok=True)
os.makedirs(os.path.dirname(log_file_path), exist_ok=True)
logging.basicConfig(filename=log_file_path, level=logging.INFO, 
                    format='%(asctime)s %(levelname)s:%(message)s')

def main():
    rename_files_randomly(images_folder_path)  # Rename files to random names
    images = process_images(images_folder_path)
    sort_and_rename_images(images, images_folder_path)

if __name__ == "__main__":
    main()
