/*********************************************************************
 * EDGE DETECTOR XP, Master program fo a parallel implementation of 
 *                   Sobel, Prewitt & Laplace Edge Detection Operators
 * 
 * Levent Besik, levent@cs.earlham.edu
 * Hassan Halta, hassan@cs.earlham.edu
 * 
 * Usage: pEdge <input file name in PNM format> 
 *********************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <time.h>    
#include <math.h>    
#include <pvm3.h>  

#include "pEdge.h"
#include "pixel_chunk.h"

int main(int argc, char **argv) {
   
   int mytid;                  /* My task id */
   int tids[MAX_PROC];         /* Slave task ids */
   int nproc = MAX_PROC;       /* Number of processes */
   struct timeval tv1, tv2;    /* For timing */
   int time_usec;              /* Accum. time */
   struct pvmhostinfo *hostp;  /* Struct containing the detailed info on PVM hosts */
   int nhost = 0;              /* Number of hosts added to PVM */
   int slave_die = 0;          /* Flag that is raised when it's time for the slave to die */
   char edge_operator;         /* Either s, p or l for eaach operator */
   int image_data[MAX_ARRAY_SIZE][MAX_ARRAY_SIZE]; /* Array that holds input image*/
   int result[MAX_ARRAY_SIZE][MAX_ARRAY_SIZE];     /* Array that holds transformed image */
   char image_header[HEADER_PARAMS][MAX];          /* Array that holds image header info */
   int slave_chunk[MAX_ARRAY_SIZE][MAX_ARRAY_SIZE];/* Chunk to be sent to the slaves */
   int image_x_size=0, image_y_size=0,  chunk_size=0, chunk_begin = 0, chunk_end = 0, slave_chunk_size = 0;  
   int killed=0, numt = 0, value=0, msgtype=0, who=0, i=0, j=0, k=0;
   char char_value[MAX];
   char fn_input[MAX];         /* array used for input path name */
   char fn_output_sobel[MAX];  /* array used for output path name */
   char fn_output_prewitt[MAX];/* array used for output path name */
   char fn_output_laplace[MAX];/* array used for output path name */
   FILE *fp_input;             /* pointer to the input file */
   FILE *fp_output_sobel;      /* pointer to the output file */
   FILE *fp_output_prewitt;    /* pointer to the output file */
   FILE *fp_output_laplace;    /* pointer to the output file */

   /* Check command line arguments */ 
   if (argc!=2){
      printf("-> Invalid arguments. Usage: pEdge <input file name>\n\n");
      exit(1);
   }
   else
     strcpy(fn_input,argv[1]); 
   
   printf("\n-> EDGE DETECTOR XP (Using Sobel,Prewitt & Laplace Operators)\n");
   fflush(NULL);
      
   /* Get Proper Path For Output File */
   strcpy(fn_output_sobel, getenv("HOME"));
   strcat(fn_output_sobel, output_file_prefix); 
   strcat(fn_output_sobel, "sobel.pnm\0"); 
   
   strcpy(fn_output_prewitt, getenv("HOME"));
   strcat(fn_output_prewitt, output_file_prefix); 
   strcat(fn_output_prewitt, "prewitt.pnm\0"); 
   
   strcpy(fn_output_laplace, getenv("HOME"));
   strcat(fn_output_laplace, output_file_prefix); 
   strcat(fn_output_laplace, "laplace.pnm\0"); 
   
   
   /* Open Input and Output File */
   if ((fp_input = fopen(fn_input,"r")) == NULL) {
     printf("* Can't open the input file: %s\n\n", fn_input);
     exit(1);
   }
   
   if ((fp_output_sobel = fopen(fn_output_sobel,"w")) == NULL) {
     printf("* Can't open the output file: %s\n\n", fn_output_sobel);
      exit(1);
   }
   if ((fp_output_prewitt = fopen(fn_output_prewitt,"w")) == NULL) {
      printf("* Can't open the output file: %s\n\n", fn_output_prewitt);
      exit(1);
   }
   if ((fp_output_laplace = fopen(fn_output_laplace,"w")) == NULL) {
      printf("* Can't open the output file: %s\n\n", fn_output_laplace);
      exit(1);
   }
   
   
   /* Read the image header */  
   for(i=0; i<2; i++){
      
      fgets(char_value, 128, fp_input);
      strcpy(image_header[i], char_value);        
      }
   
   for(i=2; i<HEADER_PARAMS; i++){
      
      fscanf(fp_input, "%s", char_value);
      strcpy(image_header[i], char_value);        
      }

   /* Check image type - has to be grayscale = P2 and 255*/
   if (strcmp(image_header[0], "P2\n")!=0 || atoi(image_header[4])!=255) {
      printf("* Input image is not grayscale! (It is instead %s with %s colors)\n\n", image_header[0], image_header[4]);
      exit(1);
   }
   else{
      printf("\n-> Input image is grayscale. Type %s\n", image_header[0]);
     fflush(NULL);
   }

      
   /* Retrieve and check the image sizes - can't be larger than MAX_ARRAY_SIZE*/
   image_x_size = atoi(image_header[2]);
   image_y_size = atoi(image_header[3]);
   
   if (image_x_size > MAX_ARRAY_SIZE || image_y_size > MAX_ARRAY_SIZE) {
      printf("* Input image is too big! Resolution needs to be lower than %d in both dimensions.\n\n", MAX_ARRAY_SIZE);
      exit(1);
   }
   else{
      printf("-> Input file's resolution is %d x %d.\n", image_x_size, image_y_size);				
      fflush(NULL);
   }
   
   /* Print image comment */
   printf("-> Image comment: %s \n", image_header[1]);				
   fflush(NULL);
   
   /* Enroll In PVM */
   mytid = pvm_mytid();
   
   /* Call pvm_config to see how many hosts we have. */
   if(pvm_config(&nhost, 0, &hostp ) < 0){
      printf("* Trouble getting PVM configuration. Aborting.\n");
      exit(1);
   }
   
   /* Calculate the number of slaves we'll need.
    *  Previous experiments show that 1 process per host is optimal.*/
   nproc = nhost;
   printf("-> %d hosts detected. Using that many slaves for parallelizing... \n", nproc);				
   
   /* Make sure this is not more than the max_proc since otherwise
    * we'll run over our tids[] array */
   if(nproc > MAX_PROC) nproc = MAX_PROC;
   
   /* Start Slave Tasks */
   numt = pvm_spawn(SLAVENAME, (char**)0, 0, "", nproc, tids);
   
   if (numt < nproc) {
      printf("* Trouble spawning slaves. Aborting. Error codes are: \n");
      for (i=numt; i<nproc; i++)
	printf("  TID %d %d\n", i, tids[i]);
      for (i=0; i<numt; i++)
	pvm_kill(tids[i]);
      pvm_exit();
      exit(1);
   }
      
   /* Write the image header to all 3 output files */  
   if(DEBUG) printf("-> Writing the image header info to output files.\n");
   for(i=0; i<HEADER_PARAMS; i++){
      fprintf(fp_output_sobel, "%s\n", image_header[i]);
      fprintf(fp_output_prewitt, "%s\n", image_header[i]);
      fprintf(fp_output_laplace, "%s\n", image_header[i]);
   }
   
   /* Read the image data to iamage_data array*/  
   printf("-> Loading the image file into memory.\n");
   fflush(NULL);
   for(j=0; j<image_y_size; j++){
      for(i=0; i<image_x_size; i++){
	 
	 if (fscanf (fp_input, "%d", &value) == EOF) 
	   printf("\n WARNING: End of file is reached before the array is filled - Could be a damaged image!\n");  
	 
	 if(DEBUG==2) printf("Read %d for (%d, %d) in image_data matrix\n", value, j, i); 
	 
	 image_data[j][i] = value;
      }
   }
   
   /* Calculate our chunk size by dividing the image by # of slaves in almost equal chunks in y direction
    * This is an approx calculation, and the last chunk can be more or less then this. Hence we calculate
    * the actual chunk size again in the loop */
   chunk_size = floor((image_y_size-2)/nproc);
   
   /****************************** SOBEL *****************************************/
   
   printf("-> Transforming image with Sobel \n");
   fflush(NULL);
   edge_operator='s';
  
   /* Send each slave a chunk of the image */
   for(i=1; i<=nproc; i++){

      /* Determine the beginning of new chunk to be sent to the slave*/
      chunk_begin = 1 + ((i-1) * chunk_size);

      /* If this is the last chunk, then chunk_end is the rest of the pixels, regardless
       * of what chunk_size is. Else update end of the new chunk */
      if(i==nproc || (chunk_begin + chunk_size > image_y_size-2)) 
	chunk_end = image_y_size-2;
      else chunk_end = chunk_begin + chunk_size;
      
      /* So after all these adjustments, calculate the actual chunk size */
      slave_chunk_size = chunk_end - chunk_begin + 1;
      
      /* Enumurate the slave_chunk from the image file with the given chunk_begin / end
       * We send one less row from the beginning and one more row from the end to
       * overlap the chunks */
      for(j=0; j<slave_chunk_size+2; j++){
	 if(chunk_begin==0) chunk_begin = 1;
	 
	 for(k=0; k<image_x_size; k++){   
	    slave_chunk[j][k] = image_data[j+chunk_begin-1][k];
	 }
      } 
      
      /* Send it */
      if(DEBUG) printf("\n* Sending Slave %d the chunk between %d and %d.\n", i, chunk_begin, chunk_end);
      fflush(NULL);
      
      pvm_initsend(PvmDataDefault); 
      pvm_pkint(&slave_die, 1, 1);         /* Time to die or not*/
      pvm_pkstr(&edge_operator);           /* The type of the operator we want */
      pk_pixel_chunk(slave_chunk);         /* The chunk array we want to transform */
      pvm_pkint(&chunk_begin, 1, 1);       /* Where the chunk begins on y axis */
      pvm_pkint(&image_x_size, 1, 1);      /* The x size of the chunk = x_image_size */ 
      pvm_pkint(&slave_chunk_size, 1, 1);  /* The y size of the chunk */
      pvm_send(tids[i-1], 0);
      
   }
      
   for(i=1; i<=nproc; i++){   
         
      /* Wait for the result from the slave. */
      msgtype = 5;
      pvm_recv(-1, msgtype);
      pvm_upkint(&who, 1, 1);
      pvm_upkint(&chunk_begin, 1, 1);
      upk_pixel_chunk(slave_chunk);
      pvm_upkint(&slave_chunk_size, 1, 1);
      
      if(DEBUG) printf("\n* Got results from %d for the chunk between %d and %d.\n", who, chunk_begin, chunk_begin+slave_chunk_size);
      fflush(NULL);
      
      /* Record the result from the slave to the results[][] array. Ignore the very first and last row. */
      for(j=chunk_begin+1; j<=chunk_begin + slave_chunk_size-1; j++){
	 for(k=0; k<image_x_size; k++){   
	    result[j][k]=slave_chunk[j-chunk_begin][k];
	 }
      }
   }
   
   /* Write the results[][] array to output file. Notice we are copying the pixels on the 
    * edges from the actual image since these pixels does not have 8 surrounding pixels, 
    * and hence cannot be transformed */
   printf("-> Writing transformed image to output file %s\n", fn_output_sobel);
   fflush(NULL);
   for(j=0; j<image_y_size; j++){
      for(i=0; i<image_x_size; i++){
	 
	 if(j==0 || j==image_y_size-1 || i==0 || i==image_x_size-1){
	    fprintf(fp_output_sobel, "%d \n", image_data[j][i]);
	 }
	 else{
	    fprintf(fp_output_sobel, "%d \n", result[j][i]);
	 }
      }
   }
      
   /****************************** PREWITT *****************************************/
   
   printf("-> Transforming image with Prewitt\n");
   fflush(NULL);
   edge_operator='p';
  
   /* Send each slave a chunk of the image */
   for(i=1; i<=nproc; i++){

      /* Determine the beginning of new chunk to be sent to the slave*/
      chunk_begin = 1 + ((i-1) * chunk_size);
      
      /* If this is the last chunk, then chunk_end is the rest of the pixels, regardless
       * of what chunk_size is. Else update end of the new chunk */
      if(i==nproc || (chunk_begin + chunk_size > image_y_size-2)) 
	chunk_end = image_y_size-2;
      else chunk_end = chunk_begin + chunk_size;
      
      /* So after all these adjustments, calculate the actual chunk size */
      slave_chunk_size = chunk_end - chunk_begin + 1;
      
      /* Enumurate the slave_chunk from the image file with the given chunk_begin / end
       * We send one less row from the beginning and one more row from the end to
       * overlap the chunks */
      for(j=0; j<slave_chunk_size+2; j++){
	 for(k=0; k<image_x_size; k++){   
	    if(chunk_begin==0) chunk_begin = 1;
	    slave_chunk[j][k] = image_data[j+chunk_begin][k];
	 }
      } 
      
      if(DEBUG) printf("\n* Sending Slave %d the chunk between %d and %d.\n", i, chunk_begin, chunk_end);
      fflush(NULL);
      
      pvm_initsend(PvmDataDefault); 
      pvm_pkint(&slave_die, 1, 1);         /* Time to die or not*/
      pvm_pkstr(&edge_operator);           /* The type of the operator we want */
      pk_pixel_chunk(slave_chunk);         /* The chunk array we want to transform */
      pvm_pkint(&chunk_begin, 1, 1);       /* Where the chunk begins on y axis */
      pvm_pkint(&image_x_size, 1, 1);      /* The x size of the chunk = x_image_size */ 
      pvm_pkint(&slave_chunk_size, 1, 1);  /* The y size of the chunk */
      pvm_send(tids[i-1], 0);
      
   }
   
   for(i=1; i<=nproc; i++){   
         
      /* Wait for the result from the slave. */
      msgtype = 5;
      pvm_recv(-1, msgtype);
      pvm_upkint(&who, 1, 1);
      pvm_upkint(&chunk_begin, 1, 1);
      upk_pixel_chunk(slave_chunk);
      pvm_upkint(&slave_chunk_size, 1, 1);
      
      if(DEBUG) printf("\n* Got results from %d for the chunk between %d and %d.\n", who, chunk_begin, chunk_begin+slave_chunk_size);
      fflush(NULL);
      
      /* Record the result from the slave to the results[][] array. Ignore the very first and last row */
      for(j=chunk_begin+1; j<=chunk_begin + slave_chunk_size-1; j++){
	 for(k=0; k<image_x_size; k++){   
	    result[j][k]=slave_chunk[j-chunk_begin][k];
	 }
      }
   }
   /* Write the results[][] array to output file. Notice we are copying the pixels on the 
    * edges from the actual image since these pixels does not have 8 surrounding pixels, 
    * and hence cannot be transformed */
   printf("-> Writing transformed image to output file %s\n", fn_output_prewitt);
   fflush(NULL);
   for(j=0; j<image_y_size; j++){
      for(i=0; i<image_x_size; i++){
	 
	 if(j==0 || j==image_y_size-1 || i==0 || i==image_x_size-1){
	    fprintf(fp_output_prewitt, "%d \n", image_data[j][i]);
	 }
	 else{
	    fprintf(fp_output_prewitt, "%d \n", result[j][i]);
	 }
      }
   }
   
   
    /****************************** LAPLACE *****************************************/
   
   printf("-> Transforming image with Laplace\n");
   fflush(NULL);
   edge_operator='l';
  
   /* Send each slave a chunk of the image */
   for(i=1; i<=nproc; i++){
      
      /* Determine the beginning of new chunk to be sent to the slave*/
      chunk_begin = 1 + ((i-1) * chunk_size);
      
      /* If this is the last chunk, then chunk_end is the rest of the pixels, regardless
       * of what chunk_size is. Else update end of the new chunk */
      if(i==nproc || (chunk_begin + chunk_size > image_y_size-2)) 
	chunk_end = image_y_size-2;
      else chunk_end = chunk_begin + chunk_size;
      
      /* So after all these adjustments, calculate the actual chunk size */
      slave_chunk_size = chunk_end - chunk_begin + 1;
      
      /* Enumurate the slave_chunk from the image file with the given chunk_begin / end
       * We send one less row from the beginning and one more row from the end to
       * overlap the chunks */
      
      for(j=0; j<slave_chunk_size+2; j++){
	 for(k=0; k<image_x_size; k++){   
	    if(chunk_begin==0) chunk_begin = 1;
	    slave_chunk[j][k] = image_data[j+chunk_begin][k];
	 }
      } 
      
      if(DEBUG) printf("\n* Sending Slave %d the chunk between %d and %d.\n", i, chunk_begin, chunk_end);
      fflush(NULL);
      
      pvm_initsend(PvmDataDefault); 
      pvm_pkint(&slave_die, 1, 1);         /* Time to die or not*/
      pvm_pkstr(&edge_operator);           /* The type of the operator we want */
      pk_pixel_chunk(slave_chunk);         /* The chunk array we want to transform */
      pvm_pkint(&chunk_begin, 1, 1);       /* Where the chunk begins on y axis */
      pvm_pkint(&image_x_size, 1, 1);      /* The x size of the chunk = x_image_size */ 
      pvm_pkint(&slave_chunk_size, 1, 1);  /* The y size of the chunk */
      pvm_send(tids[i-1], 0);
      
   }
   
   
   for(i=1; i<=nproc; i++){   
      
      /* Wait for the result from the slave. */
      msgtype = 5;
      pvm_recv(-1, msgtype);
      pvm_upkint(&who, 1, 1);
      pvm_upkint(&chunk_begin, 1, 1);
      upk_pixel_chunk(slave_chunk);
      pvm_upkint(&slave_chunk_size, 1, 1);
      
      if(DEBUG) printf("\n* Got results from %d for the chunk between %d and %d.\n", who, chunk_begin, chunk_begin+slave_chunk_size);
      fflush(NULL);
      
      /* Record the result from the slave. Ignore the first and the last row */
      for(j=chunk_begin+1; j<=chunk_begin + slave_chunk_size-1; j++){
	 for(k=0; k<image_x_size; k++){   
	    result[j][k]=slave_chunk[j-chunk_begin][k];
	 }
      }
   }
   
   /* Write the results[][] array to output file. Notice we are copying the pixels on the 
    * edges from the actual image since these pixels does not have 8 surrounding pixels, 
    * and hence cannot be transformed */
   printf("-> Writing transformed image to output file %s\n", fn_output_laplace);
   fflush(NULL);
   for(j=0; j<image_y_size; j++){
      for(i=0; i<image_x_size; i++){
	 
	 if(j==0 || j==image_y_size-1 || i==0 || i==image_x_size-1){
	    fprintf(fp_output_laplace, "%d \n", image_data[j][i]);
	 }
	 else{
	    fprintf(fp_output_laplace, "%d \n", result[j][i]);
	 }
      }
   }
   
   /* When all the operations are done, we start sending -exit- commands to
    * each of the slaves. We do this by raising "slave_die" flag in
    * our messages to the slaves. */
   killed=0;
   slave_die=1;
   chunk_begin=0; 
   chunk_size=0;
         
   /* Sending "Thanks for working for us. Goodbye" to slaves */
   for(killed=1; killed<=nproc; killed++ ) {
      
      if(DEBUG) printf("\n* Killing the Slave %d.\n",tids[killed-1]);
      fflush(NULL);
      
      pvm_initsend(PvmDataDefault); 
      pvm_pkint(&slave_die, 1, 1);   /* Time to die */
      pvm_send(tids[killed-1], 0);
      
   }
   
   
   /* We're done when all of the slaves are killed. */
   printf("-> Finished. Transformed images are ready to be viewed.\n\n");
   
   fclose(fp_input);
   fclose(fp_output_sobel);
   fclose(fp_output_prewitt);
   fclose(fp_output_laplace);
   
  return(0);
  }

