/*---------------------------------------------------------------------------*
 *  SWIslts.c  *
 *                                                                           *
 *  Copyright 2007, 2008 Nuance Communciations, Inc.                               *
 *                                                                           *
 *  Licensed under the Apache License, Version 2.0 (the 'License');          *
 *  you may not use this file except in compliance with the License.         *
 *                                                                           *
 *  You may obtain a copy of the License at                                  *
 *      http://www.apache.org/licenses/LICENSE-2.0                           *
 *                                                                           *
 *  Unless required by applicable law or agreed to in writing, software      *
 *  distributed under the License is distributed on an 'AS IS' BASIS,        *
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 
 *  See the License for the specific language governing permissions and      *
 *  limitations under the License.                                           *
 *                                                                           *
 *---------------------------------------------------------------------------*/

#define _IN_SWI_SLTS__

#ifdef SWISLTS_USE_STATIC_API
#define SWISLTS_FNEXPORT
#else
#ifdef WIN32
#include <windows.h>
#define SWISLTS_FNEXPORT __declspec(dllexport)
#else
#define SWISLTS_FNEXPORT
#endif
#endif

#include <stdlib.h>


#include <string.h>

#include "pmemory.h"
#include "PFile.h"
#include "plog.h"

#include "SWIslts.h"
#include "lts.h"

/**
 * Phone map table
 */
typedef struct SLTS_PhoneMap_t{
    LCHAR *src;
    LCHAR *des;
} SLTS_PhoneMap;

#define INF_SILENCE_OPTIONAL         (const char *)"&"

static const SLTS_PhoneMap g_aPhoneMap[] = {
     {"PS", "&"},
     {"SS0", ""},
     {"SS1", ""},
     {"SS2", ""},
     {"WS", "&"}
};
static const int g_numPhones = sizeof(g_aPhoneMap) / sizeof(g_aPhoneMap[0]);

#ifdef USE_STATIC_SLTS 
#define MAX_INPUT_LEN 255
static SLTS_Engine g_sltsEngine;
static SWIsltsWrapper g_sltsWrapper;
#endif

#define MAX_PRON_LEN      255
#define MAX_PHONE_LEN     4

static SWIsltsResult GetPhoneStr(SLTS_Engine *pEng, char *apszPhones[], int num_phones, char *pszPhoneStr, size_t *len);

SWISLTS_FNEXPORT SWIsltsResult SWIsltsGetWrapper(SWIsltsWrapper **ppLtsWrap)
{
  if (ppLtsWrap != NULL) {
#ifdef USE_STATIC_SLTS
    *ppLtsWrap = &g_sltsWrapper;
#else
    *ppLtsWrap = MALLOC(sizeof(SWIsltsWrapper), MTAG);
    if (*ppLtsWrap == NULL) {
      return SWIsltsErrAllocResource;
    }
#endif
    (*ppLtsWrap)->init = SWIsltsInit;
    (*ppLtsWrap)->term = SWIsltsTerm;
    (*ppLtsWrap)->open = SWIsltsOpen;
    (*ppLtsWrap)->close = SWIsltsClose;
    (*ppLtsWrap)->textToPhone = SWIsltsTextToPhone;
  }
  return SWIsltsSuccess;
}

SWISLTS_FNEXPORT SWIsltsResult SWIsltsReleaseWrapper(SWIsltsWrapper *pLtsWrap)
{
#ifndef USE_STATIC_SLTS
  if (pLtsWrap != NULL) {
    FREE(pLtsWrap);
    pLtsWrap = NULL;
  }
#endif
  return SWIsltsSuccess;
}

/* External Core SLTS API implementation */
SWISLTS_FNEXPORT SWIsltsResult SWIsltsInit()
{
  return SWIsltsSuccess;
}

SWISLTS_FNEXPORT SWIsltsResult SWIsltsTerm()
{
  return SWIsltsSuccess;
}

/* create a new instance of SLTS */
SWISLTS_FNEXPORT SWIsltsResult SWIsltsOpen(SWIsltsHand *phLts, 
                                           const char *data_filename)     
{
  SLTS_Engine      * pEng;
  SWIsltsResult      nRes = SWIsltsSuccess;

  if ((phLts == NULL)
#ifndef USE_STATIC_SLTS
    || (data_filename == NULL)
#endif 	
    ) 
  {
    return SWIsltsInvalidParam;
  }

#ifdef USE_STATIC_SLTS 
  pEng = &g_sltsEngine;
#else
  pEng = CALLOC(1, sizeof(SLTS_Engine), MTAG);
  if (pEng == NULL) {
    return SWIsltsErrAllocResource;
  }
#endif

  /* initialize */
  nRes = create_lts((char *)data_filename, &pEng->m_hLts);
  if (nRes != SWIsltsSuccess) {
    PLogError(L("create_lts with the model file (%s) fails with return code %d\n"), (char *)data_filename, nRes);
    goto CLEAN_UP;
  }

  *phLts = (SWIsltsHand)pEng;
  
  return SWIsltsSuccess;

 CLEAN_UP:
  if (*phLts != NULL) {
    SWIsltsClose(*phLts);
  }
  
  return nRes;
}

