# https://docs.microsoft.com/en-us/azure/cognitive-services/face/quickstarts/client-libraries?tabs=visual-studio&pivots=programming-language-python
import argparse
from concurrent import futures
import os, io
import re
import sys
import tempfile

from azure.cognitiveservices.vision.face import FaceClient
from msrest.authentication import CognitiveServicesCredentials
from azure.cognitiveservices.vision.face.models import TrainingStatusType, Person


import tqdm
import logging
import json
import glob
import uuid
import pandas as pd
from PIL import Image
import time

'''
Authenticate for Azure
Authenticates your credentials and creates a client.
'''
SUBSCRIPTION_KEY = ""
ENDPOINT = ""
face_client = FaceClient(ENDPOINT, CognitiveServicesCredentials(SUBSCRIPTION_KEY))



class NumpyEncoder(json.JSONEncoder):
    """ Custom encoder for numpy data types """
    def default(self, obj):
        if isinstance(obj, (np.int_, np.intc, np.intp, np.int8,
                            np.int16, np.int32, np.int64, np.uint8,
                            np.uint16, np.uint32, np.uint64)):
            return int(obj)
        elif isinstance(obj, (np.float_, np.float16, np.float32, np.float64)):
            return float(obj)
        elif isinstance(obj, (np.complex_, np.complex64, np.complex128)):
            return {'real': obj.real, 'imag': obj.imag}
        elif isinstance(obj, (np.ndarray,)):
            return obj.tolist()
        elif isinstance(obj, (np.bool_)):
            return bool(obj)
        elif isinstance(obj, (np.void)):
            return None
        return json.JSONEncoder.default(self, obj)




#################
# IDENTIFICATION

df = pd.read_csv("identification_individual.csv")
data = []






for index, row in df.iterrows():
    '''
    Create the PersonGroup
    '''
    # Create empty Person Group. Person Group ID must be lower case, alphanumeric, and/or with '-', '_'.
    print('Person group:', index)
    PERSON_GROUP_ID = index
    pg = face_client.person_group.create(person_group_id=PERSON_GROUP_ID, name=index)
    gallery = []
    images = ['image_1', 'image_2', 'image_3', 'image_4', 'image_5', 'image_6', 'image_7', 'image_8', 'image_9']
    for g in images:
        id = face_client.person_group_person.create(PERSON_GROUP_ID, row[g])
        i = open('identification/'+row[g], 'r+b')
        try:
            pgp = face_client.person_group_person.add_face_from_stream(PERSON_GROUP_ID, id.person_id, i)
        except:
            try:
                # sometimes there are two faces in an image!
                # so make the box the middle 1/9 of the image
                im = Image.open('identification/'+row[g])
                im_w,im_h = im.size
                w,h = int(im_w/3), int(im_h/3)
                left, top = w, h
                # first delete old
                i = open('identification/'+row[g], 'r+b')
                pgp = face_client.person_group_person.add_face_from_stream(PERSON_GROUP_ID, id.person_id, i, target_face=[left,top,w,h])
            except:
                continue
        gallery += [{'image_i': g, 'name': row[g], 'id': id.person_id}]
    '''
    Train PersonGroup
    '''
    print()
    print('Training the person group...')
    # Train the person group
    face_client.person_group.train(PERSON_GROUP_ID)
    while (True):
        training_status = face_client.person_group.get_training_status(PERSON_GROUP_ID)
        print("Training status: {}.".format(training_status.status))
        print()
        if (training_status.status is TrainingStatusType.succeeded):
            break
        elif (training_status.status is TrainingStatusType.failed):
            face_client.person_group.delete(person_group_id=PERSON_GROUP_ID)
            sys.exit('Training the person group has failed.')
        time.sleep(1)
    '''
    Identify a face against a defined PersonGroup
    '''
    # Group image for testing against
    image = open('identification/'+row['target_image'], 'r+b')
    # Detect faces
    face_ids = []
    # We use detection model 3 to get better performance.
    faces = face_client.face.detect_with_stream(image)
    if len(faces):
        for face in faces:
            face_ids.append(face.face_id)
            # identify faces
        response = face_client.face.identify(face_ids, PERSON_GROUP_ID, recognition_model='recognition_04')
        results = []
        if not response:
            continue
        for person in response:
            if len(person.candidates):
                candidateId = person.candidates[0].person_id
                conf = person.candidates[0].confidence
                for g_dict in gallery:
                    if g_dict['id'] == candidateId:
                        # see if it is correct
                        correct = row['correct']
                        answer = ''
                        for k,v in row.to_dict().items():
                            if v == g_dict['name']:
                                answer = k
                        is_correct = answer == correct
                        results += [{'candidate_matched':g_dict['name'], 'confidence':conf, 'is_correct':is_correct}]
    else:
        response = []
        restuls = []
    datum = {
    'index': index,
    'row': row.to_dict(),
    'response': [x.as_dict() for x in response],
    'results': results
    }
    data += [datum]
    '''
    Delete the PersonGroupPersons and PersonGroup
    '''
    for g_dict in gallery:
        face_client.person_group_person.delete(PERSON_GROUP_ID,g_dict['id'])
    face_client.person_group.delete(index)


azure_identification = {
    'task': "identification",
    'service': "azure",
    'answers': data
    }


with open('azure_identification.json', 'w') as f:
    json.dump(azure_identification, f, cls=NumpyEncoder)

iden_df = pd.json_normalize(data['answers'], sep='_')
iden_df['candidate_matched'] = iden_df.results.apply(lambda x: x[0]['candidate_matched'] if bool(x) else '')
iden_df['confidence'] = iden_df.results.apply(lambda x: x[0]['confidence'] if bool(x) else -1)
iden_df['answered_correctly'] = iden_df.results.apply(lambda x: x[0]['is_correct'] if bool(x) else False)
iden_df.to_csv("azure_identification_answers.csv", index=False)



#################
# VERIFICATION


df = pd.read_csv("verification.csv")

data = []



for index, row in df.iterrows():
    print(index)
    sourceFile = "verification/{}".format(row['target_image'])
    targetFile = "verification/{}".format(row['other_image'])
    imageSource=open(sourceFile,'r+b')
    imageTarget=open(targetFile,'r+b')
    # Detect face(s) from source image 1, returns a list[DetectedFaces]
    # We use detection model 3 to get better performance.
    detected_faces = face_client.face.detect_with_stream(imageSource, detection_model='detection_03')
    # Add the returned face's face ID
    source_image_id = detected_faces[0].face_id
    # Detect faces from target image url list, returns a list[DetectedFaces]
    detected_faces = face_client.face.detect_with_stream(imageTarget, detection_model='detection_03')
    # Add the returned face's face ID
    detected_faces_id = detected_faces[0].face_id
    # Verify
    verify_result = face_client.face.verify_face_to_face(source_image_id, detected_faces_id)
    info = {}
    info['question_id'] = row['question_id']
    info['correct_answer'] = row['correct_answer']
    info['response'] = verify_result.as_dict()
    info['is_correct'] = bool(row['correct_answer']) == verify_result.is_identical
    data += [info]

azure_verification = {
    'task': "verification",
    'service': "azure",
    'answers': data
    }


with open('azure_verification.json', 'w') as f:
    json.dump(azure_verification, f, cls=NumpyEncoder)

ver_df = pd.json_normalize(data, sep='_').merge(df)
ver_df = ver_df.rename(columns = {'is_correct':'answered_correctly'}, inplace = True)
ver_df.to_csv("azure_verification_answers.csv", index=False)
