import cv2
import dlib
import numpy as np

from matplotlib import pyplot as plt

#from scipy.cluster.hierarchy import dendrogram, linkage, fcluster, fclusterdata, leaders

import scipy as sp
import time
import os
import shutil as sh
import glob
import openface

#propia
import utils
import vector_face as vecFace 
import clustering 
import reports


def resultats(trueP,trueN,falseP,falseN):
    resultats={}
    resultats['precision'] = trueP/float(trueP+falseP)
    resultats['recall'] = trueP/float(trueP+falseN)
    

    down = float(resultats['precision']+resultats['recall'])
    
    if down <= 0.0 :
        resultats['f1'] = 0.0
    else:
        resultats['f1'] = 2*((resultats['precision']*resultats['recall'])/down)
    
    return resultats


# Manipular nom del fitxer retellat perque doni sols el nom de la imatge original
def nameBaseFileClip(finalName, divisor = '--'):
        
    name = finalName.split('/')[-1]
    baseName = name.split('.')[0]
    refName = baseName.split(divisor)
    refFile = refName[0]
    #refFile = divisor.join(refName[:len(refName)-1])
        
    return refFile
    
# Manipular nom del fitxer original per donar sols el nom de la imatge original
def nameBaseFile(original):
    
    name = original.split('/')[-1]
    baseName = name.split('.')[0]
    
    return baseName


# Generar el vector mascara per fer el test
def generateVecTest(n, div = 5):
    
    frac = int(n/div)
    vecTest = []
    inicial = 0
    
    for x in range(0,div):
        vecTest.append([0 if valor >= (x*frac) and valor < ((x+1)*frac) else 1 for valor in range(0,n) ])
    
    return vecTest

# generar el vector per la grafica de imatge o no imatge
# sortida resultats segons comparativa amb imatges o fitxer
def generateVecTestGrafic(images,images_clip,locFaces):
    nf = len(locFaces['nameFile'].keys())
    ni = len(images)
    vecImg = []
    vecFile = []
    
    false_negative = 0
    false_positive = 0
    
    true_positive = 0
    true_negative = 0
    
    imgClip = [nameBaseFileClip(x) for x in sorted(images_clip)]
    imgOrg = [nameBaseFile(x) for x in sorted(images)]
    
    for img in imgOrg:
        if  img in imgClip:
            vecImg.append(1)
        else:
            vecImg.append(0)
            
        if img in locFaces['nameFile'].keys():
            vecFile.append(len([x for x in range(0,len(locFaces['nameFile'][img])) if x < 1 ]))
        
        # Per controlar els resultats i valorar
        if vecImg[-1] == vecFile[-1]:
            if vecImg[-1] == 0:
                true_negative += 1
            else:
                true_positive += 1
        else:
            if vecImg[-1] > vecFile[-1]:
                false_positive += 1
            else:
                false_negative += 1
    
    outDict = {}
    outDict['images'] = vecImg
    outDict['file'] = vecFile
    outDict['true_positives'] = true_positive
    outDict['true_negatives'] = true_negative
    outDict['false_positives'] = false_positive
    outDict['false_negatives'] = false_negative
    outDict['total_file'] = nf 
    outDict['total_images'] = ni
    
    return outDict