/* deletes given instance of SLTS */
SWISLTS_FNEXPORT SWIsltsResult SWIsltsClose(SWIsltsHand hLts)
{
  SLTS_Engine *pEng = (SLTS_Engine *)hLts;
  if (pEng == NULL) {
    return SWIsltsInvalidParam;
  }

  /* clean up internal buffers and slts structure */
  if (pEng->m_hLts) {
    free_lts(pEng->m_hLts);
  }
  pEng->m_hLts = NULL;

#ifndef USE_STATIC_SLTS 
  FREE(pEng);
#endif

  pEng = NULL;

  return SWIsltsSuccess;
}

/* send phones to internal buffer */
SWISLTS_FNEXPORT SWIsltsResult SWIsltsTextToPhone(SWIsltsHand hLts, 
                                               const char *text, 
                                               char *output_phone_string[],
                                               int *output_phone_len,
                                               int max_phone_len)
{
  int i;
  SWIsltsResult          nRes = SWIsltsSuccess;
#ifdef USE_STATIC_SLTS  
  char new_text[MAX_INPUT_LEN];
#else
  char *new_text;
#endif

  SLTS_Engine *pEng;
  if (hLts == NULL) {
    return SWIsltsInvalidParam;
  }

  if (text == NULL) {
    return SWIsltsInvalidParam;
  }
  
  /* check that the output phone string param is allocated */
  for(i=0; i<max_phone_len; i++){
    if(output_phone_string[i] == NULL)
      return SWIsltsInvalidParam;
  }

  pEng = (SLTS_Engine *)hLts;

  /* get rid newlines, tabs, and spaces, if any */
#ifdef USE_STATIC_SLTS 
  if((strlen(text)+1) > MAX_INPUT_LEN){  
    return SWIsltsMaxInputExceeded;
  }
#else  
  new_text = MALLOC((strlen(text)+1)*sizeof(char), MTAG);
  if (new_text == NULL) {
    PLogError(L("SWISLTS_OUT_OF_MEMORY"));
    return SWIsltsErrAllocResource;        
  }
#endif

  strcpy(new_text, text);
  i = strlen(new_text)-1;
  while(new_text[i] == '\n' || new_text[i] == ' ' || new_text[i] == '\t') i--;
  new_text[i+1] = '\0'; 

  /* now check if the input string is empty */
  if(strlen(new_text) == 0){
    *output_phone_len = 0;
    nRes = SWIsltsEmptyPhoneString;
    goto CLEAN_UP;
  }

  *output_phone_len = max_phone_len;
  nRes = run_lts(pEng->m_hLts, pEng->m_hDict, new_text, output_phone_string, output_phone_len);
  if (nRes != SWIsltsSuccess) {
    goto CLEAN_UP;
  }

#ifndef USE_STATIC_SLTS 
  if(new_text){
    FREE(new_text);
  }
  new_text = NULL;
#endif

  return SWIsltsSuccess;

 CLEAN_UP:

#ifndef USE_STATIC_SLTS 
  if(new_text){
    FREE(new_text);
  }
  new_text = NULL;
#endif

  return nRes;
}

