#include "../const.h"
#include "AU.h"


// This functions reads from a .INI file AUs configurations
// and save them in an appropriate data structure
bool
InitAus(au*& aus)
{

int i, dir, name, face_part, status, n_block;
double points;
char filename[MAX_LENGTH_FILE];
FILE* f;

#ifdef FUNC_TRACK
PRN(("Starting InitAus\n"));
#endif

aus = (au*) calloc(AU_NUM, sizeof(au));
if (aus == NULL)
   return false;

sprintf(filename, "%s%s", INIT_FOLDER, AUS_FILE);
f = fopen(filename, "r+");
if (f == 0) {
   PRN(("can't open %s\n", filename));
   return false;
}

i = 0;
while(fscanf(f, "#AU_NAME::%d %*s %*s %d\n", &name, &face_part)) {
   aus[i] = au(name, face_part);
   if (face_part == EYES) {
      fscanf(f, "Eyes_Status::%d\n", &status);
      aus[i].eyes_status = status;
   }
   else if (face_part == LOWER || face_part == UPPER) {
       while (fscanf(f, "Block %d::%d %lf\n", &n_block, &dir, &points)) {
           aus[i].motion_points[n_block][dir] = points;
       }
   }
   i++;
   fscanf(f, "#####\n");

}

fclose(f);


#ifdef FUNC_TRACK
PRN(("Ending InitAus\n"));
#endif

return true;

}

// This function verifies if each block's movement can be 
// classified as a known AU
bool
RecognizeAU(Block* blocks, int eyes_status, int* face_bb, int*& rec_aus, const Init_para& parameters)
{

int i, j, dim, dir, max_mouth, best_mouth_au;
double mod, max_points;
double* au_points;
au* aus;


#ifdef FUNC_TRACK
PRN(("Starting RecognizeAU\n"));
#endif

// Initializing...
au_points = (double*) calloc(AU_NUM, sizeof(double));
if (au_points == NULL)
   return false;
   
rec_aus = (int*) calloc(AU_NUM, sizeof(int));
if (rec_aus == NULL)
   return false;

if (!InitAus(aus))
   return false;

// maximum dimension of the face
dim = face_bb[1] - face_bb[0];
if ((face_bb[3] - face_bb[2]) > dim)
   dim = face_bb[3] - face_bb[2];
 

// Maximum motion module for mouth blocks...
max_mouth = 0;
for (i = 0; i < MOUTH_BL_NUM; i++) {
    mod = blocks[i].motion_mod;
    if (mod > max_mouth)
       max_mouth = (int) mod;
}


// Mouth blocks
for (i = 0; i < MOUTH_BL_NUM; i++) {
    dir = blocks[i].motion_dir;
    if (max_mouth)
       mod = (double) blocks[i].motion_mod/max_mouth;
    else
       mod = 0.0;
    if (blocks[i].motion_mod > (parameters.min_mov * dim)) {                    // small movements are ignored
        for (j = 0; j < AU_NUM; j++) {
              if (aus[j].face_part == LOWER) {
                     au_points[j] += (aus[j].motion_points[i][dir] * mod);
              }
        }
     }
}

// Eyebrow blocks
for (i = MOUTH_BL_NUM; i < ALL_BL_NUM; i++) {
    dir = blocks[i].motion_dir;
    if (blocks[i].motion_mod > (parameters.min_mov * dim)) {                    // small movements are ignored
        for (j = 0; j < AU_NUM; j++) {
              if (aus[j].face_part == UPPER) {
                     au_points[j] += aus[j].motion_points[i][dir];
              }
        }
     }
}                 


i = 0;
max_points = 0.0;
best_mouth_au = -1;
for (j = 0; j < AU_NUM; j++) {
     if (aus[j].face_part == EYES && aus[j].eyes_status == eyes_status)        // Eyes AUs...
        au_points[j] += 1.0;
     if (aus[j].face_part == LOWER && au_points[j] > max_points) {             // Choosing only one mouth AU
        max_points = au_points[j];
        best_mouth_au = j;
     }
     if (parameters.debug_au)
        PRN(("AU %d: score %lf\n", aus[j].name, au_points[j]));
     if (aus[j].face_part != LOWER && au_points[j] >= parameters.au_thres) {
        PRN(("AU %d recognized!\n", aus[j].name));
        rec_aus[i] = aus[j].name;
        i++;
     }
}
if (best_mouth_au >= 0 && au_points[best_mouth_au] >= parameters.au_thres) {
        PRN(("AU %d recognized!\n", aus[best_mouth_au].name));
        rec_aus[i] = aus[best_mouth_au].name;
        i++;
}
if (i < AU_NUM)
   rec_aus[i] = 0;               // marking end of 'rec_aus' array    

// No AU recognized   
if (i == 0) {
        PRN(("No AU recognized!\n"));
        return false;
}


// Freeing memory...
free(au_points);
for (i = 0; i < AU_NUM; i++)
    FreeAu(aus[i]);
free(aus);

#ifdef FUNC_TRACK
PRN(("Ending RecognizeAU\n"));
#endif

return true;

}

// Freeing AU memory...
void
FreeAu(au& action_unit)
{

int i;

if (action_unit.face_part != EYES) {
    for (int i = 0; i < ALL_BL_NUM; i++) {
        free(action_unit.motion_points[i]);
    }
    
    free(action_unit.motion_points);
    action_unit.motion_points = NULL;
}

return;

}
