// file: $isip/util/demo/isip_comm_data_recorder/isip_comm_data_recorder.c
// version: $Id: isip_comm_data_recorder.cc 10399 2006-01-27 19:44:47Z stanley $//

// include files
//
#include "isip_comm_data_recorder.h"

// method:  _GalSS_print_usage
//
// arguments:
//  int argc: (input) number of arguments
//  char ** argv: (input) arguments in the command line
//  
// return value: none
//
// This function prints out the usage message for this server.  It is
// called by Galaxy Communicator structure when users using -help
// option.
//
void _GalSS_print_usage(int argc, char **argv) {
  
  GalUtil_OAPrintOptions(argc, argv, oas, NULL);
  printf("\n");
  
  // return none
  //
  return;
}

// method:  _GalSS_init_server
//
// arguments:
//  Gal_Server *server_a: (input) Galaxy server pointer
//  int argc: (input) number of arguments
//  char ** argv: (input) arguments in the command line
//  
// return value: none
//
// this function initializes the server according the command line
// input, required by Galaxy Communicator structure
//
void *_GalSS_init_server(Gal_Server *server_a, int argc, char **argv) {
  
  // local variables
  //
  sys_id = NULL;
  
  // verification the commandline options are correct (to a first
  // approximation)
  //
  if (GalUtil_OACheckUsage(argc, argv, oas, NULL) == 0)
    exit(1);
  
  // get decoder key id
  //
  GalUtil_OAExtract(argc, argv, oas, "-sysid", GAL_OA_STRING, &sys_id);
  
  // if no value, assign default value
  //
  if (sys_id == NULL) {
    strcpy(sys_id_value, "si");
  }
  else {
    strcpy(sys_id_value, sys_id);
  }
  
  // return none
  //
  return (void *) NULL;
}


// method: reinitialize
//
// arguments:
//
//  Gal_Frame  f: (input) Galaxy Frame
//  void *server_data): (input) server information
//  
// return value: Galaxy Frame
//
// keys in GFrame:
//       key   :restart              value:  [server_name]
//
// this function reinitializes the server and returns a NULL Galaxy
// Frame by following the Galaxy convention
//
Gal_Frame reinitialize(Gal_Frame frame_a, void *server_data_a) {
  
  // local variables
  //
  Gal_Frame new_frame;
  
  // get the program name from incoming frame
  //
  char *program_name = Gal_GetString(frame_a, ":program");
  
  if (!program_name) {
    program_name = "main";
  }
  
  strcpy(server_name_d, SERVER_NAME);
  
  // make a new frame and set properties to it
  //
  new_frame = Gal_MakeFrame(program_name, GAL_CLAUSE);
  Gal_SetProp(new_frame, ":restart", Gal_StringObject(server_name_d));
  Gal_SetProp(new_frame, ":program", Gal_StringObject(program_name));
  
  // send the frame to hub
  //
  GalSS_EnvWriteFrame((GalSS_Environment *) server_data_a, new_frame, 0);
  
  // free the frame
  //
  Gal_FreeFrame(new_frame);
  
  // print debug message
  //
  GalUtil_PInfo1("**** Received the reinitialize message *****\n");
  
  // initialize some important global variable
  //
  utt_counter_d = 0;
  
  // Change from
  //server_state_d = GET_PROCESS_ID;
  // in order to by-pass the PID code since the process manager no longer
  // needs this information.
  //
  server_state_d = WAIT_AUDIO_READY;
  
  // return gracefully
  //
  return (Gal_Frame) NULL;
}


// method: receive_signal
//
// arguments:
//
//  Gal_Frame  frame: (input) Galaxy Frame
//  void *server_data): (input) server information
//  
// return value: Galaxy Frame
//
// keys in GFrame:
//       key   :hub_logdir              value:
//       key   :hub_log_prefix          value:  
//
// this function gets the frame from hub and call process signal to
// process it. Returns a NULL Galaxy Frame by following the Galaxy
// convention
//
Gal_Frame receive_signal(Gal_Frame frame_a, void *server_data_a) {
  
  fprintf(stdout,  "server state = %d\n", server_state_d);
  fflush(stdout);
  
  // calls the process_frame fuction that branches out depending on
  // the state of the finite state machine
  //
  process_frame(frame_a, server_data_a);
  
  // return gracefully
  //
  return (Gal_Frame)NULL;
}


// method: send_signal
//
// arguments:
//
//  Gal_Frame  frame: (input) Galaxy Frame
//  void *server_data): (input) server information
//  
// return value: none
//
// this function sends frame to hub 
//
void send_signal(Gal_Frame frame_a, void *server_data_a) {
  
  // send the frame to hub
  //
  GalSS_EnvWriteFrame((GalSS_Environment *) server_data_a, frame_a, 0);
}

