// file: tie_state.cc
//
// this program is to tie states using decision tree
//

// isip include files
//
#include "tie_state.h"
#include "tie_state_constants.h"
#include <string.h>

// main program
//
int main(int_4 argc, char_1** argv) {
  
  // variables to hold commandline parameters
  //
  char_1* params_file = new char_1[ISIP_MAX_STRING_LENGTH];
  char_1* monophones_file;
  char_1* occupancy_file;
  char_1* membership_file;
  char_1* in_models_file;
  char_1* in_spe_models_file;
  char_1* out_models_file;
  char_1* in_states_file;
  char_1* out_states_file;  
  char_1* out_trees_file;
  char_1* in_trees_file;
  char_1* phlist_file;
  char_1* out_clist_file;
     
  // parameters to define size
  //
  int_4 num_monophones = 0;
  int_4 num_features = 0;
  int_4 num_states = 0;
  int_4 num_questions = 0;
  int_4 num_trees = 0;
  int_4 num_context = 1;
  int_4 num_mixtures = 1;
  int_4 ph_size = 3;

  // number of states in common models and number of special monophones
  //
  int_4 state_num = TS_NUM_STATE_SIZE;
  
  // thresholds for building trees
  //
  float_4 split_threshd = TS_DEFAULT_SPLIT_THRESHOLD;
  float_4 merge_threshd = TS_DEFAULT_MERGE_THRESHOLD;
  float_4 num_occ_threshd = TS_DEFAULT_NUM_OCC_THRESHOLD;
  
  // define the array of decision trees, root nodes etc
  //
  Decision_tree** decision_trees = (Decision_tree**)NULL;
  Dt_node* root_node = (Dt_node*)NULL;
  Link_list* leaf_list = (Link_list*)NULL;
  int_4* trans_map = (int_4*)NULL;
  float_4* occupancy = (float_4*)NULL;
  
  // context dependent models related variables
  //
  int_4 num_cd_models;
  int_4 num_phy_models;
  Cd_model** cd_models = (Cd_model**)NULL;
  char_1* phone = (char_1*)NULL;
  Hash_cell* next = (Hash_cell*)NULL;
  Hash_cell** hash_cells = (Hash_cell**)NULL;
  int_4 hash_size = 0;
    
  // define the array of left and right context for states
  //
  int_4** l_context = (int_4**)NULL;
  int_4** r_context = (int_4**)NULL;
  
  // other parameter
  //
  logical_1 mode = TS_TRAIN_MODE;
  logical_1 output_mode = TS_ASCII_MODE;
  int_4 debug_level = 0;
  
  // read the command line arguments
  //
  read_cmdline_cc(argc, argv, params_file);
  
  // read parameters
  //
  read_params_cc(params_file, in_models_file, out_models_file, in_states_file,
		 out_states_file, monophones_file, membership_file,
		 occupancy_file, out_trees_file, in_trees_file, phlist_file,
		 out_clist_file, in_spe_models_file, split_threshd,
		 merge_threshd, num_occ_threshd, num_context, mode,
		 output_mode, debug_level);
  
  delete [] params_file;
  
  // read monophones
  //
  char_1** monophones = read_monophones_cc(num_monophones, monophones_file);
  delete [] monophones_file;
  
  // read the membership file, set the questions and answers
  // 
  FILE* fp_membership = fopen((char*)membership_file, "r");
  if (fp_membership == (FILE*)NULL) {
    fprintf(stdout, "Error : cannot open input file %s\n", membership_file);
    exit(ISIP_PROTO_ERROR);
  }
  
  Question** questions = (Question**)NULL;
  read_questions_cc(num_questions, questions, fp_membership);
  
  Hash_table* answers = (Hash_table*)NULL;
  read_answers_cc(fp_membership, answers, monophones, num_monophones);
  
  delete [] membership_file;
  fclose(fp_membership);
  
  // open the output models file
  //
  FILE* fp_model_out = fopen((char*)out_models_file, "w");
  if (fp_model_out == (FILE*)NULL) {
    fprintf(stdout, "Error : cannot open input file %s\n", out_models_file);
    exit(ISIP_PROTO_ERROR);
  }
  delete [] out_models_file;
  
  // allocate memory for transition map
  //
  trans_map = new int_4[num_monophones];
  
  // create a memory manager 
  //
  Memory_manager* manager = new Memory_manager(TS_MAX_NUM_MODELS,
					       TS_MAX_NUM_MODELS);
  Link_list::set_manager_cc(manager);

  // read the input states file
  //
  State** states = read_states_cc(num_states, num_features, in_states_file);  
  delete [] in_states_file;
  
  // if this is traning mode where we create trees
  //
  if(mode == TS_TRAIN_MODE) {
    
    // read the state occupancy file
    //
    num_mixtures = states[1]->get_num_mixtures_cc();
    
    occupancy = new float_4[num_states];
    memset(occupancy, 0, num_states * sizeof(float_4));
    read_occupancy_cc(occupancy, num_states, num_mixtures, occupancy_file);
    delete [] occupancy_file;
  }

  // read the input models file, pool states and contexts to dt_node
  // according to the model and its position in the model
  //
  FILE* fp_model_in = fopen((char*)in_models_file, "r");
  if (fp_model_in == (FILE*)NULL) {
    fprintf(stdout, "Error : cannot open input file %s\n", in_models_file);
    exit(ISIP_PROTO_ERROR);
  }
  delete [] in_models_file;
  
  // label for all the leaf nodes, that is the new index for tied state
  //
  int_4 label = 1; 
  State** new_states = new State*[num_states];
  for (int_4 i = 0; i < num_states; i++) {
    new_states[i] = (State*)NULL;
  }
  new_states[0] = new State(*states[0]);

  // read models that need special treatment
  //
  FILE* fp_spe_model_in = fopen((char*)in_spe_models_file, "r");
  if (fp_spe_model_in == (FILE*)NULL) {
    fprintf(stdout, "Error : cannot open input file %s\n", in_spe_models_file);
    exit(ISIP_PROTO_ERROR);
  }  
  delete [] in_spe_models_file;

  int_4 num_special_models;
  char_1** special_models; 
  special_models = read_spm_cc(num_special_models, fp_spe_model_in);
  
  fclose(fp_spe_model_in);
  
  // pool the states and contexts according to the central phone
  //
  pool_states_cc(fp_model_in, states, monophones, num_monophones,
		 num_cd_models, cd_models, decision_trees,
		 num_trees, new_states, trans_map, label, num_special_models,
		 special_models, ph_size, mode, state_num);

  // if this is traning mode where we create trees
  //
  if(mode == TS_TRAIN_MODE) {
    
    pool_context_cc(fp_model_in, num_states, num_context, monophones,
		    num_monophones, l_context, r_context);

    // loop and build all the decision trees
    //
    for (int_4 i = 0; i < num_trees; i++) {
      
      // set thresholds for tree
      //
      decision_trees[i]->set_split_threshd_cc(split_threshd);
      decision_trees[i]->set_merge_threshd_cc(merge_threshd);
      decision_trees[i]->set_num_occ_threshd_cc(num_occ_threshd);
      
      // compute the likelihood for the root node
      //
      root_node = decision_trees[i]->get_root_cc();
      root_node->compute_likelihood_cc(states, occupancy,
				       num_features, num_mixtures);
    
      // spilt the tree
      //
      split_tree_cc(decision_trees[i], questions, answers,
		    states, occupancy, l_context, r_context,
		    split_threshd, num_occ_threshd, num_questions,
		    num_features, num_mixtures, debug_level);
      
      // this should be the new leaf list after merge, for now use
      // the leaf list of DT
      //
      leaf_list = decision_trees[i]->get_leaf_list_cc();

      // loop the new leaf list, assign the label to tied states and
      // output to the new states file
      //
      index_tree_cc(leaf_list, label, new_states, states,
		    occupancy, num_features, num_mixtures);

      // final merge pass
      //
      merge_tree_cc(decision_trees[i], questions, answers,
		    states, occupancy, l_context, r_context,
		    merge_threshd, num_features, num_mixtures,
		    debug_level);

    } // end building all the trees
    
    // mark the end of the new states 
    //
    new_states[label] = (State*)NULL;
  
    // print the new states
    //
    print_state_cc(out_states_file, new_states, label, num_features,
		   output_mode);
    
    delete [] out_states_file;
    
    // open the decision tree file to write
    //
    FILE* fp_out_trees = fopen((char*)out_trees_file, "w");
    if (fp_out_trees == (FILE*)NULL) {
      fprintf(stdout, "Error : cannot open input file %s\n", out_trees_file);
      exit(ISIP_PROTO_ERROR);
    }
    delete [] out_trees_file;  

    // output the file containing all decision trees
    //
    write_trees_cc(decision_trees, num_trees, fp_out_trees);
    fclose(fp_out_trees);
    
    FILE* fp_clist_out = fopen((char*)out_clist_file, "w");
    if (fp_clist_out == (FILE*)NULL) {
      fprintf(stdout, "Error : cannot open output file %s\n", out_clist_file);
      exit(ISIP_PROTO_ERROR);
    }
    delete [] out_clist_file;

    update_models_cc(monophones, num_monophones, num_cd_models,
		     cd_models, num_special_models, special_models,
		     decision_trees, ph_size, answers);
    
    Hash_table* phy_models = (Hash_table*)NULL;
    
    write_clist_cc(fp_clist_out, phy_models, num_phy_models, cd_models,
		   num_cd_models, ph_size);

    write_cd_models_cc(fp_model_out, num_monophones, monophones,
		       num_phy_models, cd_models, num_cd_models,
		       trans_map, phy_models);

    fclose(fp_clist_out);
    fclose(fp_model_out);

    // free memory 
    //
    for (int_4 i = 0; i < num_states; i++) {

      if (l_context[i] != (int_4*)NULL) {
	delete [] l_context[i];
      }
      if (r_context[i] != (int_4*)NULL) {
	delete [] r_context[i];
      }
    }  
    delete [] l_context;
    delete [] r_context;
    

    // delete hash table of the phy_models
    //
    hash_cells = phy_models->get_cells_cc();
    hash_size = phy_models->get_size_cc();
    for (int_4 k = 0; k < hash_size; k++) {
      for (Hash_cell* cell = hash_cells[k]; cell != (Hash_cell*)NULL;
	   cell = next) {
	next = cell->get_next_cc();
	phone = (char_1*)(cell->get_item_cc());
	delete [] phone;
	manager->delete_cc(cell);	
      }
      hash_cells[k] = (Hash_cell*)NULL;
    }
    delete phy_models;
    

    // delete occupancy
    //
    delete [] occupancy;
  }

  // test mode
  //
  else {
    
    // read the input models file, pool states and contexts to dt_node
    // according to the model and its position in the model
    //
    FILE* fp_in_trees = fopen((char*)in_trees_file, "r");
    if (fp_in_trees == (FILE*)NULL) {
      fprintf(stdout, "Error : cannot open input file %s\n", in_trees_file);
      exit(ISIP_PROTO_ERROR);
    }
    delete [] in_trees_file;  

    Question*** test_questions = (Question***)NULL;
    int_4* num_test_questions = (int_4*)NULL;
    
    read_trees_cc(decision_trees, num_trees, test_questions,
		  num_test_questions, fp_in_trees);
    fclose(fp_in_trees);

    // read the input models file, pool states and contexts to dt_node
    // according to the model and its position in the model
    //  
    FILE* fp_phlist_in = fopen((char*)phlist_file, "r");
    if (fp_phlist_in == (FILE*)NULL) {
      fprintf(stdout, "Error : cannot open input file %s\n", phlist_file);
      exit(ISIP_PROTO_ERROR);
    }
    delete [] phlist_file;

    FILE* fp_clist_out = fopen((char*)out_clist_file, "w");
    if (fp_clist_out == (FILE*)NULL) {
      fprintf(stdout, "Error : cannot open output file %s\n", out_clist_file);
      exit(ISIP_PROTO_ERROR);
    }
    delete [] out_clist_file;
  
    char_1** special_ph = (char_1**)NULL;
    Hash_table* phy_models = (Hash_table*)NULL;
    int_4  state_size = 3;
  
    pool_phones_cc(fp_phlist_in,  monophones,  num_monophones,
		   num_cd_models, cd_models, decision_trees,
		   special_ph, ph_size, state_size, num_special_models,
		   special_models,answers);		 
    
    write_clist_cc(fp_clist_out, phy_models, num_phy_models, cd_models,
		   num_cd_models, ph_size);

    write_cd_models_cc(fp_model_out, num_monophones, monophones,
		       num_phy_models, cd_models, num_cd_models,
		       trans_map, phy_models);
    
    // close the phone list, clist and models files
    //
    fclose(fp_phlist_in);
    fclose(fp_clist_out);
    fclose(fp_model_out);
  
    // delete hash table of the phy_models
    //
    next = (Hash_cell*)NULL;
    hash_cells = phy_models->get_cells_cc();
    hash_size = phy_models->get_size_cc();
    for (int_4 k = 0; k < hash_size; k++) {
      for (Hash_cell* cell = hash_cells[k]; cell != (Hash_cell*)NULL;
	   cell = next) {
	next = cell->get_next_cc();
	phone = (char_1*)(cell->get_item_cc());
	delete [] phone;
	manager->delete_cc(cell);
      }
      hash_cells[k] = (Hash_cell*)NULL;
    }
    delete phy_models;

    // clean up the test questions
    //
    if (test_questions != (Question***)NULL) {
      for (int_4 j = 0; j < num_trees; j++) {
	if (test_questions[j] != (Question**)NULL) {
	  for (int_4 k = 0; k < num_test_questions[j]; k++) {
	    if (test_questions[j][k] != (Question*)NULL) {
	      delete test_questions[j][k];
	    }
	  }
	  delete [] test_questions[j];
	}
      }
      delete [] test_questions;
      delete [] num_test_questions;
    }
  }

  // close files
  //
  fclose(fp_model_in);
    
  // delete transition map and num states
  //
  delete [] trans_map;
  
  // delete hash table of the answers
  //
  char_1* answer = (char_1*)NULL;
  next = (Hash_cell*)NULL;
  hash_cells = answers->get_cells_cc();
  hash_size = answers->get_size_cc();
  for (int_4 k = 0; k < hash_size; k++) {
    for (Hash_cell* cell = hash_cells[k]; cell != (Hash_cell*)NULL;
	 cell = next) {
      next = cell->get_next_cc();      
      answer = (char_1*)(cell->get_item_cc());
      if (answer != (char_1*)NULL) {
	delete answer;
      }
      manager->delete_cc(cell);
    }
    hash_cells[k] = (Hash_cell*)NULL;
  }
  delete answers;

  // free states
  //  
  for (int_4 i = 0; i < num_states; i++) {
    delete states[i];
    if (new_states[i] != (State*)NULL) {
      delete new_states[i];
    }
  }  
  delete [] states;
  delete [] new_states;

  // delete the cd_models
  //
  for (int_4 i = 0; i < num_cd_models; i++) {
    if (cd_models[i] != (Cd_model*)NULL) {
      delete cd_models[i];
    }
  }
  delete [] cd_models;
  
  // delete the special models
  //
  for (int_4 i = 0; i < num_special_models; i++) {
    if (special_models[i] != (char_1*)NULL) {
      delete [] special_models[i];
    }
  }
  delete [] special_models; 
  
  // delete trees
  //
  for (int_4 i = 0; i < num_trees; i++) {
    if (decision_trees[i] != (Decision_tree*)NULL) {
      delete decision_trees[i];
    }
  }
  delete [] decision_trees;
  
  // delete the context independent phones
  //
  for (int_4 i = 0; i < num_monophones; i++) {
    if (monophones[i] != (char_1*)NULL) {
      delete [] monophones[i];
    }
  }
  delete [] monophones;
  
  // delete questions
  //
  for (int_4 i = 0; i < num_questions; i++) {
    if (questions[i] != (Question*)NULL) {
      delete questions[i];
    }
  }
  delete [] questions;

  // delete memory manager
  //
  delete manager;
  
  // exit gracefully
  //
  exit(ISIP_NO_ERROR);
}
