#include "../const.h"
#include <math.h>
#include "Blocks.h"
#include "Image.h"
#include "BMP.h"


// This function place blocks on the image so that each main face features is assigned one block
bool
PlaceBlocks(const Image& im_exp, const Image& mouth, const Image& eyebrow_left, const Image& eyebrow_right, 
            int* face_bb, Block*& all_blocks, const Init_para& parameters, int serialNumber)
{

int i;
bool result;
int* mouth_bb;
int* eyebrow_left_bb;
int* eyebrow_right_bb;
Block* mouth_blocks;
Block* eyebrow_left_blocks;
Block* eyebrow_right_blocks;

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

all_blocks = (Block*) calloc(ALL_BL_NUM, sizeof(Block));
if (all_blocks == NULL)
   return false;
   
// Getting bounding boxes...
BoundingBox(mouth, 1, mouth_bb);
BoundingBox(eyebrow_left, 1, eyebrow_left_bb);
BoundingBox(eyebrow_right, 1, eyebrow_right_bb);


result = PlaceMouthBlocks(mouth_bb, im_exp, face_bb, mouth_blocks, parameters, serialNumber);
if (!result)
   return false;
result = PlaceEyebrowBlocks(eyebrow_left_bb, LEFT, im_exp, eyebrow_left_blocks, parameters, serialNumber);
if (!result)
   return false;
result = PlaceEyebrowBlocks(eyebrow_right_bb, RIGHT, im_exp, eyebrow_right_blocks, parameters, serialNumber);
if (!result)
   return false;


for (i = 0; i < MOUTH_BL_NUM; i++)
    all_blocks[i] = mouth_blocks[i];
for (i = 0; i < EYEBROW_BL_NUM; i++)
    all_blocks[i + MOUTH_BL_NUM] = eyebrow_left_blocks[i];
for (i = 0; i < EYEBROW_BL_NUM; i++)
    all_blocks[i + MOUTH_BL_NUM + EYEBROW_BL_NUM] = eyebrow_right_blocks[i];

    
if (parameters.debug_blocks) {
   char name[MAX_LENGTH_FILE];
   sprintf(name, "%s%s%d.bmp", DATA_FOLDER, parameters.file_debug_blocks, serialNumber);
   ShowAllBlocks(name, im_exp, all_blocks);
}


// Freeing memory...
free(mouth_blocks);
free(eyebrow_left_blocks);
free(eyebrow_right_blocks);
free(mouth_bb);
free(eyebrow_left_bb);
free(eyebrow_right_bb);


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

return true;

}

