/****************************<--OGI-->*************************************/
/*                                                                        */
/*             Center for Spoken Language Understanding                   */
/*        Oregon Graduate Institute of Science & Technology               */
/*                         Portland, OR USA                               */
/*                        Copyright (c) 2000                              */
/*                                                                        */
/*      This module is not part of the CSTR/University of Edinburgh       */
/*               release of the Festival TTS system.                      */
/*                                                                        */
/*  In addition to any conditions disclaimers below, please see the file  */
/*  "license_ogi_tts.txt" distributed with this software for information  */
/*  on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES.  */
/*                                                                        */
/****************************<--OGI-->*************************************/
#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>
#include "festival.h"
#include "OGIdbase.h"
#include "OGIdbUnits.h"
#include "OGIdbase_scm.h"
#include "OGI_file.h"

static void set_dbUnit_info(int indx, OGIdbase *db, EST_Relation *dbUnit_rel,  EST_Relation *dbSeg_rel);

LISP OGIdbase_UnitSel_Diphone(LISP utt){
  EST_Utterance *u = get_c_utt(utt);
  LISP llisp;
  EST_Item *lseg,*rseg;
  EST_String Lnm,Rnm,diphname;
  int indx;
  OGIdbase *db = NULL;

  if (db_is_loaded())
    db = get_db();
  else
    EST_error("OGIdbase_UnitSel_Diphone: no OGIdbase loaded.");

  u->create_relation("dbUnit");
  u->create_relation("dbSeg");
  EST_Relation *dbUnit_rel = u->relation("dbUnit");
  EST_Relation *dbSeg_rel = u->relation("dbUnit");

  /// loop through Segment items
  for (lseg=u->relation("Segment")->head(); lseg!=0; lseg=next(lseg)) {

    rseg = next(lseg);
    if (rseg != 0){
      
      // Left phone name
      if (lseg->f_present("allophone_name"))
	Lnm = lseg->f("allophone_name");
      else
	Lnm = lseg->name();	
      
      // Right phone name
      if (rseg->f_present("allophone_name"))
	Rnm = rseg->f("allophone_name");
      else
	Rnm = rseg->name();	
      
      // diphone unit 
      diphname = Lnm + "-" + Rnm;
      indx = db->name_lookup_hash(diphname);
      //      cout << diphname << " ---> " << indx;

      // if lookup failed, try alternates function
      if (indx < 0){
	llisp = cons(rintern("OGI_di_alt"),
		     cons(strintern(Lnm),
			  cons(strintern(Rnm),NIL)));
	llisp = leval(llisp,NIL);
	indx = get_c_int(llisp);
	//	cout << "  FAILED: backoff to " << indx;
      }
      // cout << endl;

      // Set info in dbUnit relation
      set_dbUnit_info(indx, db, dbUnit_rel, dbSeg_rel);

    }
  }    
  
  return utt;
}

static void set_dbUnit_info(int indx, 
			    OGIdbase *db,
			    EST_Relation *dbUnit_rel,
			    EST_Relation *dbSeg_rel){
  int n;
  // append a new dbUnit
  EST_Item *dbUnit = dbUnit_rel->append();
  EST_Item *seg;
  OGIdbUnits *U = &(db->unitdic);

  dbUnit->set_name(U->name(indx));
  dbUnit->set("db_indx",indx);
  dbUnit->set("Lindx",U->Lindx(indx));
  dbUnit->set("Rindx",U->Rindx(indx));
  dbUnit->set("isSegBndL", U->Lbnd(indx));
  dbUnit->set("isSegBndR", U->Rbnd(indx));
  
  // is previous unit this unit's natural neighbor?
  if (ffeature(dbUnit, "p.db_indx").Int() == U->Lindx(indx))
    dbUnit->set("isNatNbL", 1);
  else
    dbUnit->set("isNatNbL", 0);
    
  // append daughters to the dbUnit for each segment in the unit
  for (n=0; n<U->Nseg(indx); n++){
    seg = dbSeg_rel->append();
    seg->set_name(U->seg_name(indx,n));
    seg->set("dur",U->seg_dur(indx,n)/db->Fs);
    dbUnit->append_daughter(seg);
  }
}



