
import argparse
import pickle
from collections import Counter
from pathlib import Path
import face_recognition
from PIL import Image, ImageDraw, ImageEnhance, ExifTags
import os
from io import BytesIO
from django.conf import settings
import uuid
import cv2
from scipy.spatial import distance as dist
from peoples.models import peopleEncodings

DEFAULT_ENCODINGS_PATH = Path("output/encodings.pkl")
BOUNDING_BOX_COLOR = "blue"
TEXT_COLOR = "white"

dirname = os.getcwd()
attendance_folder = os.path.join(dirname, 'img', 'attendance')

def rotate_image2(image_path):
    # Load the image
    image = cv2.imread(image_path)

    # Open the image using PIL
    pil_image = Image.open(image_path)

    # Get the EXIF orientation tag if available
    try:
        exif = pil_image._getexif()
    except AttributeError:
        exif = None

    # Determine the angle to rotate based on the orientation
    angle = 0
    if exif:
        orientation = exif.get(0x0112)
        if orientation == 3:
            angle = 180
        elif orientation == 6:
            angle = 270
        elif orientation == 8:
            angle = 90

    # Rotate the image
    if angle != 0:
        # Get image dimensions
        height, width = image.shape[:2]
        # Calculate the rotation matrix
        rotation_matrix = cv2.getRotationMatrix2D((width / 2, height / 2), angle, 1)
        # Apply the rotation
        image = cv2.warpAffine(image, rotation_matrix, (width, height))

    # Save the rotated image
    cv2.imwrite("rotated_image.jpg", image)

# Example usage




#augmentasi
def enhance_image(image_path):
    img = Image.open(image_path)
    # Rotate
    rotated_image = img.rotate(10)
    # Pergeseran horizontal dan vertikal
    shifted_image = img.transform(img.size, Image.AFFINE, (1, 0, 50, 0, 1, 50))
    # Zoom
    zoom_factor = 1.2
    zoomed_image = img.resize((int(img.size[0] * zoom_factor), int(img.size[1] * zoom_factor)))
    # Flip horizontal
    flipped_image = img.transpose(Image.FLIP_LEFT_RIGHT)
    # Brightness enhancement
    brightness_factor = 1.5
    enhanced_brightness = ImageEnhance.Brightness(img).enhance(brightness_factor)
    #  Contrast enhancement
    contrast_factor = 1.5
    enhanced_contrast = ImageEnhance.Contrast(img).enhance(contrast_factor)
    augmented_images = [rotated_image, shifted_image, zoomed_image, flipped_image, enhanced_brightness, enhanced_contrast]
    return augmented_images


def enhance_image_brightness(image_path):
    img = Image.open(image_path)
    enhancer = ImageEnhance.Sharpness(img)
    enhanced_contras = enhancer.enhance(2)  
    enhancer = ImageEnhance.Color(enhanced_contras)
    enhanced_img = enhancer.enhance(0)  
    return enhanced_img

def encode_known_face(image_location: str, people_id) :
    model =  "hog"

    encodings_location = DEFAULT_ENCODINGS_PATH
    with encodings_location.open(mode="rb") as f:
        loaded_encodings = pickle.load(f)
    augmented_images = enhance_image(image_location)
    
    names =loaded_encodings["names"]
    encodings =loaded_encodings["encodings"]
    for augmented_img in augmented_images:
        augmented_img.save("temp_enhanced_image.jpg")
        image = face_recognition.load_image_file("temp_enhanced_image.jpg")
        face_locations = face_recognition.face_locations(image, model=model)
        face_encodings = face_recognition.face_encodings(image, face_locations)
        for encoding in face_encodings:
            names.append(people_id)
            encodings.append(encoding)

            
    name_encodings = {"names": names, "encodings": encodings}
    with encodings_location.open(mode="wb") as f:
        pickle.dump(name_encodings, f)

    return True
def encode_known_faces(model: str = "hog", encodings_location: Path = DEFAULT_ENCODINGS_PATH) -> None:
    names = []
    encodings = []
    for filepath in Path("training").glob("*/*"):
        name = filepath.parent.name
        augmented_images = enhance_image(filepath)
        for augmented_img in augmented_images:
            augmented_img.save("temp_enhanced_image.jpg")
            image = face_recognition.load_image_file("temp_enhanced_image.jpg")
            face_locations = face_recognition.face_locations(image, model=model)
            face_encodings = face_recognition.face_encodings(image, face_locations)
            for encoding in face_encodings:
                names.append(name)
                encodings.append(encoding)
    name_encodings = {"names": names, "encodings": encodings}
    with encodings_location.open(mode="wb") as f:
        pickle.dump(name_encodings, f)