SWISLTS_FNEXPORT SWIsltsResult SWIsltsG2PGetWordTranscriptions(SWIsltsHand hLts, 
                                                               const char *text, 
                                                               SWIsltsTranscription **ppTranscriptions,
                                                               int *pnNbrOfTranscriptions)
{
  SWIsltsResult          nRes = SWIsltsSuccess;
  char                  PHONE_STRING[MAX_PRON_LEN][MAX_PHONE_LEN];
  char                * phone_string[MAX_PRON_LEN];   
  SLTS_Engine          * pEng = (SLTS_Engine *)hLts;
  int                    i;
  int                    num_phones = 0;
  SWIsltsTranscription * pTranscription = NULL;
  int                    nNbrOfTranscriptions = 0;
  int                  * pnNbrOfTranscriptionsToSave = NULL;
  LCHAR                * pBlock = NULL;

  for( i = 0; i < MAX_PRON_LEN; i++ ) {
    phone_string[i] = PHONE_STRING[i]; 
  }

  nRes = SWIsltsTextToPhone(hLts, text, phone_string, &num_phones, MAX_PRON_LEN);
  if( nRes != SWIsltsSuccess ) {
    PLogError(L("SWIsltsTextToPhone( ) fails with return code %d\n"), nRes);
    goto CLEAN_UP;
  }
#if DEBUG
  pfprintf(PSTDOUT,"number of phones: %d\n ", num_phones);
  for( i = 0; i < num_phones; i++ ) {
    pfprintf(PSTDOUT,"%s ", phone_string[i]);
  }
  pfprintf(PSTDOUT,"\n ");
#endif

  /* only one transcription available from seti */
  nNbrOfTranscriptions = 1;
  pBlock = (LCHAR *)CALLOC(sizeof(int) + nNbrOfTranscriptions * sizeof(SWIsltsTranscription), sizeof(LCHAR), MTAG);
  if (pBlock == NULL) {
    PLogError(L("SWISLTS_OUT_OF_MEMORY"));
    nRes = SWIsltsErrAllocResource;
    goto CLEAN_UP;
  }
  pnNbrOfTranscriptionsToSave = (int *)pBlock;
  pTranscription = (SWIsltsTranscription *)(pBlock + sizeof(int));

  *ppTranscriptions = pTranscription;
  *pnNbrOfTranscriptions = *pnNbrOfTranscriptionsToSave = nNbrOfTranscriptions;

  /* extra +1 for double-null at the end */
  pTranscription->pBuffer = MALLOC(MAX_PHONE_LEN * (num_phones + 1+1), MTAG);
  if( pTranscription->pBuffer == NULL ) {
    PLogError(L("SWISLTS_OUT_OF_MEMORY"));
    nRes = SWIsltsErrAllocResource;
    goto CLEAN_UP;
  }

  nRes = GetPhoneStr(pEng, phone_string, num_phones, (char *)pTranscription->pBuffer, &(pTranscription->nSizeOfBuffer));
  if( nRes != SWIsltsSuccess ) {
    PLogError(L("SWIsltsInternalErr: GetPhoneStr( ) fails with return code %d\n"), nRes);
    goto CLEAN_UP;
  }

  return SWIsltsSuccess;

 CLEAN_UP:

  *ppTranscriptions = NULL;
  *pnNbrOfTranscriptions = 0;  

  for( i = 0; i < nNbrOfTranscriptions; i++ ) {
    if(pTranscription[i].pBuffer) {
      FREE(pTranscription[i].pBuffer);
    }
  }
  FREE(pTranscription);

  return nRes;
}


SWISLTS_FNEXPORT SWIsltsResult SWIsltsG2PFreeWordTranscriptions(SWIsltsHand hLts, 
                                                                SWIsltsTranscription *pTranscriptions)
{
  SWIsltsResult          nRes = SWIsltsSuccess;
  int                    nNbrOfTranscriptions;
  int                    i;
  LCHAR                * pBuffer = NULL;

  if( pTranscriptions == NULL ) {
    return SWIsltsInvalidParam;
  }

  pBuffer = ((LCHAR *)pTranscriptions - sizeof(int));
  nNbrOfTranscriptions = (int)*pBuffer;

  for( i = 0; i < nNbrOfTranscriptions; i++ ) {
    if( pTranscriptions[i].pBuffer ) {
      FREE(pTranscriptions[i].pBuffer);
    }
  }
  FREE(pBuffer);

  return nRes;
}

static SWIsltsResult GetPhoneStr(SLTS_Engine *pEng, char *apszPhones[], int num_phones, char *pszPhoneStr, size_t *len)
{
  int                    i, j;
  int                    nFound;
  SWIsltsResult          nRes = SWIsltsSuccess;
  const char           * pszLastPhone = NULL;
  
  *pszPhoneStr = '\0';

  for( i = 0; i < num_phones; i++ ) {
    nFound = 0;
    for ( j = 0; j <  g_numPhones && nFound == 0; j++ ) {
      if( strcmp(apszPhones[i], g_aPhoneMap[j].src) == 0 ) {
        nFound = 1;
        if( strcmp(g_aPhoneMap[j].des, INF_SILENCE_OPTIONAL) == 0 ) {
          if( *pszPhoneStr != '\0' && strcmp(pszLastPhone, INF_SILENCE_OPTIONAL) != 0 ) {
            strcat(pszPhoneStr, g_aPhoneMap[j].des);
          }
        }
        else if( g_aPhoneMap[j].des != '\0' ) {
          strcat(pszPhoneStr, g_aPhoneMap[j].des);
        }
        pszLastPhone = g_aPhoneMap[j].des;
      }
    }
    if( nFound == 0 ) {
      strcat(pszPhoneStr, apszPhones[i]);
      pszLastPhone = apszPhones[i];
    }
  }

  *len = strlen(pszPhoneStr) + 1;
  // add the double-null per SREC/Vocon convention
  pszPhoneStr[ *len] = 0;

  return nRes;
}