// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright © Microsoft Corporation. All rights reserved

/******************************************************************************
*   srengalt.h 
*       This file contains the implementation of the CSrEngineAlternates class.
*       This implements the interface ISpSRAlternates.
*       When an app calls GetAlternates or Commit on a result, SAPI will
*       look for the AlternatesCLSID field in the engine object token, and
*       create this object.
*       It will then call the methods here, passing the relevant results information.
*       This includes any serialized data the main engine has returned with
*       the results to allow alternates to be generated off-line.
******************************************************************************/

#include "stdafx.h"
#include "srengalt.h"

/****************************************************************************
* CSrEngineAlternates::GetAlternates *
*---------------------------*
*   Description:
*       This method generates alternate phrases when SAPI requests them.
*       The method reads the extra information returned from the SR engine
*       inside the results object. This gets returned
*       as alternates phrases to SAPI. In addition this method can find a private interface to engine
*       from the context and query the engine for additional result information.
*       
*       The engine must have returned info serialised within the results object
*       allowing us to produce alternatives. This is the case in the sample engine.
*
*   Return: 
*       S_OK
*       FAILED(hr)
*****************************************************************************/   
STDMETHODIMP CSrEngineAlternates::GetAlternates(SPPHRASEALTREQUEST *pAltRequest,
    SPPHRASEALT **ppAlts, ULONG *pcAlts)
{
    HRESULT hr = S_OK;

    // We will just produce one alternate
    // This will replace the words in the original phrase on a one-to-one basis
    // Real alternates may have different numbers of words to original,
    // and replace only parts of the original.
    *pcAlts = 1;
    *ppAlts = (SPPHRASEALT *)::CoTaskMemAlloc(sizeof(SPPHRASEALT));

    if( *ppAlts == NULL)
    {
        return E_OUTOFMEMORY;
    }

    (*ppAlts)[0].ulStartElementInParent = pAltRequest->ulStartElement;
    (*ppAlts)[0].cElementsInParent = pAltRequest->cElements;
    (*ppAlts)[0].cElementsInAlternate = pAltRequest->cElements;
    (*ppAlts)[0].pvAltExtra = NULL;
    (*ppAlts)[0].cbAltExtra = 0;

    // Create and fill an SPPHRASE structure
    SPPHRASE phrase;
    memset(&phrase, 0, sizeof(phrase));
    phrase.cbSize = sizeof(phrase);
    // An alternates analyzer should really query its SR engine to find it's lang id. 
    //  For the sample engine we will just hard-code this
    phrase.LangID = 1033; 
    
    WCHAR *pAlts = (WCHAR *) pAltRequest->pvResultExtra;
    ULONG nAltChars = pAltRequest->cbResultExtra / sizeof(WCHAR);
    ULONG nWord = 0;

    // Count words in alternate data
    ULONG i;
    for(i = 0; i < nAltChars; i++)
    {
        if(iswspace(pAlts[i]) || pAlts[i] == '\0')
        {
            nWord++;
        }
    }

    // Allocate elements
    SPPHRASEELEMENT* pElements = (SPPHRASEELEMENT*)_malloca(sizeof(SPPHRASEELEMENT) * nWord);
    memset(pElements, 0, sizeof(SPPHRASEELEMENT)*nWord);

    // Add words in alternate to elements
    ULONG cW = 0;
    ULONG cWord = 0;
    for(i = 0; i < nAltChars && cWord < nWord; i++)
    {
        if(iswspace(pAlts[i]) || pAlts[i] == '\0')
        {
            pElements[cWord].bDisplayAttributes = SPAF_ONE_TRAILING_SPACE;
            WCHAR *pszWord = (WCHAR *)_malloca(sizeof(WCHAR) * (i - cW + 1));
            wcsncpy_s(pszWord, i - cW + 1, &pAlts[cW], i - cW);
            pszWord[i - cW] = '\0';
            pElements[cWord].pszDisplayText =  pszWord;

            cW = i + 1;
            cWord++;
        }
    }

    // Add elements to phrase
    phrase.Rule.ulCountOfElements = cWord;
    phrase.pElements = pElements;

    // Make phrase builder and add phrase info
    CComPtr<ISpPhraseBuilder> cpBuilder;
    hr = cpBuilder.CoCreateInstance(CLSID_SpPhraseBuilder);
    if(SUCCEEDED(hr))
    {
        hr = cpBuilder->InitFromPhrase(&phrase);
    }
    if(SUCCEEDED(hr))
    {
        (*ppAlts)[0].pPhrase = cpBuilder;
        (*ppAlts)[0].pPhrase->AddRef();
    }

    // Alternates class can also query enginethrough private interface
    CComPtr<ISampleSRExtension> m_cpExt;
    hr = pAltRequest->pRecoContext->QueryInterface(&m_cpExt);
    if(SUCCEEDED(hr))
    {
        hr = m_cpExt->ExamplePrivateEngineCall();
    }

    for(i = 0; i < cWord; i++)
    {
        _freea((void*)pElements[i].pszDisplayText);
    }
    _freea(pElements);
    
    if (FAILED(hr))
    {
        ::CoTaskMemFree(*ppAlts);
    }

    return hr;
}
    
/****************************************************************************
* CSrEngineAlternates::Commit *
*---------------------------*
*   Description:
*       Here the engine could use the information from the application 
*       about which was the correct alternate in order to do supervised
*       adaptation.
*       In this sample we do nothing here.
*   Return: 
*       S_OK
*****************************************************************************/   
STDMETHODIMP CSrEngineAlternates::Commit(SPPHRASEALTREQUEST *pAltRequest,
    SPPHRASEALT *pAlt, void **ppvResultExtra, ULONG *pcbResultExtra)
{
    return S_OK;
}
