// file: ts_state_1.cc
//

// system include files
//
#include <string.h>

// isip include files
//
#include "tie_state.h"
#include "tie_state_constants.h"

// method: pool_states_cc
//
// arguments:
//   FILE* fpm               : (input)  file pointer to model file
//   State** states          : (input)  old states
//   char_1** mono           : (input)  monophone list
//   int_4 num_mono          : (input)  number of monophones
//   int_4 num_cd_models     : (output) number of cd_models
//   Cd_models** cd_models   : (output) pointers to all Cd_models
//   Decision_tree** trees   : (output) pointers to decision trees
//   int_4 num_trees         : (output) number of decision_trees
//   State** new_states      : (output) new states 
//   int_4* trans_map        : (input)  map between central phone index and
//                                      transition matrix index
//   int_4 num_special_st    : (output) sum of states in special models
//   int_4 num_special_model : (input)  number of special models
//   char_1** special_models : (input) phones name in special models
//   int_4 ph_size           : (input) default phone size 
//   logical_1 mode          : (input) training or testing mode
//   int_4 state_num         : (input) number of states in common monophone
//
// return: a logical_1 showing status
//
// this method pools states to different decision tree root nodes
// according to the model and the states` position in the model
//
logical_1 pool_states_cc(FILE* fpm_a,  State** states_a, char_1** mono_a,
			 int_4 num_mono_a, int_4& num_cd_models_a,
			 Cd_model**& cd_models_a, Decision_tree**& trees_a,
			 int_4& num_trees_a, State**& new_states_a, 
			 int_4* trans_map_a, int_4& num_special_st_a,
			 int_4 num_special_model_a, char_1** special_models_a,
			 int_4 ph_size_a, logical_1 mode_a,
			 int_4 state_num_a) {
  
  // variables to read data
  //
  char_1* tmp = new char_1[ISIP_MAX_STRING_LENGTH];
  char_1* name = new char_1[ISIP_MAX_STRING_LENGTH];
  char_1* temp_str = (char_1*)NULL;
  
  int_4 model_index;
  int_4 num_states;
  int_4 state_ind = (int_4)0;

  // tag testing if it's special model
  //
  logical_1 tag_special_model = ISIP_FALSE;
 
  int_4 trans_index;
  
  int_4 i;
  int_4 j;
  int_4 k;

  char_1* lph ;
  char_1* cph ;
  char_1* rph ;
  int_4 n_lph = 0;
  int_4 n_cph = 0;
  int_4 n_rph = 0;
  
  // other local variables
  //
  int_4** special_models_state_index = new int_4*[TS_MAX_NUM_SPEMODEL];
  int_4** special_models_cd_st_index = new int_4*[TS_MAX_NUM_SPEMODEL];
  for (int_4 i = 0; i < TS_MAX_NUM_SPEMODEL; i++) {
    special_models_state_index[i] = new int_4[TS_NUM_STATE_SIZE];
    special_models_cd_st_index[i] = new int_4[TS_NUM_STATE_SIZE];
  }
  int_4 num_special_models_seen = (int_4)0;
  int_4* special_model_index = new int_4[20];
  
  // number of root nodes
  //
  num_trees_a = get_num_trees_cc(num_mono_a, num_special_model_a, state_num_a);
  
  // read data from file
  //
  while (fscanf(fpm_a, "%s", tmp) != EOF) {

    // ignore comment lines
    //
    if (tmp[0] == (char_1)'#') {
      
      // do nothing
      //
      fscanf(fpm_a, "%[^\n]", tmp);
      fscanf(fpm_a, "%[\n]", tmp);
    }
    
    // read the model data
    //
    else if (strcmp((char*)tmp, (char*)TS_STR_NUM_MODELS) == 0) {
      
      // read the number of models and allocate memory for number of states
      //
      fscanf(fpm_a, "%s%ld", tmp, &num_cd_models_a);    

      if (mode_a == TS_TRAIN_MODE) {

	// allocate memory for Cd_models
	//
	cd_models_a = new Cd_model*[num_cd_models_a];
	for (j = 0; j < num_cd_models_a; j++) {
	  cd_models_a[j] = new Cd_model();
	}
	
	//  define variables for building decision trees
	//
	Dt_node** roots;
	Link_node* leaf_node = (Link_node*)NULL;
	Link_list* leaf_list = (Link_list*)NULL;
	int_4 tree_index ;

	// varialbes for root nodes
	//  
	int_4* root_num_states = new int_4[num_trees_a];
	int_4** root_state_index = new int_4*[num_trees_a];


	
	// allocate memory for the trees
	//
	trees_a = new Decision_tree*[num_trees_a];
	
	for (i=0;i<num_trees_a;i++) {  
	  trees_a[i] = new Decision_tree();
	}

	// allocate memory for root nodes
	//
	roots = new Dt_node*[num_trees_a];	
	for (i = 0; i < num_trees_a; i++) {
	  roots[i] = new Dt_node();
	  root_num_states[i] = (int_4)0; 
	  root_state_index[i]= new int_4[TS_MAX_NUM_STATES];
	}


	// variable for phones in Cd_models
	//
	char_1** phn = new char_1*[ph_size_a];

	
	for (i = 0; i < num_cd_models_a; i++) {
	  
	  // read the model index, phone name, number of states
	  //
	  fscanf(fpm_a, "%s%ld", tmp, &model_index);     // index line 
	  fscanf(fpm_a, "%s%s", tmp, name);              // phone name
	  fscanf(fpm_a, "%s%ld", tmp, &num_states);      //num_states
	  fscanf(fpm_a, "%s%ld", tmp, &trans_index);     // the transition line
	  
	  cd_models_a[i] ->set_num_states_cc(num_states-2);
	    
	  // check if the left context exist
	  //
	  temp_str = (char_1*)strstr((char*)name, "-");
	    
	  // if not
	  //
	  if (temp_str == (char_1*)NULL) {
	    lph = (char_1*)NULL;
	    
	    // check if right context exist
	    //
	    temp_str = (char_1*)strstr((char*)name, "+");
	    if (temp_str == (char_1*)NULL) {
	      cph = name ;
	      rph = (char_1*)NULL;
	    }
	    else {
	      cph = (char_1*)strtok((char*)name, "+");
	      rph = (char_1*)strtok((char*)NULL, " ");
	    } 
	  }
	  
	  // if left context exist
	  //
	  else {
	    
	    // check if right context exist
	    //
	    temp_str = (char_1*)strstr((char*)name, "+");
	    lph = (char_1*)strtok((char*)name, "-");
	    
	    if (temp_str == (char_1*)NULL) {
	      cph = (char_1*)strtok((char*)NULL, " ");
	      rph = (char_1*)NULL;
	    }
	    else {
	      cph = (char_1*)strtok((char*)NULL, "+");
	      rph = (char_1*)strtok((char*)NULL, " ");
	    } 
	  }
	  
	  // get the left context index
	  //
	  if (lph != (char_1*)NULL) {
	    for (j=0; j<num_mono_a; j++) {
	      if (strcmp((char*)lph, (char*)mono_a[j])==0) {
		n_lph = j;
		break;
	      }
	    }
	  }
	  else {
	    n_lph = TS_EMPTY_PHN;
	  }
	  
	  // get the central phone index
	  //
	  if (cph != (char_1*)NULL) {
	    
	    for (j = 0; j < num_mono_a; j++) {  
	      if (strcmp((char*)cph, (char*)mono_a[j])==0) {
		n_cph = j;
		break;
	      }
	    }
	  }
	  else {
	    n_cph = TS_EMPTY_PHN;
	  }
	  
	  // get the right context index
	  //
	  if (rph!=(char_1*)NULL) {
	    
	    for (j = 0; j < num_mono_a; j++) {	    
	      if (strcmp((char*)rph, (char*)mono_a[j])==0) {
		n_rph = j;
		break;
	      }
	    }
	  }
	  else {
	    n_rph = TS_EMPTY_PHN;
	  }
	  
	  // set phones in Cd_models
	  //
	  phn[0] = lph;
	  phn[1] = cph;
	  phn[2] = rph;
	  cd_models_a[model_index]->set_phones_cc(phn);	    
	  
	  // check if it is a special model
	  //
	  for (k = 0; k < num_special_model_a; k++) {
	    if (strcmp((char*)cph, (char*)special_models_a[k]) == 0) {
	      tag_special_model = ISIP_TRUE;
	      break;
	    }
	  }
	  
	  if (tag_special_model == ISIP_TRUE) {
	    
	    tag_special_model = ISIP_FALSE;
	    trans_map_a[n_cph] = trans_index;
	    
	    // build new state object from old states
	    //
	    fscanf(fpm_a, "%s", tmp);
	    for (j = 0; j < num_states; j++) {
	      fscanf(fpm_a, "%ld", &state_ind);
	      if ((j > 0) && (j < num_states-1)) {

		// save the special model index
		//
		special_models_state_index[num_special_models_seen][j-1]
		  = state_ind;

		// set state index in Cd_models
		//
		cd_models_a[model_index]->
		  set_state_ind_cc(num_special_st_a,j);
		special_models_cd_st_index[num_special_models_seen][j-1] 
		  = num_special_st_a;
		
		num_special_st_a ++;
		
		// loop over all the states of all the previous special
		// to determine any shared state
		//
		for (int_4 k = 0; k < num_special_models_seen; k++) {

		  for (int_4 l = 0; l < 
			 cd_models_a[special_model_index[num_special_models_seen-1]]->get_num_states_cc();
		       l++) {

		    if (special_models_state_index[k][l] == state_ind) {

		      // set state index in cd_models
		      //
		      cd_models_a[model_index]->
			set_state_ind_cc(special_models_cd_st_index[k][l],j);
		    }		 
		  }
		}
		  
		// copy to new states
		//
		new_states_a[num_special_st_a-1] = 
		  new State(*states_a[state_ind]);
	      }
	    }
	    
	    special_model_index[num_special_models_seen] = model_index;
	    num_special_models_seen++;

	    fscanf(fpm_a, "%[^\n]", tmp);
	    fscanf(fpm_a, "%[\n]", tmp);
	  }
	  
	  // if non-special case 
	  //
	  else {
	    fscanf(fpm_a, "%s", tmp);
	    for (j = 0; j < num_states; j++) {
	      
	      // set the state index
	      //
	      fscanf(fpm_a, "%ld", &state_ind);	    
	      if ((j > 0) && (j < num_states - 1)) {
		tree_index = map_tr_index_cc(n_cph, num_special_model_a,j);  
		
		// set the central phone and state position
		//
		trees_a[tree_index]->set_cph_cc(cph);
		trees_a[tree_index]->set_state_pos_cc(j);
		
		// number of states are in the current list
		//
		k = root_num_states[tree_index];  
		root_state_index[tree_index][k] = state_ind;
		root_num_states[tree_index] ++;
		
		// set state index in Cd_models
		//
		cd_models_a[model_index]->set_state_ind_cc(state_ind, j);
		
	      }
	    }
	    
	    // fill in values to the trans_map
	    //
	    trans_map_a[n_cph] = trans_index;  
	  } 
	  
	}
  
	// set the leaf_list for the trees
	//
	for (i = 0; i < num_trees_a; i++) {  
	  leaf_node = new Link_node((void_p)roots[i]);
	  leaf_list = new Link_list();
	  leaf_list->insert_cc(leaf_node);
	  trees_a[i]->set_leaf_list_cc(leaf_list);
	  trees_a[i]->set_num_leaf_cc((int_4)1);
	}  
	
	// set the information to the root node and initialize the trees
	//
	for (i = 0;i < num_trees_a; i++) {  
	  roots[i]->set_states_cc(root_num_states[i], root_state_index[i]);
	  trees_a[i]->set_root_cc(roots[i]); 
	}
	
	// free the memory
	//
	delete [] root_num_states;
	
	for (i = 0;i < num_trees_a; i++) {
	  delete []  root_state_index[i];
	}
	delete [] root_state_index ;	
	delete [] phn;
	delete [] roots;
      }   

      // if test mode
      //
      else {
	
	// get number of states for special phones and trans_map only
	//
        char_1** phn = new char_1*[ph_size_a];
	int_4 spe_model_index = 0;

	// allocate memory for Cd_models
	//	
	cd_models_a = new Cd_model*[num_special_model_a];
	for (j = 0; j < num_special_model_a;j++) {
	  cd_models_a[j] = new Cd_model();
	}
	
	// now read the model data
	//
	for (int_4 i = 0; i < num_cd_models_a; i++) {
	  
	  // read the model index, phone name, number of states
	  //
	  fscanf(fpm_a, "%s%ld", tmp, &model_index);     // index line 
	  fscanf(fpm_a, "%s%s", tmp, name);              // phone name
	  fscanf(fpm_a, "%s%ld", tmp, &num_states);      // num_states
	  fscanf(fpm_a, "%s%ld", tmp, &trans_index);  // the transition line
	  
	  // check if the left context exist
	  //
	  temp_str = (char_1*)strstr((char*)name, "-");
	  
	  // if not
	  //
	  if (temp_str == (char_1*)NULL) {
	    lph = (char_1*)NULL;
	    
	    // check if right context exist
	    //
	    temp_str = (char_1*)strstr((char*)name, "+");
	    if (temp_str == (char_1*)NULL) {
	      cph = name ;
	      rph = (char_1*)NULL;
	    }
	    else {
	      cph = (char_1*)strtok((char*)name, "+");
	      rph = (char_1*)strtok((char*)NULL, " ");
	    } 
	  }
	  
	  // if left context exist
	  //
	  else {
	    
	    // check if right context exist
	    //
	    temp_str = (char_1*)strstr((char*)name, "+");
	    lph = (char_1*)strtok((char*)name, "-");
	    
	    if (temp_str == (char_1*)NULL) {
	      cph = (char_1*)strtok((char*)NULL, " ");
	      rph = (char_1*)NULL;
	    }
	    else {
	      cph = (char_1*)strtok((char*)NULL, "+");
	      rph = (char_1*)strtok((char*)NULL, " ");
	    } 
	  }
	  
	  // get the control phone index
	  //
	  if (cph!=(char_1*)NULL) {
	    
	    for (j = 0;j < num_mono_a; j++) {  
	      if (strcmp((char*)cph, (char*)mono_a[j])==0) {
		n_cph = j;
		break;
	      }
	    }
	  }
	  else {
	    n_cph = TS_EMPTY_PHN;
	  }
	  
	  // check if it is a special model
	  //
	  for (k = 0; k < num_special_model_a; k++) {
	    if (strcmp((char*)cph, (char*)special_models_a[k]) == 0) {
	      tag_special_model = ISIP_TRUE;
	      break;
	    }
	  }
	  
	  if (tag_special_model == ISIP_TRUE) {
	    
	    tag_special_model = ISIP_FALSE;
	    trans_map_a[n_cph] = trans_index;
	    
	    // set phones in Cd_models
	    //
	    phn[0] = lph;
	    phn[1] = cph;
	    phn[2] = rph;
	    cd_models_a[spe_model_index]->set_phones_cc(phn);	
	    cd_models_a[spe_model_index] ->set_num_states_cc(num_states-2);
	    
	    // build new states from old states
	    //
	    fscanf(fpm_a, "%s", tmp);
	    for (j = 0; j < num_states; j++) {
	      fscanf(fpm_a, "%ld", &state_ind);
	      if ((j > 0) && (j < num_states-1)) {
		// save the special model index
		//
		special_models_state_index[num_special_models_seen][j-1]
		  = state_ind;
		
		// set state index in Cd_models
		//
		cd_models_a[spe_model_index]->
		  set_state_ind_cc(num_special_st_a,j);
		special_models_cd_st_index[num_special_models_seen][j-1] 
		  = num_special_st_a;
		
		num_special_st_a ++;
		
		// loop over all the states of all the previous special
		// to determine any shared state
		//
		for (int_4 k = 0; k < num_special_models_seen; k++) {
		  
		  for (int_4 l = 0; l < 
			 cd_models_a[special_model_index[num_special_models_seen-1]]->get_num_states_cc();
		       l++) {
		    
		    if (special_models_state_index[k][l] == state_ind) {
		      
		      // set state index in cd_models
		      //
		      cd_models_a[spe_model_index]->
			set_state_ind_cc(special_models_cd_st_index[k][l],j);
		    }		 
		  }
		}
		
		// copy to new states
		//
		new_states_a[num_special_st_a-1] = 
		  new State(*states_a[state_ind]);
	      }
	    }
	    
	    special_model_index[num_special_models_seen] = spe_model_index;
	    num_special_models_seen++;
	    spe_model_index++;    
	    fscanf(fpm_a, "%[^\n]", tmp);
	    fscanf(fpm_a, "%[\n]", tmp);
	  }
	  
	  // if non-special case read only transition matrix indices
	  //
	  else {
	    
	    trans_map_a[n_cph] = trans_index;
	    fscanf(fpm_a, "%[^\n]", tmp);
	    fscanf(fpm_a, "%[\n]", tmp);
	    fscanf(fpm_a, "%[^\n]", tmp);
	    fscanf(fpm_a, "%[\n]", tmp);
	  }
	}
	
	num_cd_models_a = spe_model_index;
	
	//release memory
	//
	delete [] phn;
      }   
    }
  }
  delete [] name;
  delete [] tmp;
  for (int_4 i = 0; i < TS_MAX_NUM_SPEMODEL; i++) {
    delete [] special_models_state_index[i];
    delete [] special_models_cd_st_index[i];
  }
  delete [] special_models_state_index;
  delete [] special_models_cd_st_index;
  delete [] special_model_index;
  
  // exit gracefully
  //
  return ISIP_TRUE;
}