// This function places blocks on significant parts of the mouth
bool
PlaceMouthBlocks(int* mouth_bb, const Image& im_exp, int* face_bb, Block*& mouth_blocks, const Init_para& parameters, int serialNumber)
{

int bb_width, face_width, x1, x2, y1, y2;
int* corners;
bool result;
Image region;

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


mouth_blocks = (Block*) calloc(MOUTH_BL_NUM, sizeof(Block));
if (mouth_blocks == NULL)
   return false;

// Enlarging the bounding box (in order not to lose details)
// of about one half of the bounding box width in both directions
// We are careful not to enlarge too much the bounding box:
// its width must not be greater than half the face width...
bb_width = mouth_bb[1] - mouth_bb[0];
face_width = face_bb[1] - face_bb[0];
x1 = mouth_bb[0];
x2 = mouth_bb[1];

if (mouth_bb[0] - (bb_width * parameters.mouth_bb) >= 0)
   mouth_bb[0] -= (int)(bb_width * parameters.mouth_bb);
else
   mouth_bb[0] = 0;
if (mouth_bb[1] + (bb_width * parameters.mouth_bb) < im_exp.width)
   mouth_bb[1] += (int)(bb_width * parameters.mouth_bb);
else
   mouth_bb[1] = im_exp.width - 1;

if ((mouth_bb[1] - mouth_bb[0]) > face_width/2){
   mouth_bb[0] = x1 - (face_width/2 - bb_width)/2;
   mouth_bb[1] = x2 + (face_width/2 - bb_width)/2;
   if (mouth_bb[0] < 0)
      mouth_bb[0] = 0;
   if (mouth_bb[1] > im_exp.width)
      mouth_bb[1] = im_exp.width - 1;
}


// Cropping mouth region
result = CropImage(mouth_bb, im_exp, region, NO_BORDER);
if (!result)
   return false;

// Extracting mouth corners
result = FindCorners(region, corners, parameters, serialNumber);
if (!result)
   return false;

// Mapping corners coordinates onto the expanded image
corners[0] += region.offsetX;
corners[1] += region.offsetY;
corners[2] += region.offsetX;
corners[3] += region.offsetY;


// First block: left mouth corner
if (corners[0] - (int)(bb_width * parameters.mouth_bl_dim) >= 0)
   x1 = corners[0] - (int)(bb_width * parameters.mouth_bl_dim);
else
   x1 = 0;

if (corners[0] + (int)(bb_width * parameters.mouth_bl_dim) < im_exp.width)
   x2 = corners[0] + (int)(bb_width * parameters.mouth_bl_dim);
else
   x2 = im_exp.width - 1;

if (corners[1] - (int)(bb_width * parameters.mouth_bl_dim) >= 0)
   y1 = corners[1] - (int)(bb_width * parameters.mouth_bl_dim);
else
   y1 = 0;

if (corners[1] + (int)(bb_width * parameters.mouth_bl_dim) < im_exp.height)
   y2 = corners[1] + (int)(bb_width * parameters.mouth_bl_dim);
else
   y2 = im_exp.height - 1;

mouth_blocks[0] = Block(x1, x2, y1, y2);



// Second block: right mouth corner
if (corners[2] - (int)(bb_width * parameters.mouth_bl_dim) >= 0)
   x1 = corners[2] - (int)(bb_width * parameters.mouth_bl_dim);
else
   x1 = 0;

if (corners[2] + (int)(bb_width * parameters.mouth_bl_dim) < im_exp.width)
   x2 = corners[2] + (int)(bb_width * parameters.mouth_bl_dim);
else
   x2 = im_exp.width - 1;

if (corners[3] - (int)(bb_width * parameters.mouth_bl_dim) >= 0)
   y1 = corners[3] - (int)(bb_width * parameters.mouth_bl_dim);
else
   y1 = 0;
   
if (corners[3] + (int)(bb_width * parameters.mouth_bl_dim) < im_exp.height)
   y2 = corners[3] + (int)(bb_width * parameters.mouth_bl_dim);
else
   y2 = im_exp.height - 1;

mouth_blocks[1] = Block(x1, x2, y1, y2);



// Third block: left portion of upper lip
x1 = mouth_blocks[0].x_max;

x2 = mouth_blocks[0].x_max + (mouth_blocks[1].x_min - mouth_blocks[0].x_max)/2;

if (mouth_bb[2] - (int)(bb_width * parameters.mouth_bl_dim) >= 0)
   y1 = mouth_bb[2] - (int)(bb_width * parameters.mouth_bl_dim);
else
   y1 = 0;

if (mouth_bb[2] + (int)(bb_width * parameters.mouth_bl_dim) < im_exp.height)
   y2 = mouth_bb[2] + (int)(bb_width * parameters.mouth_bl_dim);
else
   y2 = im_exp.height - 1;

mouth_blocks[2] = Block(x1, x2, y1, y2);


// Fourth block: right portion of upper lip
x1 = mouth_blocks[2].x_max;

x2 = mouth_blocks[1].x_min;

y1 = mouth_blocks[2].y_min;

y2 = mouth_blocks[2].y_max;

mouth_blocks[3] = Block(x1, x2, y1, y2);



// Fifth block: left portion of lower lip
x1 = mouth_blocks[0].x_max;

x2 = mouth_blocks[0].x_max + (mouth_blocks[1].x_min - mouth_blocks[0].x_max)/2;

if (mouth_bb[3] - (int)(bb_width * parameters.mouth_bl_dim) >= 0)
   y1 = mouth_bb[3] - (int)(bb_width * parameters.mouth_bl_dim);
else
   y1 = 0;

if (mouth_bb[3] + (int)(bb_width * parameters.mouth_bl_dim) < im_exp.height)
   y2 = mouth_bb[3] + (int)(bb_width * parameters.mouth_bl_dim);
else
   y2 = im_exp.height - 1;

mouth_blocks[4] = Block(x1, x2, y1, y2);



// Sixth block: right portion of lower lip
x1 = mouth_blocks[4].x_max;

x2 = mouth_blocks[1].x_min;

y1 = mouth_blocks[4].y_min;

y2 = mouth_blocks[4].y_max;

mouth_blocks[5] = Block(x1, x2, y1, y2);


// Freeing memory...
free(corners);
FreeImg(region);

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

return true;

}