// method: process_frame
//
// arguments:
//
//  Gal_Frame  frame: (input) Galaxy Frame
//  void *server_data): (input) server information
//  
// return value: none
//
// this function process frame according to different states of the
// system.
//
void process_frame(Gal_Frame frame_a, void *server_data_a) {
  
  if (server_state_d == GET_PROCESS_ID) {
    state_get_process_id(frame_a, server_data_a);
  }
  else if (server_state_d == WAIT_AUDIO_READY) {
    state_audio_ready(frame_a, server_data_a);
  }
  else if (server_state_d == RAW_DATA) {
    state_raw_data(frame_a, server_data_a);
  }
}


// method: state_get_process_id
//
// arguments:
//
//  Gal_Frame frame: (input) Galaxy Frame
//  void *server_data): (input) server information
//  
// return value: none
//
// this function process frame when the system in the process id state.
//
void state_get_process_id(Gal_Frame frame_a, void *server_data_a) {
  
  // local variables
  //
  Gal_Frame new_frame;
  
  // get the audio_ready signal
  //
  int process_id;
  
  // get the process id from the frame
  //
  process_id = Gal_GetInt(frame_a, ":process_id");
  
  if (process_id) {
    
    // get program name from incoming frame
    //
    char *program_name = Gal_GetString(frame_a, ":program");
    
    if (!program_name) {
      program_name = "main";
    }
    
    // get process id of this server
    //
    int pid = getpid();
    
    // make a new frame and set properties to it
    //
    new_frame = Gal_MakeFrame(program_name, GAL_CLAUSE);
    Gal_SetProp(new_frame, ":server_name", Gal_StringObject(server_name_d));
    Gal_SetProp(new_frame, ":pid", Gal_IntObject(pid));
    Gal_SetProp(new_frame, ":program", Gal_StringObject(program_name));
    
    // send the new frame to hub
    //
    send_signal(new_frame, server_data_a);
    
    // free the memeory for new frame
    //
    Gal_FreeFrame(new_frame);
    
    server_state_d = WAIT_AUDIO_READY;
  }
  else {
    fprintf(client_log_file_d,
	    "Process id query does not been received, exit\n");
    fflush(client_log_file_d);
    
    // exit from the program
    //
    exit(1);
  }
}