# generar el vector per la grafica de imatge o no imatge
# sortida resultats segons comparativa amb imatges o fitxer
def generateCrossVecTestGrafic(images,saveData,transfi,locFaces,repFace):
#def generateCrossVecTestGrafic(locFaces):
    nf = len(locFaces['nameFile'].keys())
    ni = len(images)
    
    false_negative = 0
    false_positive = 0
    
    true_positive = 0
    true_negative = 0    
    
    imgOrg = [nameBaseFile(x) for x in sorted(images)]

    
    #Dades del fitxer
    vecTempFile = [locFaces['nameFile'][x]  for x in sorted([y for y in locFaces['nameFile'].keys()])]
    nVecs = max([len(x) for x in vecTempFile])
    vecFile = []
    for i in range(0,nVecs):
        vecFile.append([])
    
    for vec in vecTempFile:
        for i in range(0,nVecs):
            if len(vec) > i:
                vecFile[i].append(int(vec[i]))
            else:
                vecFile[i].append(-1)
    
    vecTemp = [saveData['nameFile'][img] if img in saveData['nameFile'].keys() else [] for img in sorted([x.split('/')[-1].split('.')[0] for x in images])]
    
    # Dades del proces   
    #vecTemp = [saveData['nameFile'][img] for img in sorted([x for x in saveData['nameFile'].keys()])]
    

    nVecs = max([len(x) for x in vecTemp])
    
    vecTempImg = []
    for vecT in vecTemp:
        vecTempImg.append([transfi[y] if y in transfi.keys() else -1 for y in vecT])
            
        
    vecImg = []
    for i in range(0,nVecs):
        vecImg.append([])
    
    for vec in vecTempImg:
        for i in range(0,nVecs):
            if len(vec) > i:
                vecImg[i].append(int(vec[i]))
            else:
                vecImg[i].append(-1)
    
    #print str(vecImg)
    # Per calcular True false_negatives
    vecNamesFilesFaces = [len(locFaces['nameFile'][x]) if len(locFaces['nameFile'][x]) > 0 else 0 for x in sorted([y for y in imgOrg]) ]
    
    for pos in range(0,len(vecImg[0])):
    #for pos in range(0,max([len(x) for x in vecTempFile])):
        if vecNamesFilesFaces[pos] == 0:
            if vecImg[0][pos] == -1:
                true_negative += 1
            else:
                false_negative += 1
        else:
            if vecImg[0][pos] == -1:
                false_negative += 1
            else:
                tmpF = []
                tmpI = []
                
                for i in range(0,len(vecImg)):
                    if i < len(vecFile):
                        tmpF.append(vecFile[i][pos])
                    if vecImg[i][pos] != -1:
                        tmpI.append(vecImg[i][pos])
                for vec in tmpI:
                    if vec in tmpF:
                        true_positive += 1
                    else:
                        false_positive += 1
                        #print 'Fals positiu Imatge ' + str(vec) + ' Fitxer  ' + str(tmpF) 
    
    
   # i = 0
   # for vecF in vecFile:
   #     if len(vecF) == 0 and len(vecF) == len(vecImg[i]):
   #             true_negative += 1
   #     else:
   #         if len(vecImg[i]) < len(vecF):
   #             false_negative += len(vecF)-len(vecImg[i])
   #         else:
   #             if len(vecImg[i]) > len(vecF):
   #                 false_positive += 1
   #                 
   #     for vecI in [x for x in vecImg[i] if x!=-1]:
   #         if vecI in vecF:
   #             true_positive += 1
   #         else:
   #             false_positive += 1
   #     i += 1
        
   # for des in saveData['descarts']:
   #     shName = nameBaseFileClip(des)
   #     if shName in locFaces['nameFile'].keys():
   #         if len(locFaces['nameFile'][shName]) < 1:
   #             true_negative += 1
   #         else:
   #             false_negative += 1
    
    outDict = {}
    outDict['images'] = vecImg
    outDict['file'] = vecFile
    outDict['true_positives'] = true_positive
    outDict['true_negatives'] = true_negative
    outDict['false_positives'] = false_positive
    outDict['false_negatives'] = false_negative
    outDict['total_file'] = nf 
    outDict['total_images'] = ni
    
    return outDict
    

# Vector de totes les imatges Originals ordenat i preperades per cross-Validation
# es crean div paquets imatges dins de una list amb diferente agrupacio 
# Sortida diccionari primera part es el resultat (1-(n/div)) segona part n/div
def vecImagesTest(images, div = 5):
    n = len(images)
    vecTest = generateVecTest(n,div)
    salida = {}
    salida['first'] = []
    salida['second'] = []
    
    for vec in vecTest:
        salida['first'].append([images[i] for i in range(0,n) if vec[i] != 0])
        salida['second'].append([images[i] for i in range(0,n) if vec[i] != 1])
    
        
    return salida

# convertir '[22,33,44]' e una lista 
def convertStrList(string, values=('-1','0','1','2','3','4','5','6','7','8','9'), div = ','):
    salida = ''
    
    for s in string:
        if s in values or s ==  div:
            salida += s

    if len(salida) > 0:
        return salida.split(',')
    else:
        return []

# Lectura dels fitxers de referencia per imatge
def locFaces(locFaces):
    outDict = {}
    
    idsFileDict = {}
    filesIdsDict = {}
    
    idsFileDict['sinId'] =[]

    if os.path.exists(locFaces):
        
        # Obtenir per fitxer
        f = open(locFaces,'r')
        
        linea = f.readline()
        while linea != "":
            nameFile , ids , pos = linea.split(';')            
            nameBase = nameFile.split('.')[0]
                        
            persones = convertStrList(ids)
            
            if not nameBase in filesIdsDict.keys():
                filesIdsDict[nameBase] =[]
            
            if len(persones) > 0:
                filesIdsDict[nameBase].extend(persones)
                for persona in persones:
                    if not persona in idsFileDict.keys():
                        idsFileDict[persona] = []
                    idsFileDict[persona].append(nameBase)
            else:
                idsFileDict['sinId'].append(nameBase)
            
            linea = f.readline()
    
        f.close
    
    outDict['ids'] = idsFileDict
    outDict['nameFile'] = filesIdsDict 
    
    return outDict