// This function places blocks on significant parts of the eyebrow
bool
PlaceEyebrowBlocks(int* eyebrow_bb, int which, const Image& im_exp, Block*& eyebrow_blocks, const Init_para& parameters, int serialNumber)
{

int bb_width, x1, x2, y1, y2;

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


eyebrow_blocks = (Block*) calloc(EYEBROW_BL_NUM, sizeof(Block));
if (eyebrow_blocks == NULL)
   return false;

bb_width = eyebrow_bb[1] - eyebrow_bb[0];

if (which == LEFT) {

      // First block: outer eyebrow corner
      if (eyebrow_bb[0] - (int)(bb_width * parameters.eyebr_bb) >= 0)
         x1 = eyebrow_bb[0] - (int)(bb_width * parameters.eyebr_bb);
      else
         x1 = 0;
         
      if (eyebrow_bb[0] + (int)(bb_width * parameters.eyebr_bb) < im_exp.width)
         x2 = eyebrow_bb[0] + (int)(bb_width * parameters.eyebr_bb);
      else
         x2 = im_exp.width - 1;
      
      y1 = eyebrow_bb[2];
      
      if (eyebrow_bb[3] + (int)(bb_width * parameters.eyebr_bb) < im_exp.height)
         y2 = eyebrow_bb[3] + (int)(bb_width * parameters.eyebr_bb);
      else
         y2 = im_exp.height - 1;

      eyebrow_blocks[0] = Block(x1, x2, y1, y2);


      // Second block: inner eyebrow corner
      if (eyebrow_bb[1] - (int)(bb_width * parameters.eyebr_bb*2) >= 0)
         x1 = eyebrow_bb[1] - (int)(bb_width * parameters.eyebr_bb*2);
      else
         x1 = 0;

      if (eyebrow_bb[1] + (int)(bb_width * parameters.eyebr_bb) < im_exp.width)
         x2 = eyebrow_bb[1] + (int)(bb_width * parameters.eyebr_bb);
      else
         x2 = im_exp.width - 1;

      y1 = eyebrow_bb[2];
      
      y2 = eyebrow_bb[3];

      eyebrow_blocks[1] = Block(x1, x2, y1, y2);


      // Third block: central portion of eyebrow
      x1 = eyebrow_blocks[0].x_max;
      
      x2 = eyebrow_blocks[1].x_min;
      
      y1 = eyebrow_bb[2];
      
      y2 = eyebrow_bb[3];

      eyebrow_blocks[2] = Block(x1, x2, y1, y2);

}

else if (which == RIGHT) {

      // First block: outer eyebrow corner
      if (eyebrow_bb[1] - (int)(bb_width * parameters.eyebr_bb) >= 0)
         x1 = eyebrow_bb[1] - (int)(bb_width * parameters.eyebr_bb);
      else
         x1 = 0;
         
      if (eyebrow_bb[1] + (int)(bb_width * parameters.eyebr_bb) < im_exp.width)
         x2 = eyebrow_bb[1] + (int)(bb_width * parameters.eyebr_bb);
      else
         x2 = im_exp.width - 1;
      
      y1 = eyebrow_bb[2];
      
      if (eyebrow_bb[3] + (int)(bb_width * parameters.eyebr_bb) < im_exp.height)
         y2 = eyebrow_bb[3] + (int)(bb_width * parameters.eyebr_bb);
      else
         y2 = im_exp.height - 1;
      
      eyebrow_blocks[0] = Block(x1, x2, y1, y2);
      


      // Second block: inner eyebrow corner
      if (eyebrow_bb[0] - (int)(bb_width * parameters.eyebr_bb) >= 0)
         x1 = eyebrow_bb[0] - (int)(bb_width * parameters.eyebr_bb);
      else
         x1 = 0;
         
      if (eyebrow_bb[0] + (int)(bb_width * parameters.eyebr_bb*2) < im_exp.width)
         x2 = eyebrow_bb[0] + (int)(bb_width * parameters.eyebr_bb*2);
      else
         x2 = im_exp.width - 1;
         
      y1 = eyebrow_bb[2];
      
      y2 = eyebrow_bb[3];

      eyebrow_blocks[1] = Block(x1, x2, y1, y2);
      


      // Third block: central portion of eyebrow
      x1 = eyebrow_blocks[1].x_max;
      
      x2 = eyebrow_blocks[0].x_min;
      
      y1 = eyebrow_bb[2];
      
      y2 = eyebrow_bb[3];

      eyebrow_blocks[2] = Block(x1, x2, y1, y2);

}


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

return true;

}