// method: state_audio_ready
//
// arguments:
//
//  Gal_Frame  frame: (input) Galaxy Frame
//  void *server_data): (input) server information
//  
// return value: none
//
// keys in GFrame:
//       key   :hub_logdir              value:
//       key   :hub_log_prefix          value:  
//
// this function process the frame when the system in the audio ready
// state.
//
void state_audio_ready(Gal_Frame frame_a, void *server_data_a) {
  
  // define local variable
  //
  Gal_Frame frame = NULL;
  
  // get the audio_ready signal
  //
  int audio_ready;
  
  // extract the audio_ready key from the frame
  //
  audio_ready = Gal_GetInt(frame_a, ":audio_ready");
  
  if (audio_ready) {
    
    int button_counter = Gal_GetInt(frame_a, ":button_press_counter"); 
    
    if (button_counter == 1 && !file_open) {
      char *client_name = Gal_GetString(frame_a, ":client_name");
      
      // hard coded filename
      //
      sprintf(audio_logdir_d, "/tmp/log_data_recorder_%s.txt", client_name);
      client_log_file_d = fopen(audio_logdir_d, "w");
      file_open = true;
    }
    
    // call to the set_format method with set the properties for the
    // audiofile object
    // 
    set_format(frame_a, server_data_a);
    adf_d.debug(L"set_format");
    
    // get the log directory information and hub log prefix information
    //
    char *hub_logdir = Gal_GetString(frame_a, ":hub_logdir");
    char *hub_log_prefix = Gal_GetString(frame_a, ":hub_log_prefix");
    
    // assign the log directory the global variable
    //
    sprintf(audio_logdir_d, "%s%s", hub_logdir, hub_log_prefix);
    
    // open the log file
    //
    sprintf(filename_d, "%s-%3d.%s", audio_logdir_d, utt_counter_d,
	    extension_d);
    
    // get the length of file name
    //
    int len = strlen(filename_d);
    
    // print debug information
    //
    if (DEBUG) {
      printf("filename_d[%d]=%s\n", len, filename_d);
    }
    
    // remove space in the file if exists because of the printing
    // format, such as speaker-  1.raw to speaker-001.raw
    //
    if (filename_d[len - 6] == ' ') { 
      filename_d[len - 6] = '0';
    }
    if (filename_d[len - 7] == ' ') { 
      filename_d[len - 7] = '0';
    }
    
    // free the frame
    //
    Gal_FreeFrame(frame);
    
    // declare a string and assign the filename_d object to it. do
    // some manipulation to get the desired filename format.
    //
    String output_filename;
    output_filename.assign((byte*)filename_d);
    output_filename.debug(L"output");
    output_filename.replaceAll(L"-", L"_");
    Filename output_file(output_filename);
    
    // open in write mode
    //
    adf_d.open(output_file, File::WRITE_ONLY);
    utt_counter_d++;
    String filename_only;
    output_file.getName(filename_only);
    
    char filename_temp[MAX_LENGTH] ;
    
    for (int i = 0; i < filename_only.length(); i++) {
      filename_temp[i] = filename_only(i);
    }
    
    // terminate the character string
    //
    filename_temp[filename_only.length()] = '\0';
    
    // replace the filename with the updated output filename
    //
    for (int i = 0; i < output_filename.length(); i++) {
      filename_d[i] = output_filename(i);
    }
    
    // terminate the character string
    //
    filename_d[output_filename.length()] = '\0';    
    
    // print debug information
    //
    if (DEBUG) {
      printf("audio_logdir_d = %s\n", audio_logdir_d);
    }
    
    char *source_server = Gal_GetString(frame_a, ":source_name");
    
    fprintf(client_log_file_d, "************************************************************************\n");        
    fprintf(client_log_file_d,
	    "received audio_ready_signal (%d)\n",
	    button_counter);
    fflush(client_log_file_d);
    
    // construct acknowledgement frame
    //
    frame = Gal_MakeFrame("main", GAL_CLAUSE);
    
    // assign the acknowlegements to the global variable
    //
    sprintf(acknowledgement_d, ":%s_ack", source_server);
    
    Gal_SetProp(frame, acknowledgement_d,
		Gal_StringObject("audio_ready"));
    Gal_SetProp(frame, ":source_name", Gal_StringObject(server_name_d));
    Gal_SetProp(frame, ":button_press_counter", Gal_IntObject(button_counter));
    Gal_SetProp(frame, ":filename", Gal_StringObject(filename_temp));
    
    // send the frame
    //
    send_signal(frame, server_data_a);
    
    fprintf(client_log_file_d,
	    "acknowledgement for audio_ready_signal (%d) has been sent\n",
	    button_counter);
    fflush(client_log_file_d);
    server_state_d = RAW_DATA;
    
  }
  else {
    
    if (client_log_file_d) {
      fprintf(client_log_file_d,
	      "audio ready signal has not been received, exit\n");
      fflush(client_log_file_d);
    }
    
    else {
      printf("Entered state_audio_ready without getting :audio_ready");
      fprintf(stderr,"Entered state_audio_ready without getting :audio_ready");
      fflush(stdout);
      fflush(stderr);
    }    
    
    // exit the program
    //
    exit(1);
  } 
}


// method: state_raw_data
//
// arguments:
//
//  Gal_Frame  frame: (input) Galaxy Frame
//  void *server_data): (input) server information
//  
// return value: 
//
// this function process frame when the system in the raw data state
//
void state_raw_data(Gal_Frame frame_a, void *server_data_a) {
  
  // define local variable
  //
  Gal_Frame frame = NULL;
  
  // get the the utterance end signal if it exists
  //
  if (Gal_GetString(frame_a, ":utterance_status")) {
    
    // close the file
    //
    adf_d.close();
    
    char *source_server = Gal_GetString(frame_a, ":source_name");
    
    // assign the acknowlegements to the global variable
    //
    sprintf(acknowledgement_d, ":%s_ack", source_server);
    
    // construct acknowledgement frame
    //
    frame = Gal_MakeFrame("main", GAL_CLAUSE);
    
    Gal_SetProp(frame, acknowledgement_d,
		Gal_StringObject("utterance_status"));
    Gal_SetProp(frame, ":source_name", Gal_StringObject(server_name_d));
    
    // send the frame
    //
    send_signal(frame, server_data_a);
    
    fprintf(client_log_file_d,
	    "total number of samples get = %d\n",
	    total_samples_d);    
    fprintf(client_log_file_d,
	    "acknowledgement for utterance status has been sent\n");
    fflush(client_log_file_d);
    
    // set the server state
    //
    server_state_d = WAIT_AUDIO_READY;
    
    return;
  }
  
  else if (Gal_GetString(frame_a, ":reset")) {
    
    // close the audiofile object
    //
    adf_d.close();
    
    // set the server state
    //
    server_state_d = WAIT_AUDIO_READY;
    return;
  }
  
  // else get the end pointed data and write it to a file
  //
  else {  
    
    int num_samples;
    
    // if the frame has the :raw_data_endpointed key
    //
    if (Gal_GetString(frame_a, ":raw_data_endpointed")) {
      
      // declare some temporary vectorfloats
      //
      VectorFloat output;
      Vector <VectorFloat> temp;
      temp.setLength(num_channels_d);
      char chan_key[MAX_LENGTH];
      int i;
      
      for ( i = 0; i < num_channels_d; i++) {
	
	// declare a float pointer
	//
	float* float32_obj;
	
	// create the channel key for get the data from Galaxy frame
	//
	sprintf(chan_key, ":channel%d", i);
	
	// extract the audio samples from the communicator frame
	//
	float32_obj = (float*)Gal_GetFloat32(frame_a, chan_key, &num_samples);
	output.setLength(num_samples);
	
	for (int j = 0; j < num_samples; j++) {
	  output(j) = float32_obj[j] * NORMALIZATION_VALUE;
	}
	
	// assign those values to teh vectorfloat object
	//
	temp(i).assign(output);
      }
      
      if (num_samples > 0) {    
	adf_d.writeAudioData(temp);
      }
      
      total_samples_d += num_samples;
      
      fprintf(client_log_file_d, "number of samples got = %d\n", num_samples);
      
      // define local variable
      //
      Gal_Frame frame = NULL;
      
      // construct a frame
      //
      frame = Gal_MakeFrame("main", GAL_CLAUSE);
      
      Gal_SetProp(frame, ":filename", Gal_StringObject(filename_d));
      Gal_SetProp(frame, ":start_decoding",
		  Gal_StringObject("start_decoding"));
      
      // send the frame
      //
      send_signal(frame, server_data_a);
      
    }
    else {
      fprintf(client_log_file_d, "Raw data does not been received, exit\n");
      fflush(client_log_file_d);
      
      // exit the program
      //
      exit(1);
    }
  }
}

