// file: exft_comp_1.cc
//

// isip include files
//
#include "extract_feature.h"
#include "extract_feature_constants.h"

#include <window.h>
#include <window_constants.h>
 
// method: compute_cc
//
// arguments: none
//
// return: none
//
// this is the core computation for Communicator.
//
float_8* Extract_feature::compute_cc(int_2*& in_data, int_4& num_samples,
				     int_4 block_a) {

  // local variables
  //
  float_8* mfc_data;
  logical_1 is_uttend = ISIP_FALSE;
  
  // store the input data into working buffer
  //
  if (num_samples != 0) {

    // dynamically assign space for data block
    //
    int_4 size = num_chans_d * num_samples * sizeof(int_2);
    if (buf_d == NULL) {
      size_d = capacity_d = size;
      buf_d = (char_1*) malloc(capacity_d);
    }
    else {
      size_d += size;
      if (size_d > capacity_d) {
	capacity_d = size_d;
	buf_d = (char_1*) realloc(buf_d, capacity_d);
      }
    }

    // copy the input data to work buffer
    //
    char_1* tmp_buf = &buf_d[size_d - size];
    memcpy(tmp_buf, (char*)in_data, size);
  }
  else {
    is_uttend = ISIP_TRUE;
  }

  // buffer all utterance for processing
  //
  if ((block_a == -1) && (num_samples != 0)) {
    num_samples = 0;
    return (float_8*)NULL;
  }

  // buffer data util receiving the enough data by user defined
  //
  else if (block_a > 0) {
    float_8 data_time = (size_d - current_d) /
      (sizeof(int_2) * frame_num_samples_d)
      * frame_dur_d;
    if (data_time < block_a) {
      return (float_8*)NULL;
    }
  }
  
  // process the raw data in working buffer
  // data size must garantee at least window size
  //
  if ((size_d - current_d) >= window_num_samples_d * (int_4)sizeof(int_2)) {
    
    // set the buffer size
    //
    data_sig_d.set_filesize_cc(size_d - current_d);

    // determine the time that this corresponds to
    //
    //    data_sig.end_file_time_d = (float_8)data_sig.file_size_d / 
    //      (sample_freq * (float_8)EXTF_NUM_BYTES * (float_8)num_chans);
    
    // determine the number of samples that will make up a window of data
    // and the number of samples that will make up a frame of data
    //
    int_4 window_num_samples = (int_4)(window_dur_d * EXTF_MSEC_TO_SEC
				       * sample_freq_d);
    int_4 frame_num_samples = (int_4)(frame_dur_d * EXTF_MSEC_TO_SEC
				      * sample_freq_d);
    
    // determine the number of frames that will be read in
    //
    int_4 delta_size = 0;
    if (delta_d) {
      delta_size += delta_win_d;
      if(delta_delta_d) {
	delta_size += delta_win_d;
      }
    }
    int_4 num_frames_to_read = (int_4)(data_sig_d.get_file_size_cc() /
				       (frame_num_samples * EXTF_NUM_BYTES))
      + delta_size;

    float_8** model_vector = new float_8*[num_frames_to_read];
    float_8* tmp_vec = new float_8[num_frames_to_read * vector_size_d];
    
    for (int_4 j = 0; j < num_frames_to_read; j++) {
      model_vector[j] = &tmp_vec[j * vector_size_d];
    }
    float_8 *vector = model_vector[0];

    // allocate memory for the window buffer
    //
    int_4 win_nsamps_pow2 = power_of_2(window_num_samples);
    float_8* window_buffer = new float_8[win_nsamps_pow2];
    
    // zero out a single window buffer
    //
    memset((void*)window_buffer, (int)0, (win_nsamps_pow2 * sizeof(float_8)));
    
    // start at sample 0, we will just skip the first bit of data so that we
    // can center the window around the frame of data. thus the window starts
    // at sample 0 while the frame does not
    // 
    int_4 start_sample = (int_4)0;
    int_4 num_samps_read = (int_4)0;
    
    Window window;
    window.set_cc(window_type_d, window_num_samples);

    // pointer to the window buffer
    //
    int_4 frame = 0;

    // pointer to the current pending data for processing
    //
    char* cur_buf = (char*)&buf_d[current_d];
    
    // loop until all data has been read
    //
    while (data_sig_d.read_cc(cur_buf, window_buffer, start_sample,
			      (start_sample + window_num_samples),
			      channel_d, num_samps_read)) {

      // stop reading data if a full window cannot be read
      //
      if (num_samps_read == window_num_samples) {
	
	if (zero_mean_d == ISIP_TRUE) {
	  
	  // zero mean source data
	  //
	  float_8 sum = (float_8)0;
	  for (int k = 0; k < window_num_samples; k++) {	  
	    sum += window_buffer[k];
	  }
	  sum /= (float_8)window_num_samples;
	  for (int k = 0; k < window_num_samples; k++) {	  
	    window_buffer[k] -= sum;
	  }
	}
	
	// compute energy from raw data
	//
	float_8 e = 0.0;
	for (int_4 i = 0; i < window_num_samples; i++) {
	  e += window_buffer[i] * window_buffer[i];
	}

	// don't allow the energy to floor which would indicate a signal
	// dropout
	//
	if (e >= 1.0) {
	  e = log(e);
	}
	else {
	  e = EXTF_MIN_ENERGY;
	}

	if (e > max_energy_d) {
	  max_energy_d = e;
	}

	// preemphasize the data if necessary
	//
	if (use_pre_emph_d == ISIP_TRUE) {
	  pre_emphasize_data_cc(window_buffer);
	}
	
	// apply the window if necessary
	//
	window.apply_cc(window_buffer, window_num_samples, (int_4)1);
	
	int_4 vec_pos = 0;
	int_4 algo = 0;
	
	for (algo = 0; algo < EXTF_NUM_ALGORITHMS; algo++) {
	  
	  if (num_coeffs_d[algo] > 0) {
	    
	    if (algo == EXTF_ALGO_MEAN ) {
	      vector[vec_pos] = e;
	    }
	    else if (algo == EXTF_ALGO_FBA) {
	      compute_fba_cc(&vector[vec_pos], num_coeffs_d[algo], 
			     window_buffer);
	    }
	    else if (algo == EXTF_ALGO_FFT_MFCC) {
	      compute_fft_mfcc_cc(&vector[vec_pos], num_coeffs_d[algo],
				  window_buffer);
	    }
	  }
	  vec_pos+= num_coeffs_d[algo];
	}
	
	// increment the start of the window
	//
	start_sample += frame_num_samples;
	
	// increment the vector
	//
	frame++;
	vector = model_vector[frame];
	
      } // end if loop    
      
      // there is no enough data to fill the whole window
      //
      else {
	break;
      }
    } // end of the while loop
    
    // normalize the vectors
    //
    normalize_cc(model_vector, frame, num_block_d);

    // perform any necessary post-processing
    //
    int valid_frame = 0;
    if (delta_d == ISIP_TRUE) {
      int i = 0;
      if (!num_block_d) {
	if (compute_delta_cc(model_vector[i], vector_first_d, delta_win_d,
			     delta_delta_d, EXTF_UTT_START)) {
	  valid_frame++;
	}
	i++;
      }

      for (; i < frame; i++) {
	if (compute_delta_cc(model_vector[i], vector_first_d, delta_win_d,
			     delta_delta_d, EXTF_UTT_DURING)) {
	  valid_frame++;
	}
      }
    }
    else {
      valid_frame = frame;
    }
    
    /*
    FILE* fmfc = fopen("mfc.txt", "w");
    for (int_4 i=0; i < frame; i++) {
      for (int_4 j=0; j < vector_size_d; j++) {
	fprintf(fmfc, "%f ", model_vector[i][j]);
      }
      fprintf(fmfc, "\n");
    }
    fclose(fmfc);
    */
    
    // buffer those unprocessing data
    //
    int start = frame * frame_num_samples * sizeof(int_2) * num_chans_d;
    current_d += start;
    //    memcpy(buf_d, &buf_d[start], size_d - current_d);

    // Mark the utterance end
    //
    if (delta_d == ISIP_TRUE) {
      if (num_samples == 0) {
	int i = 0;
	while (compute_delta_cc(model_vector[frame + i], vector_first_d,
				delta_win_d, delta_delta_d, EXTF_UTT_END)) {
	  valid_frame++;
	  i++;
	}
      }
    }
    
    // output the full vectors
    //
    mfc_data = new float_8[vector_size_d * valid_frame];
    if (num_block_d) {
      write_cc(mfc_data, model_vector, valid_frame);
    }
    else {
      write_cc(mfc_data, &model_vector[delta_size], valid_frame);
    }

    // count the number of blocks processed
    //
    num_block_d++;
    num_samples = vector_size_d * valid_frame;

    // free memory
    //
    delete [] window_buffer;
    
    delete [] tmp_vec;
    delete [] model_vector;

  } // end of processing data in working buffer

  // MARK THE UTTERANCE END
  //
  else if (num_samples == 0) {
    int frame = 0, valid_frame = 0;
    
    if(delta_d == ISIP_TRUE) {
      frame += delta_win_d;
      if (delta_delta_d == ISIP_TRUE) {
	frame += delta_win_d;
      }
    }
    
    // output the full vectors
    //
    mfc_data = new float_8[vector_size_d * delta_win_d * 2];
    float_8* tmp_vec = new float_8[vector_size_d];
    float_8* mfc = mfc_data;

    for (int i = 0; ; i++) {
      if (compute_delta_cc(tmp_vec, vector_first_d,
			   delta_win_d, delta_delta_d, EXTF_UTT_END)) {
	if (++valid_frame > frame) {
	  fprintf(stderr, "Compute delta end error! exit!\n");
	  exit(1);
	}
	mfc = &mfc_data[vector_size_d * i];
	write_cc(mfc, &tmp_vec, 1);
      }
      else {
	num_samples = vector_size_d * i;
	break;
      }
    }
  }

  // output raw file if required at the time of utterance end
  //
  if (is_uttend) {
    FILE* fin = fopen((char*)input_file_d, "w");
    if (fin != NULL) {
      fwrite(buf_d, sizeof(int_2), size_d / 2, fin);
      fclose(fin);
    }
  }
  
  // exit gracefully
  //
  return mfc_data;      
}
