// file: $USFS/class/image_segmentation/v1.0/imags_proc_2.cc
//

// isip include files
//
#include "image_segmentation.h"
#include "image_segmentation_constants.h"

// other include files
//
#ifndef __ISIP_PPM
#include <ppm.h>
#endif

#ifndef __ISIP_PPM_CONSTANTS
#include <ppm_constants.h>
#endif

// method: test_cc
//
// arguments:
//   char_1* input_image: (input) the input image file name
//   char_1* output_image: (input) the output image file name
//   char_1* output_datafile: (input) the output data file name
//   
// return:
//   a logical_1 value indicating status
//
// this method segments the image file into different regions
//
logical_1 Image_segmentation::test_cc(char_1* input_image_a,
				      char_1* output_image_a,
				      char_1* output_datafile_a) {

  // declare local variables
  //
  Ppm ppm_input, ppm_output;
  FILE* fp_out_data = (FILE*)NULL;
  float_8 distance[num_models_d];
  float_8 min = 0.0;
  int_4 min_class = 0;
  int_4 flag_output_image = ISIP_TRUE;
  
  // a set of flags for initializing the image_analysis objects
  //
  static logical_1 ia_init[IMAGS_NUM_MODELS] = {
    ISIP_TRUE, ISIP_TRUE, ISIP_TRUE, ISIP_TRUE, ISIP_TRUE, ISIP_TRUE
  };

  // check if the parameters are valid
  //
  if (input_image_a == (char_1*)NULL) {
    error_handler_cc((char_1*)"test_cc", (char_1*)"no input image file
                     specified");
    return ISIP_FALSE;
  }
  if (output_datafile_a == (char_1*)NULL) {
    error_handler_cc((char_1*)"test_cc", (char_1*)"no output data file
                     specified");
    return ISIP_FALSE;
  }
  if (output_image_a == (char_1*)NULL) {
    flag_output_image = ISIP_FALSE;
  }
  
  // open image files
  //
  if (ppm_input.open_cc(input_image_a, ISIP_READ_ONLY) == ISIP_FALSE) {
    error_handler_cc((char_1*)"test_cc", (char_1*)"error opening input
                     image file");
    return ISIP_FALSE;
  }

  if (flag_output_image == ISIP_TRUE) {
    if (ppm_output.open_cc(output_image_a, ISIP_WRITE_ONLY) == ISIP_FALSE) {
      error_handler_cc((char_1*)"test_cc", (char_1*)"error opening output
                       image file");
      return ISIP_FALSE;
    }
  }

  if ((fp_out_data = fopen((char*)output_datafile_a, "a")) == (FILE*)NULL) {
    error_handler_cc((char_1*)"test_cc", (char_1*)"error opening output
                     data file");
    return ISIP_FALSE;
  }
  
  // read the dimensions of the image
  //
  int_4 width = ppm_input.get_width_cc();
  int_4 height = ppm_input.get_height_cc();

  // write a header to the output image file
  //
  if (flag_output_image == ISIP_TRUE) {
    if (ppm_output.write_header_cc(width, height, PPM_MAX_COLOR)
	== ISIP_FALSE) {
      error_handler_cc((char_1*)"test_cc", (char_1*)"error writing the image
                       header to the output file");
      return ISIP_FALSE;
    }
  }

  // write the image file name into the output data file
  //
  fprintf(fp_out_data, "\n# %s\n", input_image_a);

  // check validity of the size data
  //
  if (win_size_d < frm_size_d) {
    error_handler_cc((char_1*)"train_cc", (char_1*)"window size is less than
                     frame size");
    return ISIP_FALSE;
  }
  if ((dct_blk_size_d > 0) && (dct_blk_size_d < frm_size_d)) {
    error_handler_cc((char_1*)"train_cc", (char_1*)"dct block size is less
                     than frame size");
    return ISIP_FALSE;
  }
  
  // choose the larger size as the block size used to load buffer
  // from image file
  //
  int_4 blk_size = win_size_d;
  if (blk_size < dct_blk_size_d) {
    blk_size = dct_blk_size_d;
  }

  // loop over the image file
  //
  for (int_4 i=0; i<height; i+=frm_size_d) {
    for (int_4 j=0; j<width; j+=frm_size_d) {

      // parameters related to frame/window, note the block size used to
      // load image data is not necessarily win_size_d
      //
      int_4 frm_row_npix = frm_size_d;
      int_4 frm_col_npix = frm_size_d;
      int_4 win_row_npix = blk_size;
      int_4 win_col_npix = blk_size;
      int_4 dif_frm_win = (blk_size - frm_size_d)/2;
      int_4 frm_row_num = j;
      int_4 frm_col_num = i;
      int_4 win_row_num = 0;
      int_4 win_col_num = 0;

      // checking for the out of bounds data
      //
      if ((j + frm_size_d) >= width) {
        frm_row_npix = width - j;
	win_row_npix = blk_size - (frm_size_d - frm_row_npix);
      }
      
      if ((i + frm_size_d) >= height) {
	frm_col_npix = height - i;
	win_col_npix = blk_size - (frm_size_d - frm_col_npix);
      }

      // a flag indicating if zero padding is necessary
      //
      logical_1 zero_padding = ISIP_FALSE;

      // declare buffers for reading the ppm image in blocks
      //
      uint_1* win_red = new uint_1[win_row_npix * win_col_npix];   
      uint_1* win_green = new uint_1[win_row_npix * win_col_npix];
      uint_1* win_blue = new uint_1[win_row_npix * win_col_npix];
    
      // parameters used for reading a block from the image
      //
      int_4 actual_off_w = 0;
      int_4 actual_off_h = 0;
      int_4 actual_width = win_row_npix;
      int_4 actual_height = win_col_npix;

      // compute the actual block position and size need to be read
      // from the image
      //
      if ((win_row_num = (frm_row_num - dif_frm_win)) < 0) {
        actual_off_w = 0;
        actual_width = actual_width - (actual_off_w - win_row_num);
        zero_padding = ISIP_TRUE;
      }
      else {
        actual_off_w = win_row_num;
      }

      if ((win_row_num + win_row_npix) > width) {
        actual_width = width - actual_off_w;
        zero_padding = ISIP_TRUE;
      }
	
      if ((win_col_num = (frm_col_num - dif_frm_win)) < 0) {
        actual_off_h = 0;
        actual_height = actual_height - (actual_off_h - win_col_num);
        zero_padding = ISIP_TRUE;
      }
      else {
        actual_off_h = win_col_num;
      }

      if ((win_col_num + win_col_npix) > height) {
        actual_height = height - actual_off_h;
        zero_padding = ISIP_TRUE;
      }
       
      // read the ppm file block by block
      //
      if (ppm_input.read_cc(win_red, win_green, win_blue, actual_off_w,
      		            actual_off_h, actual_width, actual_height)
	  == ISIP_FALSE) {
	error_handler_cc((char_1*)"test_cc",
			 (char_1*)"error reading the image file");
	return ISIP_FALSE;
      }

      // do zero padding if necessary
      //
      if (zero_padding == ISIP_TRUE) {
        if (pad_zero_cc(win_red, win_green, win_blue, win_row_num,
			win_col_num, win_row_npix, win_col_npix,
			actual_off_w, actual_off_h, actual_width,
			actual_height) == ISIP_FALSE) {
	  error_handler_cc((char_1*)"test_cc", (char_1*)"error
                           doing zero padding");
	  return ISIP_FALSE;
	}
      }
      
      for (int_4 m=0; m<num_models_d; m++) {

	// initiate
	//
	distance[m] = 0.0;

	// initialize
        //
	if (ia_init[m] == ISIP_TRUE) {
          if (ia_d[m].init_cc() == ISIP_FALSE) {
            error_handler_cc((char_1*)"train_cc", (char_1*)"error initilizing
                             the Image_analysis objects");
            return ISIP_FALSE;
          }
	  ia_init[m] = ISIP_FALSE;
	}

        // test according to the models
        //
        if (ia_d[m].test_cc(distance[m], win_red, win_green, win_blue,
			    win_row_num, win_col_num, win_row_npix,
			    win_col_npix)
	    == ISIP_FALSE) {
	  error_handler_cc((char_1*)"test_cc",
			 (char_1*)"error training the models");
	  return ISIP_FALSE;
        }

	// compare distances
	//
	if (m == 0) {
	  min = distance[m];
	  min_class = m;
	}
	else if (distance[m] < min) {
	  min = distance[m];
	  min_class = m;
	}
      }

      // update the buffers according to the classification result
      //
      if (update_buf_cc(min_class, win_red, win_green, win_blue,
			win_row_npix, win_col_npix) == ISIP_FALSE) {
	error_handler_cc((char_1*)"test_cc", (char_1*)"error updating the
                         buffers according to the classification result");
	return ISIP_FALSE;
      }

      // write the block to the output image
      //
      if (flag_output_image == ISIP_TRUE) {
	if (ppm_output.write_cc(win_red, win_green, win_blue, frm_row_num,
				frm_col_num, frm_row_npix, frm_col_npix)
	    == ISIP_FALSE) {
	  error_handler_cc((char_1*)"test_cc", (char_1*)"error writing
                           the output image file");
	  return ISIP_FALSE;
	}
      }

      // write the output data file
      //
      for (int_4 i = 0; i < num_models_d; i++) {
	fprintf(fp_out_data, "%9.4f ", distance[i]);
      }
      fprintf(fp_out_data, "%ld %ld %ld %ld %ld\n", min_class,
	      frm_row_num, frm_col_num, frm_row_npix, frm_col_npix);
  
      // release memory
      //
      if (win_red != (uint_1*)NULL) {
        delete [] win_red;
      }

      if (win_green != (uint_1*)NULL) {
        delete [] win_green;
      }

      if (win_blue != (uint_1*)NULL) {
        delete [] win_blue;
      }      
    }
  }    

  // close image files
  //
  if (ppm_input.close_cc() == ISIP_FALSE) {
    error_handler_cc((char_1*)"test_cc", (char_1*)"error closing image file");
    return ISIP_FALSE;
  }

  if (flag_output_image == ISIP_TRUE) {
    if (ppm_output.close_cc() == ISIP_FALSE) {
      error_handler_cc((char_1*)"test_cc",
		       (char_1*)"error closing image file");
      return ISIP_FALSE;
    }
  }

  fclose(fp_out_data);

  // exit gracefully
  //
  return ISIP_TRUE;
}
