/******************************************************************************

Copyright (c) 2001 Microsoft Corporation

Module Name:
    FTSWrap.cpp

Abstract:
    Implementation of SearchEngine::WrapperFTS

Revision History:
    Ghim-Sim Chua   (gschua)  06/01/2000
        created

******************************************************************************/

#include "stdafx.h"

#include "msitstg.h"
#include "itrs.h"
#include "itdb.h"
#include "iterror.h"
#include "itgroup.h"
#include "itpropl.h"
#include "itquery.h"
#include "itcc.h"
#include "ftsobj.h"
#include "fs.h"

////////////////////////////////////////////////////////////////////////////////

static bool local_ExpandURL( /*[in ]*/ Taxonomy::Updater&  updater  ,
                             /*[in ]*/ const MPC::wstring& strSrc   ,
                             /*[in ]*/ LPCWSTR             szPrefix ,
                             /*[out]*/ MPC::wstring&       strDst   )
{
    strDst  = szPrefix;
    strDst += strSrc;

    if(SUCCEEDED(updater.ExpandURL( strDst )))
    {
        if(MPC::FileSystemObject::IsFile( strDst.c_str() )) return true;
    }

    return false;
}

static void local_GenerateFullURL( /*[in ]*/ Taxonomy::Updater&  updater ,
                                   /*[in ]*/ const MPC::wstring& strSrc  ,
                                   /*[out]*/ MPC::wstring&       strDst  )
{
    if(strSrc.size())
    {
        if(local_ExpandURL( updater, strSrc, L"%HELP_LOCATION%\\", strDst )) return;
        if(local_ExpandURL( updater, strSrc, L"%WINDIR%\\Help\\" , strDst )) return;
    }

    strDst = L"";
}

////////////////////////////////////////////////////////////////////////////////


SearchEngine::WrapperFTS::WrapperFTS()
{
    // SEARCH_OBJECT_LIST    m_objects;
    // SEARCH_RESULT_SET     m_results;
    // SEARCH_RESULT_SORTSET m_resultsSorted;

    MPC::LocalizeString( IDS_HELPSVC_SEMGR_OWNER   , m_bstrOwner      , /*fMUI*/true );
    MPC::LocalizeString( IDS_HELPSVC_SEMGR_FTS_NAME, m_bstrName       , /*fMUI*/true );
    MPC::LocalizeString( IDS_HELPSVC_SEMGR_FTS_DESC, m_bstrDescription, /*fMUI*/true );

    m_bstrHelpURL = L"hcp://system/blurbs/ftshelp.htm";
    m_bstrID      = L"9A22481C-1795-46f3-8CCA-7D78E9E54112";
}

SearchEngine::WrapperFTS::~WrapperFTS()
{
    Thread_Wait();

    ReleaseAll();
}

HRESULT SearchEngine::WrapperFTS::GetParamDefinition( /*[out]*/ const ParamItem_Definition*& lst, /*[out]*/ int& len )
{
    static const ParamItem_Definition c_lst[] =
    {
        { PARAM_BOOL, VARIANT_FALSE, VARIANT_TRUE , L"TITLEONLY", IDS_HELPSVC_TITLE_ONLY, NULL, L"false" },
        { PARAM_BOOL, VARIANT_FALSE, VARIANT_TRUE , L"STEMMING" , IDS_HELPSVC_STEMMING  , NULL, L"false" },
        { PARAM_BOOL, VARIANT_FALSE, VARIANT_FALSE, L"UI_BULLET", 0                     , NULL, L"false" },
    };

    lst =           c_lst;
    len = ARRAYSIZE(c_lst);

    return S_OK;
}

////////////////////////////////////////////////////////////////////////////////

void SearchEngine::WrapperFTS::ReleaseAll()
{
    ReleaseSearchResults();

    m_objects.clear();
}

void SearchEngine::WrapperFTS::ReleaseSearchResults()
{
    m_results      .clear();
    m_resultsSorted.clear();
}

////////////////////////////////////////////////////////////////////////////////