# Lectura dels fitxers de referencia per rep
def repFaces(inputDir):
    idsRepsDict = {}
    
    if os.path.exists(inputDir):
        
        repFaces = glob.glob(inputDir+'/*.csv')    
        
        for repFace in repFaces:
        # Obtenir per referencia
            f = open(repFace,'r')
            
            persona = repFace.split('/')[-1].split('.')[0]

            linea = f.readline()
            while linea != "":
                #print 'Document --> ' + str(persona) + ' Linea --> ' + linea[-10:]
                repFile = [float(x) for x in linea[:-2].split(';')]
                if not persona in idsRepsDict.keys():
                    idsRepsDict[persona] = []
                idsRepsDict[persona].append(repFile)
                
                linea = f.readline()
        
            f.close

     
    return idsRepsDict

# Organitzar les fotos Test etiquetades en carpetes
# Entrada llista de path de imatges i fitxer de nom amb ids usuaris amb files dict
def classNameFileFaces(images,reps,locFaces,repFaces, outDir):
    i = 0
    for image in images:
        nameBase = image.split('/')[-1]
        nameClip = nameBaseFileClip(image)
        
        print 'Imatge  ' + nameBase
        
        if nameClip in locFaces['nameFile'].keys():
            ids = locFaces['nameFile'][nameClip]
            
            id_find = -1
            
            if len(ids) > 0:
                #print 'imatge amb ' + str(len(ids)) + ' cares' 
                for persona in ids:
                    destino = os.path.join(outDir,persona)
                    id_find = -1
                    if not os.path.exists(destino):
                        os.mkdir(destino)
                    if persona in repFaces.keys():
                        nPersones = len(repFaces[persona])
                        m = 0
                        while m < nPersones and id_find == -1:
                            if len(repFaces[persona][m]) > 0: #Per si esta vuida la referencia de id
                                if vecFace.compareVectors(reps[i],repFaces[persona][m]) < 0.05:
                                    id_find = persona
                                    print ' ------> Esta el id ' + str(persona)
                                    sh.copy(image,os.path.join(destino,nameBase))
                            m += 1
                        
                    if id_find == -1:
                        print 'Error al obtenir vector imatge ' + nameBase
                        destino = os.path.join(outDir,'errors')
                        if not os.path.exists(destino):
                            os.mkdir(destino)
                        sh.copy(image,os.path.join(destino,nameBase))
                        
            else:
                print 'imatge vuida'
                destino = os.path.join(outDir,'sinId')
                if not os.path.exists(destino):
                    os.mkdir(destino)
                
                sh.copy(image,os.path.join(destino,nameBase))
        i += 1
        
    print 'Total imatges ' + str(i)
        
        
def testViola(inputImages, inputClip, fitxer ):
    if os.path.exists(inputImages) and os.path.exists(inputClip):
        
        exts=('*.jpg','*.jpeg','*.png')
        
        loc = locFaces(fitxer)
        
        images = []
        imagesClip = []
        
        for ext in exts:    
            images.extend(glob.glob(os.path.join(inputImages,ext)))
        
        for ext in exts:    
            imagesClip.extend(glob.glob(os.path.join(inputClip,ext)))
        
        vecs = generateVecTestGrafic(images,imagesClip,loc)
        
        reports.graficaPaso(vecs['images'],vecs['file'])
        
        # Entrada (trueP,trueN,falseP,falseN)
        test = resultats(vecs['true_positives'],vecs['true_negatives'],vecs['false_positives'],vecs['false_negatives'])
        
        print 'Resultats de Viola Jones: '
        print '|-> Imatges Totals ' + str(vecs['total_images'])
        print '|-> Imatges File   ' + str(vecs['total_file'])
        print '---------------------------------------------------'
        print ' Precision --> ' + str(round(test['precision'],4)) 
        print ' Recall   --> ' + str(round(test['recall'],4)) 
        print ' F-score --> ' + str(round(test['f1'],4))
            
    else:
        print 'Directori inexistent ' + inputDir
    
