/* Copyright (c) 1991-2002 Doshita Lab. Speech Group, Kyoto University */
/* Copyright (c) 2000-2002 Speech and Acoustics Processing Lab., NAIST */
/*   All rights reserved   */

/* hmm-lookup.c --- HMM name lookup with logical->physical mapping */

/* $Id: hmm_lookup.c,v 1.3 2002/09/11 22:01:50 ri Exp $ */

/* physical HMMs ... already indexed when reading in hmmdefs */
/* logical HMMs  ... already indexed when reading in HMMList */

#include <sent/stddefs.h>
#include <sent/htk_hmm.h>
#include <sent/ptree.h>

/* lookup physical HMM by its name */
HTK_HMM_Data *
htk_hmmdata_lookup_physical(HTK_HMM_INFO *hmminfo, char *keyname)
{
  HTK_HMM_Data *tmp;
  tmp = aptree_search_data(keyname, hmminfo->physical_root);
  if (strmatch(tmp->name, keyname)) {
    return tmp;
  } else {
    return NULL;
  }
}

/* lookup logical HMM by its name */
HMM_Logical *
htk_hmmdata_lookup_logical(HTK_HMM_INFO *hmminfo, char *keyname)
{
  HMM_Logical *tmp;
  tmp = aptree_search_data(keyname, hmminfo->logical_root);
  if (strmatch(tmp->name, keyname)) {
    return tmp;
  } else {
    return NULL;
  }
}

/* count the number of logical HMM and set to hmminfo->totallogicalnum */
static void
hmm_count_logical_num(HTK_HMM_INFO *hmminfo)
{
  HMM_Logical *lg;
  int n;

  n = 0;
  for (lg = hmminfo->lgstart; lg; lg = lg->next) n++;
  hmminfo->totallogicalnum = n;
}

/* add physical HMM to logical HMM */
/* this function should be called after HMMList is loaded */
/* logical HMM specified in hmmlist precedes physical HMM:
     physical HMM is added only if not exist in HMMList
*/
void
hmm_add_physical_to_logical(HTK_HMM_INFO *hmminfo)
{
  HMM_Logical *new, *match;
  HTK_HMM_Data *ph;

  for (ph = hmminfo->start; ph; ph = ph->next) {

    /* check if same name already exist */
    if (hmminfo->logical_root != NULL) {
      match = aptree_search_data(ph->name, hmminfo->logical_root);
      if (strmatch(match->name, ph->name)) {
	/* the physcal name was already mapped to other HMMs in HMMList */
	j_printerr("Warning: \"%s\" is defined in hmmdefs, but \"%s\" will be used instead\n", ph->name, (match->body.defined)->name);
	continue;
      }
    }
    /* create new HMM_Logical */
    /* body refers to the physical HMM */
    new = (HMM_Logical *)mybmalloc(sizeof(HMM_Logical));
    new->name = (char *)mybmalloc(strlen(ph->name) + 1);
    strcpy(new->name, ph->name);
    new->is_pseudo = FALSE;
    new->body.defined = ph;
    new->next = hmminfo->lgstart;
    hmminfo->lgstart = new;
    if (hmminfo->logical_root == NULL) {
      hmminfo->logical_root = aptree_make_root_node(new);
    } else {
      aptree_add_entry(new->name, new, match->name, &(hmminfo->logical_root));
    }
  }

  /* re-count total number */
  hmm_count_logical_num(hmminfo);
}


/* add pseudo monophone, biphone to logical HMM */
/* body refers to the cdset */
/* logical HMM specified in hmmlist precedes pseudo HMM:
     if monophone or biphone are explicitly defined in HMMList,
     their precedes pseudo HMM are not used */
static int add_count;
static void
hmm_add_pseudo_phones_sub(HTK_HMM_INFO *hmminfo, char *name)
{
  HMM_Logical *new, *match;

  /* check if already exist */
  match = aptree_search_data(name, hmminfo->logical_root);
  if (strmatch(match->name, name)) {
    /* already exist in list */
    /*    if (! match->is_pseudo) {*/
      /* this pseudo-HMM is already defined as real HMM in hmmdefs or in HMMList */
    /*designated_count++;
      }*/
  } else {
    /* create new HMM_Logical with pseudo body */
    new = (HMM_Logical *)mybmalloc(sizeof(HMM_Logical));
    new->name = (char *)mybmalloc(strlen(name) + 1);
    strcpy(new->name, name);
    new->is_pseudo = TRUE;
    new->body.pseudo = cdset_lookup(hmminfo, name);
    if (new->body.pseudo == NULL) {	/* should never happen */
      j_error("InternalError: tried to add pseudo phone \"%s\" to logical HMM, but no corresponding CD_Set found.  Why??\n");
    }
    new->next = hmminfo->lgstart;
    hmminfo->lgstart = new;
    if (hmminfo->logical_root == NULL) {
      hmminfo->logical_root = aptree_make_root_node(new);
    } else {
      aptree_add_entry(new->name, new, match->name, &(hmminfo->logical_root));
    }
    add_count++;
  }
}
    
/* add all possible pseudo monophone and biphone to logical HMM */
void
hmm_add_pseudo_phones(HTK_HMM_INFO *hmminfo)
{
  HMM_Logical *lg;
  char buf[50];
  int mcount, lcount, rcount;

  add_count = 0;
  /* add pseudo monophone */
  for (lg = hmminfo->lgstart; lg; lg = lg->next) {
    if (lg->is_pseudo) continue;
    hmm_add_pseudo_phones_sub(hmminfo, center_name(lg->name, buf));
  }
  /* add pseudo biphone, i.e. "a-k" etc. */
  for (lg = hmminfo->lgstart; lg; lg = lg->next) {
    if (lg->is_pseudo) continue;
    hmm_add_pseudo_phones_sub(hmminfo, leftcenter_name(lg->name, buf));
  }
  /* add pseudo biphone, i.e. "k+e" etc. */
  for (lg = hmminfo->lgstart; lg; lg = lg->next) {
    if (lg->is_pseudo) continue;
    hmm_add_pseudo_phones_sub(hmminfo, rightcenter_name(lg->name, buf));
  }
  j_printerr("%d added as logical...", add_count);

  hmminfo->totalpseudonum = add_count;
  /* re-count total number */
  hmm_count_logical_num(hmminfo);
}

/* return the number of state in a logical HMM */
int
hmm_logical_state_num(HMM_Logical *lg)
{
  int len;
  if (lg->is_pseudo) len = lg->body.pseudo->state_num;
  else len = lg->body.defined->state_num;
  return(len);
}

/* return trans structure for a logical HMM */
HTK_HMM_Trans *
hmm_logical_trans(HMM_Logical *lg)
{
  HTK_HMM_Trans *tr;
  if (lg->is_pseudo) tr = lg->body.pseudo->tr;
  else tr = lg->body.defined->tr;
  return(tr);
}