HRESULT SearchEngine::WrapperFTS::ExecQuery()
{
    __HCP_FUNC_ENTRY( "SearchEngine::WrapperFTS::ExecQuery" );

    HRESULT          hr;
    long             lCount;
    long             lIndex = 0;
    MPC::WStringSet  wordsSet;
    MPC::WStringList wordsList;
    
    if(m_bEnabled)
    {
        CComBSTR bstrName;
        VARIANT* v;
        bool     bTitle;
        bool     bStemming;

        __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::LocalizeString( IDS_HELPSVC_SEMGR_FTS_NAME, bstrName, /*fMUI*/true ));

        if(m_bstrQueryString.Length() == 0)
        {
            __MPC_SET_WIN32_ERROR_AND_EXIT(hr, ERROR_INVALID_DATA);
        }

      
        __MPC_EXIT_IF_METHOD_FAILS(hr, Initialize());
        

        //
        // Check if search in titles only
        //
        v = GetParamInternal( L"TITLEONLY" );
        bTitle = (v && v->vt == VT_BOOL && v->boolVal) ? true : false;

        //
        // Check if stemming is turned on
        //
        v = GetParamInternal( L"STEMMING" );
        bStemming = (v && v->vt == VT_BOOL && v->boolVal) ? true : false;

		//
		// Compute the code page each query, as the user may have changed the language using MUI between queries
		//
		UINT	cp = CP_ACP;
		WCHAR wchLocale[10];
		if (GetLocaleInfo(m_ths.GetLanguage(), LOCALE_IDEFAULTANSICODEPAGE, wchLocale, ARRAYSIZE(wchLocale)))
		   cp = wcstoul(wchLocale, NULL, 10);
		else
		   cp = CP_ACP;
        
		//
        // Execute the queries
        //
        for(SEARCH_OBJECT_LIST_ITER it = m_objects.begin(); (it != m_objects.end()) && (!Thread_IsAborted()); it++)
        {
            (void)it->Query( m_bstrQueryString, bTitle, bStemming, m_results, wordsSet, cp );
        }

		if(Thread_IsAborted()) __MPC_SET_ERROR_AND_EXIT(hr, E_ABORT);


        for(SEARCH_RESULT_SET_ITER it2 = m_results.begin(); (it2 != m_results.end()) && (!Thread_IsAborted()); it2++)
        {
            m_resultsSorted.insert( &(*it2) );
        }

		if(Thread_IsAborted()) __MPC_SET_ERROR_AND_EXIT(hr, E_ABORT);

        //
        // Copy the Highlight words from set to list
        //
        for(MPC::WStringSetIter itString = wordsSet.begin(); (itString != wordsSet.end()) && (!Thread_IsAborted()); itString++)
        {
            wordsList.push_back( *itString );
        }

		if(Thread_IsAborted()) __MPC_SET_ERROR_AND_EXIT(hr, E_ABORT);

        //
        // Store Highlight words in safe array
        //
        __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::ConvertListToSafeArray( wordsList, m_vKeywords, VT_VARIANT ));
    }

    hr = S_OK;

    __MPC_FUNC_CLEANUP;

    Thread_Abort();

    //
    // Call the SearchManager's OnComplete
    //
    (void)m_pSEMgr->WrapperComplete( hr, this );

    __MPC_FUNC_EXIT(hr);
}

STDMETHODIMP SearchEngine::WrapperFTS::get_SearchTerms( /*[out, retval]*/ VARIANT *pVal )
{
    MPC::SmartLock<_ThreadModel> lock( this );

    return ::VariantCopy( pVal, &m_vKeywords );
}

HRESULT SearchEngine::WrapperFTS::Initialize()
{
    __HCP_FUNC_ENTRY( "SearchEngine::WrapperFTS::Initialize" );

    HRESULT                      hr;
    MPC::SmartLock<_ThreadModel> lock( this );
    Taxonomy::Settings           ts( m_ths );
    JetBlue::SessionHandle       handle;
    JetBlue::Database*           db;
    Taxonomy::Updater            updater;
    Taxonomy::RS_FullTextSearch* rsFTS;
    Taxonomy::RS_Scope*          rsSCOPE;
    long                         ID_scope = -1;
    bool                         fFound;


    //
    // Clean previous search results.
    //
    //ReleaseSearchResults();
    ReleaseAll();


    __MPC_EXIT_IF_METHOD_FAILS(hr, ts     .GetDatabase      ( handle,  db, /*fReadOnly*/true ));
    __MPC_EXIT_IF_METHOD_FAILS(hr, updater.Init             ( ts    ,  db                    ));
    __MPC_EXIT_IF_METHOD_FAILS(hr, updater.GetFullTextSearch(         &rsFTS                 ));
    __MPC_EXIT_IF_METHOD_FAILS(hr, updater.GetScope         (         &rsSCOPE               ));

    ////////////////////////////////////////

    if(m_bstrScope)
    {
        __MPC_EXIT_IF_METHOD_FAILS(hr, rsSCOPE->Seek_ByID( m_bstrScope, &fFound ));
        if(fFound)
        {
            ID_scope = rsSCOPE->m_ID_scope;
        }
    }

    if(ID_scope == -1)
    {
        __MPC_EXIT_IF_METHOD_FAILS(hr, rsSCOPE->Seek_ByID( L"<SYSTEM>", &fFound ));
        if(fFound)
        {
            ID_scope = rsSCOPE->m_ID_scope;
        }
    }

    ////////////////////////////////////////

    //
    // Create the search objects.
    //
    __MPC_EXIT_IF_METHOD_FAILS(hr, rsFTS->Move( 0, JET_MoveFirst, &fFound ));
    while(fFound)
    {
        if(rsFTS->m_ID_scope == ID_scope)
        {
            CFTSObject&         obj = *(m_objects.insert( m_objects.end() ));
            CFTSObject::Config& cfg = obj.GetConfig();

            local_GenerateFullURL( updater, rsFTS->m_strCHM, cfg.m_strCHMFilename );
            local_GenerateFullURL( updater, rsFTS->m_strCHQ, cfg.m_strCHQFilename );

            if(cfg.m_strCHQFilename.size())
            {
                cfg.m_fCombined = true;
            }
        }

        __MPC_EXIT_IF_METHOD_FAILS(hr, rsFTS->Move( 0, JET_MoveNext, &fFound ));
    }

    hr = S_OK;


    __MPC_FUNC_CLEANUP;

    __MPC_FUNC_EXIT(hr);
}

