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

/* rdhmmdef.c --- read in HTK format HMM definition file (main) */

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

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

#define MAXBUFLEN  4096
#ifdef DELM
#undef DELM
#endif
#define DELM " \t\n<>"

/* parsing strategy: token look-ahead */
char *rdhmmdef_token;		/* current token */
				/* referred by other rdhmmdef_*.c */

/* global functions for rdhmmdef_*.c */
static char *buf = NULL;
static int line;

/* error exit */
void
rderr(char *str)
{
  if (rdhmmdef_token == NULL) {	/* end of file */
    j_error("\nError: %s on end of file\n", str);
  } else {
    j_error("\nError at line %d: %s\n", line, (str) ? str : "parse error");
  }
}

/* read next token and set it to rdhmmdef_token */
/* return NULL on end of file */
char *
read_token(FILE *fp)
{
  /* the quotation handler is moved to
     mystrtok_quote() in util/mystrtok.c */
  
  if (buf != NULL) {
    /* already have buffer */
    if ((rdhmmdef_token = mystrtok_quote(NULL, DELM)) != NULL) {
      /* return next token */
      return rdhmmdef_token;
    }
  } else {
    /* init: allocate buffer for the first time */
    buf = (char *)mymalloc(MAXBUFLEN);
    line = 1;
  }
  /* read new 1 line */
  if (getl(buf, MAXBUFLEN, fp) == NULL) {
    rdhmmdef_token = NULL;
  } else {
    rdhmmdef_token = mystrtok_quote(buf, DELM);
    line++;
  }
  return rdhmmdef_token;
}


/* lognize transition prob */
static void
conv_log_arc(HTK_HMM_INFO *hmm)
{
  HTK_HMM_Trans *tr;
  int i,j;
  LOGPROB l;

  for (tr = hmm->trstart; tr; tr = tr->next) {
    for(i=0;i<tr->statenum;i++) {
      for(j=0;j<tr->statenum;j++) {
	l = tr->a[i][j];
	tr->a[i][j] = (l != 0.0) ? log10(l) : LOG_ZERO;
      }
    }
  }
}


/* init data structure */
void
init_hmm(HTK_HMM_INFO *hmm)
{
  /* set default options */
  hmm->opt.stream_info.num = 1;
  hmm->opt.cov_type = C_DIAG_C;
  hmm->opt.dur_type = D_NULL;

  hmm->trstart = NULL;
  hmm->vrstart = NULL;
  hmm->ststart = NULL;
  hmm->dnstart = NULL;
  hmm->start   = NULL;
  hmm->lgstart = NULL;
  hmm->physical_root = NULL;
  hmm->logical_root = NULL;
  hmm->tr_root = NULL;
  hmm->vr_root = NULL;
  hmm->dn_root = NULL;
  hmm->st_root = NULL;
  hmm->codebooknum = 0;
  hmm->codebook_root = NULL;
  hmm->maxcodebooksize = 0;
  hmm->totalmixnum = 0;
  hmm->totalstatenum = 0;
  hmm->totalhmmnum = 0;
  hmm->totallogicalnum = 0;
  hmm->is_triphone = FALSE;
  hmm->is_tied_mixture = FALSE;
  hmm->prefer_cdset_avg = FALSE;
  hmm->totalpseudonum = 0;
}

/* main routine */
boolean
rdhmmdef(FILE *fp, HTK_HMM_INFO *hmm)
{
  char macrosw;
  char *name;

  /* initialize hmm data */
  init_hmm(hmm);
  
  /* read the first token */
  read_token(fp);
  
  /* the toplevel loop */
  while (rdhmmdef_token != NULL) {/* break on EOF */
    if (rdhmmdef_token[0] != '~') { /* toplevel commands are always macro */
      rderr(NULL);
    }
    macrosw = rdhmmdef_token[1];
    read_token(fp);		/* read next token after the "~.."  */
    switch(macrosw) {
    case 'o':			/* global option */
      set_global_opt(fp,hmm);
      break;
    case 't':			/* transition macro */
      name = mybstrdup(rdhmmdef_token);
      read_token(fp);
      def_trans_macro(name, fp, hmm);
      break;
    case 's':			/* state macro */
      name = mybstrdup(rdhmmdef_token);
      read_token(fp);
      def_state_macro(name, fp, hmm);
      break;
    case 'm':			/* density (mixture) macro */
      name = mybstrdup(rdhmmdef_token);
      read_token(fp);
      def_dens_macro(name, fp, hmm);
      break;
    case 'h':			/* HMM define */
      name = mybstrdup(rdhmmdef_token);
      read_token(fp);
      def_HMM(name, fp, hmm);
      break;
    case 'v':			/* Variance macro */
      name = mybstrdup(rdhmmdef_token);
      read_token(fp);
      def_var_macro(name, fp, hmm);
      break;
    case 'r':			/* Regression class macro (ignore) */
      name = mybstrdup(rdhmmdef_token);
      read_token(fp);
      def_regtree_macro(name, fp, hmm);
      break;
    }
  }

  /* check limitation */
  if (check_all_hmm_limit(hmm)) {
    j_printerr("limit check passed...\n");
  } else {
    j_error("Error: cannot use this HMM for system limitation.\n");
  }
  /* convert transition prob to log scale */
  conv_log_arc(hmm);
  
  /* check HMM parameter option type */
  if (!check_hmm_options(hmm)) {
    j_error("hmm options check failed\n");
  }

  /* add ID number for all HTK_HMM_State */
  /* also calculate the maximum number of mixture */
  {
    HTK_HMM_State *stmp;
    int n, max;
    n = 0;
    max = 0;
    for (stmp = hmm->ststart; stmp; stmp = stmp->next) {
      if (max < stmp->mix_num) max = stmp->mix_num;
      stmp->id = n++;
      if (n >= MAX_STATE_NUM) {
	j_error("Error: too much states > %d\n", MAX_STATE_NUM);
      }
    }
    hmm->totalstatenum = n;
    hmm->maxmixturenum = max;
  }
  /* compute total number of HMM models and maximum state num */
  {
    HTK_HMM_Data *dtmp;
    int n, maxlen;
    n = 0;
    maxlen = 0;
    for (dtmp = hmm->start; dtmp; dtmp = dtmp->next) {
      if (maxlen < dtmp->state_num) maxlen = dtmp->state_num;
      n++;
    }
    hmm->maxstatenum = maxlen;
    hmm->totalhmmnum = n;
  }
  /* compute total number of mixtures */
  {
    HTK_HMM_Dens *dtmp;
    int n = 0;
    for (dtmp = hmm->dnstart; dtmp; dtmp = dtmp->next) {
      n++;
    }
    hmm->totalmixnum = n;
  }

  return(TRUE);			/* success */
}