# principal
def main(inputDir,fitxer,bd_dir,outputDir):
    if os.path.exists(inputDir):
        
        exts=('*.jpg','*.jpeg','*.png')
        loc = locFaces(fitxer)
        rep = repFaces(bd_dir)
        
        images = []
        
        for ext in exts:    
            images.extend(glob.glob(os.path.join(inputDir,ext)))
        
        images = sorted(images)
        
        dirAct = os.getcwd()
        
        modelDir = os.path.join(dirAct,'..','..','models')
        
        dlibModelDir = os.path.join(modelDir, 'dlib')
        openfaceModelDir = os.path.join(modelDir, 'openface')
        
        predictor = os.path.join(dlibModelDir, "shape_predictor_68_face_landmarks.dat")
        small = os.path.join(openfaceModelDir, 'nn4.small2.v1.t7')
        
        align = openface.AlignDlib(predictor)
        net = openface.TorchNeuralNet(small, int(96))
        # Sortida [images,reps,errors,repsClothes]
        valores = vecFace.extractVectors(inputDir, align, net,imgDim=int(96), aviso = True)
        
        if not os.path.exists(outputDir):
            os.mkdir(outputDir)
        
        classNameFileFaces(valores[0],valores[1],loc,rep,outputDir)
    
    else:
        print ' Directori Entrada inexistent ' + inputDir
        
        
# Guardar les imatges amb un id concret
# clusters diccionaro amb keys ['images', 'reps', 'descarts', 'desReps', 'errors']
def saveImgTest(clusters , saveTest = {} , min_value = 0.05, min_cluster_value = 2):
    i = 0
    if not 'nameFile' in saveTest.keys():
        saveTest['nameFile'] = {}
    if not 'descarts' in saveTest.keys():
        saveTest['descarts'] = []
    for reps in clusters['reps']:
        #print 'Procesant imatges ' + str(clusters['images'][i])
        persones = [x for x in saveTest.keys() if x != 'nameFile' and x != 'descarts']

        nReps = len(reps)
        if nReps < min_cluster_value: #Clusters unics descartats
            saveTest['descarts'].extend(clusters['images'][i])
        else:
            if len(persones) < 1:
                persona = '0'
            else:
                persona = '-1'
                valorsMin = []
                for persona in persones:
                    for repPersona in saveTest[persona]['csv']:
                        r = 0
                        while r < nReps: 
                            valor = vecFace.compareVectors(reps[r],repPersona)
                            if valor < min_value:
                                valorsMin.append([valor,persona])
                            r += 1
                
                if len(valorsMin) < 1:
                    persona = str(max([int(x) for x in persones])+1)
                else:
                    persona = sorted(valorsMin)[0][1]
                
            if not persona in saveTest.keys():
                saveTest[persona] = {}
                saveTest[persona]['csv'] = []
                saveTest[persona]['images'] = []
                
            saveTest[persona]['csv'].extend(reps)
            saveTest[persona]['images'].extend(clusters['images'][i])
            
            # guardar la imatge segons el seu cluster
            for image in clusters['images'][i]:
                if not nameBaseFileClip(image) in saveTest['nameFile'].keys():
                    saveTest['nameFile'][nameBaseFileClip(image)]= []
                if not persona in saveTest['nameFile'][nameBaseFileClip(image)]:
                    saveTest['nameFile'][nameBaseFileClip(image)].extend(persona)
        
        i += 1
        
        if len(clusters['descarts']):
            saveTest['descarts'].extend(clusters['descarts'])

    return saveTest