////////////////////////////////////////////////////////////////////////////////

STDMETHODIMP SearchEngine::WrapperFTS::Result( /*[in]*/ long lStart, /*[in]*/ long lEnd, /*[out,retval]*/ IPCHCollection* *ppC )
{
    __HCP_FUNC_ENTRY( "SearchEngine::WrapperFTS::Result" );

    HRESULT                      hr;
    MPC::SmartLock<_ThreadModel> lock( this );
    CComPtr<CPCHCollection>      pColl;

    __MPC_PARAMCHECK_BEGIN(hr)
        __MPC_PARAMCHECK_POINTER_AND_SET(ppC,NULL);
    __MPC_PARAMCHECK_END();

    //
    // Create the Enumerator and fill it with jobs.
    //
    __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::CreateInstance( &pColl ));

    if(m_bEnabled)
    {
        long lCount = 0;

        for(SEARCH_RESULT_SORTSET_ITER it = m_resultsSorted.begin(); (lCount < m_lNumResult) && (it != m_resultsSorted.end()); it++, lCount++)
        {
            //
            // if there is a URL
            //
            if(lCount >= lStart &&
               lCount <  lEnd    )
            {
                CComPtr<ResultItem> pRIObj;

                //
                // Create the item to be inserted into the list
                //
                __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::CreateInstance( &pRIObj ));

                {
                    ResultItem_Data& data = pRIObj->Data();
                    SEARCH_RESULT*   res  = *it;

                    data.m_bstrTitle    = res->bstrTopicName;
                    data.m_bstrLocation = res->bstrLocation;
                    
                    data.m_bstrURI      = res->bstrTopicURL;
                }

                //
                // Add to enumerator
                //
                __MPC_EXIT_IF_METHOD_FAILS(hr, pColl->AddItem( pRIObj ));
            }
        }
    }

    __MPC_EXIT_IF_METHOD_FAILS(hr, pColl.QueryInterface( ppC ));

    hr = S_OK;


    __MPC_FUNC_CLEANUP;

    __MPC_FUNC_EXIT(hr);
}

////////////////////////////////////////////////////////////////////////////////

STDMETHODIMP SearchEngine::WrapperFTS::AbortQuery()
{
    //
    // Abort any threads still running
    //
    Thread_Abort();

    return S_OK;
}


STDMETHODIMP SearchEngine::WrapperFTS::ExecAsyncQuery()
{
    __HCP_FUNC_ENTRY( "SearchEngine::WrapperFTS::ExecAsyncQuery" );

    HRESULT hr;

    //
    // Create a thread to execute the query
    //
    __MPC_EXIT_IF_METHOD_FAILS(hr, Thread_Start( this, ExecQuery, NULL ));

    hr = S_OK;


    __MPC_FUNC_CLEANUP;

    __MPC_FUNC_EXIT(hr);
}

////////////////////////////////////////////////////////////////////////////////

HRESULT SearchEngine::WrapperItem__Create_FullTextSearch( /*[out]*/ CComPtr<IPCHSEWrapperInternal>& pVal )
{
    __HCP_FUNC_ENTRY( "CPCHSEWrapperItem__Create_FullTextSearch" );

    HRESULT                           hr;
    CComPtr<SearchEngine::WrapperFTS> pFTS;


    __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::CreateInstance( &pFTS ));

    pVal = pFTS;

    hr = S_OK;


    __HCP_FUNC_CLEANUP;

    __HCP_FUNC_EXIT(hr);
}