def validate_is_image_too_dark(input_image, people_id):
    file_name, file_extension = os.path.splitext(input_image.name)
    temp_image_path = "temp_"+str(people_id)+file_extension
    img = Image.open(temp_image_path)
    grayscale_img = img.convert("L")
    brightness = sum(grayscale_img.getdata()) / (grayscale_img.size[0] * grayscale_img.size[1])
    if brightness < 50:
        return False
    return True

def validate_face(input_image,people_id): 
    model =  "hog"
    file_name, file_extension = os.path.splitext(input_image.name)
    temp_image_path = "temp_"+str(people_id)+file_extension
    image = face_recognition.load_image_file(temp_image_path)
    face_location = face_recognition.face_locations(image, model=model)
    face_landmark = face_recognition.face_landmarks(image)

    return (face_location and face_landmark) 

def validate_only_one_face(input_image,people_id) :
    file_name, file_extension = os.path.splitext(input_image.name)
    temp_image_path = "temp_"+str(people_id)+file_extension
    model =  "hog"
    image = face_recognition.load_image_file(temp_image_path)
    face_location = face_recognition.face_locations(image, model=model)
    if len(face_location) > 1 :
        return False
    
    return face_location

def validate_center_face(input_image, people_id):
    model =  "hog"
    file_name, file_extension = os.path.splitext(input_image.name)
    temp_image_path = "temp_"+str(people_id)+file_extension
    loaded_image = face_recognition.load_image_file(temp_image_path)
    face_location = face_recognition.face_locations(loaded_image, model=model)

    top, right, bottom, left = face_location[0]
    width = right - left
    height = bottom - top
    center_x = left + (width // 2)
    center_y = top + (height // 2)
    image_width, image_height = loaded_image.shape[1], loaded_image.shape[0]
    if center_x >= image_width * 0.4 and center_x <= image_width * 0.6 and \
            center_y >= image_height * 0.4 and center_y <= image_height * 0.6:
        return False
    
    return True

def validate_blur(input_image, people_id):
    file_name, file_extension = os.path.splitext(input_image.name)
    temp_image_path = "temp_"+str(people_id)+file_extension
    model =  "hog"
    laplacian_thresold = 85
    loaded_image = face_recognition.load_image_file(temp_image_path)
    face_location = face_recognition.face_locations(loaded_image, model=model)
    top, right, bottom, left = face_location[0]
    grayscale_image = cv2.cvtColor(loaded_image, cv2.COLOR_BGR2GRAY)
    face_area = grayscale_image[top:bottom, left:right]
    laplacian = cv2.Laplacian(face_area, cv2.CV_64F).var()
    if laplacian < laplacian_thresold:
        return True 
    return False

def validate_eye_close(input_image, people_id) :
    file_name, file_extension = os.path.splitext(input_image.name)
    temp_image_path = "temp_"+str(people_id)+file_extension
    model =  "hog"
    image = face_recognition.load_image_file(temp_image_path)
    face_location = face_recognition.face_locations(image, model=model)
    face_landmarks_list = face_recognition.face_landmarks(image)
    for face_landmark in face_landmarks_list:
        left_eye = face_landmark['left_eye']
        right_eye = face_landmark['right_eye']
        ear_left = get_ear(left_eye)
        ear_right = get_ear(right_eye)
        closed = ear_left < 0.2 and ear_right < 0.2
        return closed

def get_ear(eye):
	A = dist.euclidean(eye[1], eye[5])
	B = dist.euclidean(eye[2], eye[4])
	C = dist.euclidean(eye[0], eye[3])
	ear = (A + B) / (2.0 * C)
	return ear

def add_user(image_location: str, person) :
    model =  "hog"
    file_name, file_extension = os.path.splitext(image_location.name)
    temp_image_path = "temp_"+str(person.id)+file_extension
    encodings = []
    names =[]
    model =  "hog"
    
    augmented_images = enhance_image(temp_image_path)
    for augmented_img in augmented_images:
        augmented_img.save(temp_image_path)  
        image = face_recognition.load_image_file(temp_image_path)
        face_locations = face_recognition.face_locations(image, model=model)
        face_encodings = face_recognition.face_encodings(image, face_locations)

        file_name, file_extension = os.path.splitext(image_location.name)
        pil_image = Image.fromarray(image)
        tmp_aug = "training/"+str(person.id)+"/"+str(uuid.uuid4())+file_extension
        pil_image.save(tmp_aug)
        for encoding in face_encodings:
            peopleEncodings.objects.create(person=person, encoding=pickle.dumps(encoding))
            
    return True

def rotate_to_top (image_path, people_id): 
    file_name, file_extension = os.path.splitext(image_path.name)
    enhanced_image = enhance_image_brightness(image_path)
    temp_image_path = "temp_"+str(people_id)+file_extension
    enhanced_image.save(temp_image_path)

    validate = validate_face(image_path,people_id)
    print("Rotate pertama")
    if not validate :
        print("Rotate kedua")
        rotate_image(temp_image_path)
        validate = validate_face(image_path,people_id)
        if not validate :
            print("Rotate ketiga")
            rotate_image(temp_image_path)
            validate = validate_face(image_path,people_id)
            if not validate :
                print("Rotate keempat")
                rotate_image(temp_image_path)
                validate = validate_face(image_path,people_id)

    if not validate :
        return False

def recognize_faces(
    image_location: str,
    people_id,
    tolerance: int =0.2
) -> None:
    model =  "hog"
    file_name, file_extension = os.path.splitext(image_location.name)
    temp_image_path = "temp_"+str(people_id)+file_extension
    
    input_image = face_recognition.load_image_file(temp_image_path)
    input_face_locations = face_recognition.face_locations(
        input_image, model=model
    )

    result = compare_face_by_id(input_image, input_face_locations, people_id,tolerance)
    if result :
        
        return result
   
    return False

def _recognize_face(unknown_encoding, loaded_encodings, tolerance):

    boolean_matches = face_recognition.compare_faces(
        loaded_encodings["encodings"], unknown_encoding, tolerance=tolerance
    )
    votes = Counter(
        name
        for match, name in zip(boolean_matches, loaded_encodings["names"])
        if match
    )
    if votes:
        return votes.most_common(1)[0][0]
    
def compare_face_by_id(input_image, input_face_locations, people_id, tolerance):
    # Ambil semua encoding dari database berdasarkan `people_id`
    encodings_queryset = peopleEncodings.objects.filter(person_id=people_id)

    known_encodings = []
    known_names = []

    for entry in encodings_queryset:
        if entry.encoding:  # Pastikan encoding tidak kosong
            try:
                encoding = pickle.loads(entry.encoding)  # Dekode dari binary ke list
                known_encodings.append(encoding)
                known_names.append(str(entry.person_id))  # Simpan ID orangnya
            except pickle.UnpicklingError:
                print(f"❌ Error: Encoding tidak valid untuk ID {entry.id}")
    
    # Jika tidak ada encoding yang ditemukan
    if not known_encodings:
        print("⚠️ Tidak ada encoding yang ditemukan di database!")
        return False
    
    # Ekstrak encoding dari gambar input
    input_face_encodings = face_recognition.face_encodings(input_image, input_face_locations)

    # Persiapkan encoding untuk fungsi `_recognize_face`
    loaded_encodings = {"names": known_names, "encodings": known_encodings}

    # Cek setiap wajah yang terdeteksi dalam input
    for unknown_encoding in input_face_encodings:
        name = _recognize_face(unknown_encoding, loaded_encodings, tolerance)
        print(name)
        if name:
            encoded_face = pickle.dumps(unknown_encoding)  # Simpan dalam format binary
            new_encoding = peopleEncodings(person_id=people_id, encoding=encoded_face)
            new_encoding.save()
            return name  # Kembalikan ID orang yang cocok

    return False  # Tidak ada wajah yang cocok

def find_face(input_image, input_face_locations, people_id, tolerance) :
    encodings_location = DEFAULT_ENCODINGS_PATH
    with encodings_location.open(mode="rb") as f:
        loaded_encodings = pickle.load(f)
    input_face_encodings = face_recognition.face_encodings(
        input_image, input_face_locations
    )
    for bounding_box, unknown_encoding in zip(
        input_face_locations, input_face_encodings
    ):
        name = _recognize_face(unknown_encoding, loaded_encodings, tolerance)
        if not name:
            name = False
    return name
    
def rotate_image(image):
    image = Image.open(image)
    rotated_image = image.rotate(90, expand=True)
    buffered = BytesIO()
    rotated_image.save(image.filename)
    rotated_image.save(buffered, format="JPEG")

    return rotated_image

def _display_face(draw, bounding_box, name, input_image):
    top, right, bottom, left = bounding_box
    face_image = input_image[top:bottom, left:right]
    cropped_image = Image.fromarray(face_image)
    cropped_image.save("asdasdas2.jpg")
    draw.rectangle(((left, top), (right, bottom)), outline=BOUNDING_BOX_COLOR)
    text_left, text_top, text_right, text_bottom = draw.textbbox(
        (left, bottom), name
    )
    draw.rectangle(
        ((text_left, text_top), (text_right, text_bottom)),
        fill="blue",
        outline="blue",
    )
    draw.text(
        (text_left, text_top),
        name,
        fill="white",
    )


def validate(model: str = "hog"):
    for filepath in Path("validation").rglob("*"):
        if filepath.is_file():
            recognize_faces(
                image_location=str(filepath.absolute()), model=model
            )