// (define (OGI_di_unitsel utt)
//   "(OGI_di_unitsel utt)
// Simple unit selection for diphone synthesis: 
// constructs name from Segment names and selects first unit
// with same name."  	   
//   ;; UNIT SELECTION 
//   (set! SelectedUnits nil)
//   (mapcar
//    (lambda (seg)
// 	(if (equal? (item.next seg) nil)
// 	    nil    ;; if last segment of utt, don't do anything
// 	    (begin
// 
// 	      ;; create the name of the desired unit 
// 	     (set! Lnm (item.feat seg "allophone_name"))
// 	     (if (string-equal Lnm "0")
// 		 (set! Lnm (item.name seg)))
// 	     (set! Rnm (item.feat (item.next seg) "allophone_name"))
// 	     (if (string-equal Rnm "0")
// 	       (set! Rnm (item.name (item.next seg))))
// 
// 	     (set! n (string-append Lnm "-" Rnm))
// 
// 	      ;; look up in dbase
// 	      (set! indx (OGIdbase.unitsel_name n))
// 
// ;;	  (print (string-append "lookup unit " n " --> " indx))
// 
// 	      ;; look for alternates if original not found
// 	      (if (equal? indx nil)
// 		  (set! indx (OGI_di_alt Lnm Rnm)))
// 
// 	      ;; lookup info structure for this unit
// 	      (set! sel (list (OGIdbase.lookup_info indx)))
// 
// 	      ;; append info to list
// 	      (set! SelectedUnits (append SelectedUnits sel))
// 	      )))
//    (utt.relation.items utt "Segment"))
//   SelectedUnits)


// (define (OGI_set_dbUnit_feats utt SelectedUnits)
//   "(OGI_set_dbUnit_feats utt SelectedUnits)
// 	  Put necessary features into the dbUnit relation for OGI waveform synthesizer.
// 	  SelectedUnits is a list of structures containing necessary dbUnit info."
//  
//    ;; create dbUnit stream
//    (utt.relation.create utt "dbUnit")
// 
//    ;; Set FEATURES on the dbUnit for each SelectedUnit
//    (mapcar
//     (lambda (sel)
// 	 ;; append a new dbUnit
// 	 (set! uttUnit (utt.relation.append utt "dbUnit"))
// 	 
// 	 ;; set its name (for debug)
// 	 (item.set_name uttUnit (cadr (assoc 'name sel)))
// 	 
// 	 ;; index into current dbase
// 	 (item.set_feat uttUnit "db_indx" (cadr (assoc 'db_indx sel)))
// 
// 	  ;; is previous unit this unit's natural neighbor?
// 	 (item.set_feat uttUnit "isNatNbL" 0)
// 
// 	 (if (item.prev uttUnit)
// 	     (if (equal? (parse-number (item.feat (item.prev uttUnit) "db_indx"))
// 		     (parse-number (cadr (assoc 'Lindx sel))))
// 		   (item.set_feat uttUnit "isNatNbL" 1)
// 		   (item.set_feat uttUnit "isNatNbL" 0)))
// 
// 	 ;; is previous unit in the same segment as begin of this one?
// 	 (if (equal? (cadr (assoc 'Lbnd sel)) 1)
// 	     (item.set_feat uttUnit "isSegBndL" 1)
// 	     (item.set_feat uttUnit "isSegBndL" 0)
// 	     )
// 
// 	 ;; append daughters to the dbUnit for each segment in the unit
// 	 (mapcar
// 	  (lambda (uinfo)
// 	    (item.append_daughter uttUnit uinfo))
// 	  (cadr (assoc 'segs sel)))
//     )
//     SelectedUnits))