// method: reset
//
// arguments:
//
//  Gal_Frame  f: (input) Galaxy Frame
//  void *server_data): (input) server information
//  
// return value: Galaxy Frame
//

// this function resets the server
//
Gal_Frame reset(Gal_Frame frame_a, void *server_data_a) {
  
  // close the audiofile object
  //
  adf_d.close();
  
  // reset the state
  //
  server_state_d = WAIT_AUDIO_READY;
  
  // return none
  //
  return (Gal_Frame) NULL;
}

// method: set_format
//
// arguments:
//
//  Gal_Frame  frame: (input) Galaxy Frame
//  void *server_data): (input) server information
//  
// return value: none
//
// keys in GFrame:
//       key   :sample_rate              value:  
//       key   :num_channels             value:
//       key   :file_format              value:
//
// this function set the file format when the system in the audio
// ready state.
//
void set_format(Gal_Frame frame_a, void *server_data_a) {
  
  // get sample rate
  //
  sample_rate_d = Gal_GetFloat(frame_a, ":sample_rate");
  
  // get the number of channels
  //
  num_channels_d = Gal_GetInt(frame_a, ":num_channels");    
  
  adf_d.setNumChannels(num_channels_d);
  adf_d.setSampleFrequency(sample_rate_d);
  
  char *file_format_d;
  
  // get the log directory information and hub log prefix information
  //
  if (Gal_GetString(frame_a, ":file_format")) {
    
    file_format_d = Gal_GetString(frame_a, ":file_format");
    
    // set up the format when necessary
    //
    if (strcmp(file_format_d, "RAW") == 0) {
      adf_d.setFileFormat(AudioFile::RAW);   // set file format
      adf_d.setFileType(AudioFile::BINARY);  // set file type
      strcpy(extension_d, "raw");            // set the extension
    }
    else if (strcmp(file_format_d, "SOF") == 0) {
      adf_d.setFileFormat(AudioFile::SOF);   // set file format
      adf_d.setFileType(AudioFile::TEXT);  // set file type
      strcpy(extension_d, "sof");            // set the extension 
    }
    else if (strcmp(file_format_d, "WAVE") == 0) {
      adf_d.setFileFormat(AudioFile::WAV);   // set file format
      adf_d.setFileType(AudioFile::BINARY);  // set file type
      strcpy(extension_d, "wav");            // set the extension
    }
    else if (strcmp(file_format_d, "SPHERE") == 0) {
      adf_d.setFileFormat(AudioFile::SPHERE);  // set file format
      adf_d.setFileType(AudioFile::BINARY);    // set file type
      strcpy(extension_d, "sphere");           // set the extension
    }
  }
  else {
    adf_d.setFileFormat(AudioFile::SOF);   // set file format
    adf_d.setFileType(AudioFile::TEXT);  // set file type
    strcpy(extension_d, "sof");            // set the extension
    
  }
  
}
