/******************************Module*Header*******************************\
* Module Name: natfreq.c
*
* At train time we want to dynamically load the natual frequency information
* from the data.  This module contains the code and data structures to do
* this.
*
* The code contained is train time only code, it should not be included
* to build hwxXXX.dll
*
\**************************************************************************/

#include <stdlib.h>
#include "common.h"

typedef struct tagFREQ_INFO
{
	wchar_t		dch;
	DWORD		frequency;
} FREQ_INFO;

static DWORD		gtotalSamples;
static DWORD		gmaxNaturalFequency;
static DWORD		gFreqInfoSize;
static FREQ_INFO	*gpaNaturalFrequencies = NULL;

/******************************Public*Routine******************************\
* LoadNatualFrequency
*
* Loads the natual frequency information from a text file generated by mkuni.exe.
*
\**************************************************************************/

BOOL LoadNatualFrequency(wchar_t *pFile, FILE *pFileLog)
{
    FILE *fp;
    DWORD iTemp,iTemp1,iTemp2;
    int iRet;
	wchar_t	aFileInput[256];

	// Open frequency file.
    fp = UtilOpen(pFile, L"r", aFileInput, 256);
    if (fp == NULL) {
        fwprintf(pFileLog, L"FAILED to load %s !!!\n", pFile);
		return FALSE;
    } else {
        fwprintf(pFileLog, L"Loaded frequency file: %s\n", aFileInput);
	}

	// Get count of labels.
    iRet = fwscanf(fp, L"%d %d %d\n", &gtotalSamples,&gmaxNaturalFequency,&gFreqInfoSize);
    if (iRet != 3) {
        fwprintf(pFileLog, L"Error reading %s\n", pFile);
        return FALSE;
    }

	// Allocate memory for frequency table.
    gpaNaturalFrequencies	= malloc(gFreqInfoSize * sizeof(FREQ_INFO));
	if (!gpaNaturalFrequencies) {
        fwprintf(pFileLog, L"Out of memory reading %s\n", pFile);
        return FALSE;
    }

    for (iTemp = 0; iTemp < gFreqInfoSize; iTemp++) {
        iRet = fwscanf(fp, L"%x %d\n", &iTemp1, &iTemp2);
        if (iRet != 2) {
			fwprintf(pFileLog, L"Error reading %s\n", pFile);
			return FALSE;
        }

        gpaNaturalFrequencies[iTemp].dch	= (wchar_t)iTemp1;
        gpaNaturalFrequencies[iTemp].frequency	= iTemp2;
    }

    fclose(fp);

    if (pFileLog) {
        fwprintf(pFileLog, L"Loaded natual frequency: first %04X %d last %04X %d\n", 
			gpaNaturalFrequencies[0].dch,
			gpaNaturalFrequencies[0].frequency,
			gpaNaturalFrequencies[gFreqInfoSize - 1].dch,
			gpaNaturalFrequencies[gFreqInfoSize - 1].frequency);
    }

	return TRUE;
}

/******************************Public*Routine******************************\
* DenseCodeNumberOfSamples
*
* Returns the number frequency of a dense code
*
\**************************************************************************/
DWORD	DenseCodeNumberOfSamples(wchar_t wCurrent)
{
    int     iMin;
    int     iMax;
    int     iNew;
    WORD    wNew;

    iMin = 0;
    iMax = gFreqInfoSize - 1;

    while (iMin < iMax)
    {
        iNew = (iMin + iMax) / 2;

        wNew = gpaNaturalFrequencies[iNew].dch;

        if (wNew < wCurrent) {
            iMin = iNew + 1;
            continue;
        } else if (wNew > wCurrent) {
            iMax = iNew - 1;
            continue;
        }

        return	gpaNaturalFrequencies[iNew].frequency;
    }

    return gpaNaturalFrequencies[iMin].dch == wCurrent ? gpaNaturalFrequencies[iMin].frequency : 0;
}

BOOL GetFrequencyInfo(DWORD index,wchar_t * pdch,DWORD * pFrequency)
{
	*pdch = 0xFFFF;
	*pFrequency = 0;
	if ( !gpaNaturalFrequencies || index >= gFreqInfoSize )
	{
		return FALSE;
	}
	*pdch = gpaNaturalFrequencies[index].dch;
	*pFrequency = gpaNaturalFrequencies[index].frequency;
	return TRUE;
}

DWORD	GetTotalDenseCodeSamples()
{
	return gtotalSamples;
}

DWORD	GetMaxDenseCodeSamples()
{
	return gmaxNaturalFequency;
}

DWORD	GetNatualFrequencyTableSize()
{
	return gFreqInfoSize;
}

void UnloadNatualFrequency()
{
	if ( gpaNaturalFrequencies )
	{
		free(gpaNaturalFrequencies);
	}
}

// Look up the "natural" frequency of a (possibly folded) code, and return -1 if it
// is not found in the table.
int	DenseCodeNumberOfSamplesInternal(wchar_t wCurrent)
{
    int     iMin;
    int     iMax;
    int     iNew;
    WORD    wNew;

    iMin = 0;
    iMax = gFreqInfoSize - 1;

    while (iMin < iMax)
    {
        iNew = (iMin + iMax) / 2;

        wNew = gpaNaturalFrequencies[iNew].dch;

        if (wNew < wCurrent) {
            iMin = iNew + 1;
            continue;
        } else if (wNew > wCurrent) {
            iMax = iNew - 1;
            continue;
        }

        return	gpaNaturalFrequencies[iNew].frequency;
    }

    return gpaNaturalFrequencies[iMin].dch == wCurrent ? gpaNaturalFrequencies[iMin].frequency : -1;
}

/******************************Public*Routine******************************\
* DenseCodeNumberOfSamplesFolded
*
* Returns the natural frequency of a dense code.  If the dense code is a folded
* code, then the counts for each component of the folding set are added.
*
\**************************************************************************/
DWORD
DenseCodeNumberOfSamplesFolded(LOCRUN_INFO *pLocRunInfo, wchar_t dch)
{
	// First try to just look up the code in the table.
	int	c = DenseCodeNumberOfSamplesInternal(dch);

	// If we didn't find it, the cause may be that dch is a folded code,
	// and the table has not been updated with folded codes yet.
	if (c == -1 && LocRunIsFoldedCode(pLocRunInfo, dch)) {
		// If it is a folded code, look up the folding set
		wchar_t *pFoldingSet = LocRunFolded2FoldingSet(pLocRunInfo, dch);
		int i;

		// Run through the folding set, adding counts
		c = 0;
		for (i = 0; i < LOCRUN_FOLD_MAX_ALTERNATES && pFoldingSet[i] != 0; i++) 
			c += DenseCodeNumberOfSamples(pFoldingSet[i]);
	}

	// Return
	return __max(c, 0);
}