// This function builds a template, cropping the given image
// according to the given block coordinates.
bool
BuildTemplate(const Block& b, const Image& image, Image& templ)
{

bool result;
int* bounding_box;

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

bounding_box = (int*) calloc(4, sizeof(int));
if (bounding_box == NULL)
   return false;

bounding_box[0] = b.x_min;
bounding_box[1] = b.x_max;
bounding_box[2] = b.y_min;
bounding_box[3] = b.y_max;

result = CropImage(bounding_box, image, templ, NO_BORDER);

free(bounding_box);

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

return result;

}

// Showing all blocks onto the given image
bool
ShowAllBlocks(char* path, const Image& image, Block* blocks)
{

bool result;
int i, n;
Image to_print;
BMP bmp;

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

to_print = Image(image.width, image.height, image.offsetX, image.offsetY);
if (to_print.data == NULL)
   return false;

for (n = 0; n < ALL_BL_NUM; n++) {
    for (i = blocks[n].x_min; i <= blocks[n].x_max; i++)
        to_print.data[i][blocks[n].y_min] = to_print.data[i][blocks[n].y_max] = WHITE;
    for (i = blocks[n].y_min + 1; i < blocks[n].y_max; i++)
        to_print.data[blocks[n].x_min][i] = to_print.data[blocks[n].x_max][i] = WHITE;
}

result = bmp.WriteAndHighlight(path, image, to_print);

FreeImg(to_print);

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

return result;

}

// Saving the block motion (module and direction)
void 
SetMotion(Block& block, int module, double direction)
{

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

block.motion_mod = module;

if (fabs(direction) <= EAST)
   block.motion_dir = DIR_E;
else if (fabs(direction) >= WEST)
   block.motion_dir = DIR_W;
else if (direction >= MIN_N && direction <= MAX_N)
   block.motion_dir = DIR_N;
else if (direction >= MIN_S && direction <= MAX_S)
   block.motion_dir = DIR_S;

else if (direction > 0 && fabs(direction) > EAST && direction < MIN_N)
   block.motion_dir = DIR_NE;
else if (direction > 0 && fabs(direction) < WEST && direction > MAX_N)
   block.motion_dir = DIR_NW;
else if (direction < 0 && fabs(direction) > EAST && direction > MAX_S)
   block.motion_dir = DIR_SE;
else if (direction < 0 && fabs(direction) < WEST && direction < MIN_S)
   block.motion_dir = DIR_SW;


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

return;

}