# Obtenir el id de referencia amb el test
def serchIdTest1(saveData,repFile, min_value = 0.05, metric = 'euclidian'):
    transfi = {} #Per tenir la referencia dels ids
    
    for persona in [x for x in saveData.keys() if x != 'nameFile' and x!= 'descarts']:
        
        i =  0
        ok = False
        nRep = len(saveData[persona]['csv'])
        valorMin = float('Inf')
        valorMax = float('-Inf')
        millorPer = -1
        
        while i < nRep and ok == False:

            for perFile in repFile.keys():
                nRepF, iPerF = len(repFile[perFile]), 0
                
                while iPerF < nRepF and ok == False:
                    if metric == 'euclidian':
                        valor = vecFace.compareVectors(repFile[perFile][iPerF],saveData[persona]['csv'][i])
                        if valor < min_value:
                            print 'Entra -->>> Valor ' + str(valor) + ' Persona File ' + str(perFile)
                            ok = True
                            if valorMin > valor:
                                valorMin = valor
                                millorPer = perFile
                    if metric == 'pearson':
                        valor = utils.pearson(repFile[perFile][iPerF],saveData[persona]['csv'][i])
                        if valor > min_value:
                            print 'Entra --->>>>>>> Valor ' + str(valor) + ' Persona File ' + str(perFile)
                            ok = True
                            if valorMax < valor:
                                valorMax = valor
                                millorPer = perFile
                    
                    iPerF += 1
            i += 1
        if metric == 'euclidian':
            if valorMin != float('Inf'):
                print 'Persona de saveDir ' + str(persona) + ' Persona File ' + str(millorPer)
                transfi[persona] = millorPer
        if metric == 'pearson':
            if valorMax != float('-Inf'):
                print 'Persona de saveDir ' + str(persona) + ' Persona File ' + str(millorPer)
                transfi[persona] = millorPer
        
    print 'Diccionario transi ---------------' 
    print str(transfi)
        
    return transfi


# Obtenir el id de referencia amb el test
def serchIdTest(saveData,repFile, min_value = 0.9, metric = 'pearson'):
    transfi = {} #Per tenir la referencia dels ids

    personsFile = [x for x in repFile.keys()]
    persones = [x for x in saveData.keys() if x != 'nameFile' and x!= 'descarts']
    #print '---- Persones amb Reps --------------------------------'
    #print str(personsFile)
    #print '---- Persones dins de SaveDir -------------------------'
    #print str(persones)
    
    for persona in persones:
        valorMax = min_value
        new_person = -1
        for rep in saveData[persona]['csv']:
            for personFile in personsFile:
                for repF in repFile[personFile]:
                    valor = utils.pearson(rep,repF)
                    if valorMax < valor:
                        valorMax = valor
                        new_person = personFile
                        #print 'Persona Save ' + str(persona) + ' <-> Persona File ' + str(personFile) + '  valor >>> ' + str(valor) 
                        
        if new_person != -1:
            transfi[persona] = new_person
            #personsFile.remove(new_person) #Eliminar persona vinculada

    print str(transfi)
        
    return transfi
    

# funcio d'entrada principal pel test
# Entrada diccionari clusters amb keys [persona]['csv','images']
def testCross(saveData, inputDirImg, fileLoc, dirReps, value = 0.9, grafic = True):
    if os.path.exists(inputDirImg):
        exts=('*.jpg','*.jpeg','*.png')
        
        #  locFile['ids','nameFile'] --> []
        locFile = locFaces(fileLoc)
        # repFile[persona] --> [[rep],[rep],..]
        repFile = repFaces(dirReps)
        
        images = []
        
        transfi = serchIdTest(saveData,repFile) #Per tenir la referencia dels ids
        
        for ext in exts:    
            images.extend(glob.glob(os.path.join(inputDirImg,ext)))
            
        images = sorted(images)
        
        vecs = generateCrossVecTestGrafic(images,saveData,transfi,locFile,repFile)
        
        if grafic:
            reports.graficaPersones(vecs['images'],vecs['file'])
        
        # Entrada (trueP,trueN,falseP,falseN)
        test = resultats(vecs['true_positives'],vecs['true_negatives'],vecs['false_positives'],vecs['false_negatives'])
        
        print 'Resultats de Cross-Validation: '
        print '|-> Imatges Totals ' + str(vecs['total_images'])
        print '|-> Imatges File   ' + str(vecs['total_file'])
        print '---------------------------------------------------'
        print ' Precision --> ' + str(round(test['precision'],4)) 
        print ' Recall   --> ' + str(round(test['recall'],4)) 
        print ' F-score --> ' + str(round(test['f1'],4))
        
      
    else:
        print ' Directori Entrada inexistent ' + inputDir


def proba(fitxer):
    #  locFile['ids','nameFile'] --> []
    locFile = locFaces(fitxer)
    # repFile[persona] --> [[rep],[rep],..]
    
    vecs = generateCrossVecTestGrafic(locFile)
    

#proba('../test/set-Test/locFacesTest-Part1.txt')
#main('../test/set-Test/retalls/Part1/Alt_Profile/sc12min8','../test/set-Test/locFacesTest-Part1.txt','../BD/part1','../test/set-Test/probaClass-2')


#testViola('../fotos/test/part1','../test/set-Test/retalls/Part1/Alt_Profile/sc12min8','../test/set-Test/locFacesTest-Part1.txt')
    
