import os
import cv2
from deepface.detectors import FaceDetector


def build_model():
    detector = {}
    detector["face_detector"] = build_cascade("haarcascade")
    detector["eye_detector"] = build_cascade("haarcascade_eye")
    return detector


def build_cascade(model_name="haarcascade"):
    opencv_path = get_opencv_path()
    if model_name == "haarcascade":
        face_detector_path = opencv_path + "haarcascade_frontalface_default.xml"
        if os.path.isfile(face_detector_path) != True:
            raise ValueError(
                "Confirm that opencv is installed on your environment! Expected path ",
                face_detector_path,
                " violated.",
            )
        detector = cv2.CascadeClassifier(face_detector_path)

    elif model_name == "haarcascade_eye":
        eye_detector_path = opencv_path + "haarcascade_eye.xml"
        if os.path.isfile(eye_detector_path) != True:
            raise ValueError(
                "Confirm that opencv is installed on your environment! Expected path ",
                eye_detector_path,
                " violated.",
            )
        detector = cv2.CascadeClassifier(eye_detector_path)

    else:
        raise ValueError(f"unimplemented model_name for build_cascade - {model_name}")

    return detector


def detect_face(detector, img, align=True):
    resp = []

    detected_face = None
    img_region = [0, 0, img.shape[1], img.shape[0]]

    faces = []
    try:
        # faces = detector["face_detector"].detectMultiScale(img, 1.3, 5)

        # note that, by design, opencv's haarcascade scores are >0 but not capped at 1
        faces, _, scores = detector["face_detector"].detectMultiScale3(
            img, 1.1, 10, outputRejectLevels=True
        )
    except:
        pass

    if len(faces) > 0:

        for (x, y, w, h), confidence in zip(faces, scores):
            detected_face = img[int(y) : int(y + h), int(x) : int(x + w)]

            if align:
                detected_face = align_face(detector["eye_detector"], detected_face)

            img_region = [x, y, w, h]

            resp.append((detected_face, img_region, confidence))

    return resp


def align_face(eye_detector, img):

    detected_face_gray = cv2.cvtColor(
        img, cv2.COLOR_BGR2GRAY
    )  # eye detector expects gray scale image

    # eyes = eye_detector.detectMultiScale(detected_face_gray, 1.3, 5)
    eyes = eye_detector.detectMultiScale(detected_face_gray, 1.1, 10)

    # ----------------------------------------------------------------

    # opencv eye detectin module is not strong. it might find more than 2 eyes!
    # besides, it returns eyes with different order in each call (issue 435)
    # this is an important issue because opencv is the default detector and ssd also uses this
    # find the largest 2 eye. Thanks to @thelostpeace

    eyes = sorted(eyes, key=lambda v: abs((v[0] - v[2]) * (v[1] - v[3])), reverse=True)

    # ----------------------------------------------------------------

    if len(eyes) >= 2:

        # decide left and right eye

        eye_1 = eyes[0]
        eye_2 = eyes[1]

        if eye_1[0] < eye_2[0]:
            left_eye = eye_1
            right_eye = eye_2
        else:
            left_eye = eye_2
            right_eye = eye_1

        # -----------------------
        # find center of eyes
        left_eye = (int(left_eye[0] + (left_eye[2] / 2)), int(left_eye[1] + (left_eye[3] / 2)))
        right_eye = (int(right_eye[0] + (right_eye[2] / 2)), int(right_eye[1] + (right_eye[3] / 2)))
        img = FaceDetector.alignment_procedure(img, left_eye, right_eye)
    return img  # return img anyway


def get_opencv_path():
    opencv_home = cv2.__file__
    folders = opencv_home.split(os.path.sep)[0:-1]

    path = folders[0]
    for folder in folders[1:]:
        path = path + "/" + folder

    return path + "/data/"
