#ifndef _INC_MLUISUPP
#define _INC_MLUISUPP

#include <shlwapi.h>
#include <shlwapip.h>

#ifdef __cplusplus
extern "C"
{
#endif

//+------------------------------------------------------------------
// Multilang Pluggable UI support
// inline functions defs (to centralize code)
//+------------------------------------------------------------------

#ifdef UNICODE
#define MLLoadString            MLLoadStringW
#define MLLoadShellLangString   MLLoadShellLangStringW
#define MLBuildResURLWrap       MLBuildResURLWrapW
#define MLLoadResources         MLLoadResourcesW
#define SHHtmlHelpOnDemandWrap  SHHtmlHelpOnDemandWrapW
#define SHWinHelpOnDemandWrap   SHWinHelpOnDemandWrapW
#else
#define MLLoadString            MLLoadStringA
#define MLLoadShellLangString   MLLoadShellLangStringA
#define MLBuildResURLWrap       MLBuildResURLWrapA
#define MLLoadResources         MLLoadResourcesA
#define SHHtmlHelpOnDemandWrap  SHHtmlHelpOnDemandWrapA
#define SHWinHelpOnDemandWrap   SHWinHelpOnDemandWrapA
#endif

void        MLFreeResources(HINSTANCE hinstParent);
HINSTANCE   MLGetHinst();
HINSTANCE   MLLoadShellLangResources();

#ifdef MLUI_MESSAGEBOX
int         MLShellMessageBox(HWND hWnd, LPCTSTR pszMsg, LPCTSTR pszTitle, UINT fuStyle, ...);
#endif

//
// The following should be both A and W suffixed
//

int         MLLoadStringA(UINT id, LPSTR sz, UINT cchMax);
int         MLLoadStringW(UINT id, LPWSTR sz, UINT cchMax);

int         MLLoadShellLangStringA(UINT id, LPSTR sz, UINT cchMax);
int         MLLoadShellLangStringW(UINT id, LPWSTR sz, UINT cchMax);

HRESULT     MLBuildResURLWrapA(LPSTR    pszLibFile,
                               HMODULE  hModule,
                               DWORD    dwCrossCodePage,
                               LPSTR    pszResName,
                               LPSTR    pszResURL,
                               int      nBufSize,
                               LPSTR    pszParentDll);

HRESULT     MLBuildResURLWrapW(LPWSTR   pszLibFile,
                               HMODULE  hModule,
                               DWORD    dwCrossCodePage,
                               LPWSTR   pszResName,
                               LPWSTR   pszResURL,
                               int      nBufSize,
                               LPWSTR   pszParentDll);

void        MLLoadResourcesA(HINSTANCE hinstParent, LPSTR pszLocResDll);
void        MLLoadResourcesW(HINSTANCE hinstParent, LPWSTR pszLocResDll);

HWND        SHHtmlHelpOnDemandWrapA(HWND hwndCaller, LPCSTR pszFile, UINT uCommand, DWORD_PTR dwData, DWORD dwCrossCodePage);
HWND        SHHtmlHelpOnDemandWrapW(HWND hwndCaller, LPCWSTR pszFile, UINT uCommand, DWORD_PTR dwData, DWORD dwCrossCodePage);

BOOL        SHWinHelpOnDemandWrapA(HWND hwndCaller, LPCSTR lpszHelp, UINT uCommand, DWORD_PTR dwData);
BOOL        SHWinHelpOnDemandWrapW(HWND hwndCaller, LPCWSTR lpszHelp, UINT uCommand, DWORD_PTR dwData);

//
// End of: The following should be both A and W suffixed
//

#ifdef MLUI_INIT

// WARNING: do not attempt to access any of these members directly
// these members may not be initialized until appropriate accessors
// are called, for example hinstLocRes won't be intialized until
// you call MLGetHinst()... so just call the accessor.
struct tagMLUI_INFO
{
    HINSTANCE   hinstLocRes;
    HINSTANCE   hinstParent;
    WCHAR       szLocResDll[MAX_PATH];
    DWORD       dwCrossCodePage;
} g_mluiInfo;


// REARCHITECT: These aren't thread safe... Do they need to be?
//
void MLLoadResourcesA(HINSTANCE hinstParent, LPSTR pszLocResDll)
{
#ifdef RIP
    RIP(hinstParent != NULL);
    RIP(pszLocResDll != NULL);
#endif

    if (g_mluiInfo.hinstLocRes == NULL)
    {
#ifdef MLUI_SUPPORT
        // plugUI: resource dll == ?
        // resource dll must be dynamically determined and loaded.
        // but we are NOT allowed to LoadLibrary during process attach.
        // therefore we cache the info we need and load later when
        // the first resource is requested.
        SHAnsiToUnicode(pszLocResDll, g_mluiInfo.szLocResDll, sizeof(g_mluiInfo.szLocResDll)/sizeof(g_mluiInfo.szLocResDll[0]));
        g_mluiInfo.hinstParent = hinstParent;
        g_mluiInfo.dwCrossCodePage = ML_CROSSCODEPAGE;
#else
        // non-plugUI: resource dll == parent dll
        g_mluiInfo.hinstLocRes = hinstParent;
#endif
    }
}

void MLLoadResourcesW(HINSTANCE hinstParent, LPWSTR pszLocResDll)
{
#ifdef RIP
    RIP(hinstParent != NULL);
    RIP(pszLocResDll != NULL);
#endif

    if (g_mluiInfo.hinstLocRes == NULL)
    {
#ifdef MLUI_SUPPORT
        // plugUI: resource dll == ?
        // resource dll must be dynamically determined and loaded.
        // but we are NOT allowed to LoadLibrary during process attach.
        // therefore we cache the info we need and load later when
        // the first resource is requested.
        StrCpyNW(g_mluiInfo.szLocResDll, pszLocResDll, sizeof(g_mluiInfo.szLocResDll)/sizeof(g_mluiInfo.szLocResDll[0]));
        g_mluiInfo.hinstParent = hinstParent;
        g_mluiInfo.dwCrossCodePage = ML_CROSSCODEPAGE;
#else
        // non-plugUI: resource dll == parent dll
        g_mluiInfo.hinstLocRes = hinstParent;
#endif
    }
}

void
MLFreeResources(HINSTANCE hinstParent)
{
    if (g_mluiInfo.hinstLocRes != NULL &&
        g_mluiInfo.hinstLocRes != hinstParent)
    {
        MLClearMLHInstance(g_mluiInfo.hinstLocRes);
        g_mluiInfo.hinstLocRes = NULL;
    }
}

// this is a private internal helper.
// don't you dare call it from anywhere except at
// the beginning of new ML* functions in this file
__inline void
_MLResAssure()
{
#ifdef MLUI_SUPPORT
    if(g_mluiInfo.hinstLocRes == NULL)
    {
        g_mluiInfo.hinstLocRes = MLLoadLibraryW(g_mluiInfo.szLocResDll,
                                               g_mluiInfo.hinstParent,
                                               g_mluiInfo.dwCrossCodePage);

        // we're guaranteed to at least have resources in the install language
        ASSERT(g_mluiInfo.hinstLocRes != NULL);
    }
#endif
}

int
MLLoadStringA(UINT id, LPSTR sz, UINT cchMax)
{
    _MLResAssure();
    return LoadStringA(g_mluiInfo.hinstLocRes, id, sz, cchMax);
}

int
MLLoadStringW(UINT id, LPWSTR sz, UINT cchMax)
{
    _MLResAssure();
    return LoadStringWrapW(g_mluiInfo.hinstLocRes, id, sz, cchMax);
}

int
MLLoadShellLangStringA(UINT id, LPSTR sz, UINT cchMax)
{
    HINSTANCE   hinstShellLangRes;
    int         nRet;

    hinstShellLangRes = MLLoadShellLangResources();
    
    nRet = LoadStringA(hinstShellLangRes, id, sz, cchMax);

    MLFreeLibrary(hinstShellLangRes);

    return nRet;
}

int
MLLoadShellLangStringW(UINT id, LPWSTR sz, UINT cchMax)
{
    HINSTANCE   hinstShellLangRes;
    int         nRet;

    hinstShellLangRes = MLLoadShellLangResources();
    
    nRet = LoadStringWrapW(hinstShellLangRes, id, sz, cchMax);

    MLFreeLibrary(hinstShellLangRes);

    return nRet;
}

HINSTANCE
MLGetHinst()
{
    _MLResAssure();
    return g_mluiInfo.hinstLocRes;
}

HINSTANCE
MLLoadShellLangResources()
{
    HINSTANCE hinst;
    
    hinst = MLLoadLibraryW(g_mluiInfo.szLocResDll,
                           g_mluiInfo.hinstParent,
                           ML_SHELL_LANGUAGE);

    // we're guaranteed to at least have resources in the install language
    // unless we're 100% toasted

    return hinst;
}

#ifdef MLUI_MESSAGEBOX
int MLShellMessageBox(HWND hWnd, LPCTSTR pszMsg, LPCTSTR pszTitle, UINT fuStyle, ...)
{
    va_list     vaList;
    int         nRet            = 0;
    LPTSTR      pszFormattedMsg = NULL;
    TCHAR       szTitleBuf[256];
    TCHAR       szBuffer[1024];

    //
    // prepare the message
    //

    if (IS_INTRESOURCE(pszMsg))
    {
        if (MLLoadShellLangString(LOWORD((DWORD_PTR)pszMsg), szBuffer, ARRAYSIZE(szBuffer)))
        {
            pszMsg = szBuffer;
        }
    }

    if (!IS_INTRESOURCE(pszMsg) &&  // the string load might have failed
        pszMsg != NULL)
    {
        va_start(vaList, fuStyle);

        if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING,
                          pszMsg, 0, 0, (LPTSTR)&pszFormattedMsg, 0, &vaList))
        {
            pszMsg = pszFormattedMsg;
        }

        va_end(vaList);
    }

    //
    // prepare the title
    //

    if (!IS_INTRESOURCE(pszTitle) && pszTitle != NULL)
    {
        // do nothing
    }
    else if (pszTitle != NULL && MLLoadShellLangString(LOWORD((DWORD_PTR)pszTitle), szTitleBuf, ARRAYSIZE(szTitleBuf)))
    {
        pszTitle = szTitleBuf;
    }
    else if (hWnd && GetWindowText(hWnd, szTitleBuf, ARRAYSIZE(szTitleBuf)))
    {
        pszTitle = szTitleBuf;
    }
    else
    {
        pszTitle = TEXT("");
    }

    //
    // launch a MessageBox
    //
#ifdef SHFUSION_H
    ULONG_PTR uCookie = 0;
    SHActivateContext(&uCookie);
#endif

    nRet = MessageBox(hWnd, pszFormattedMsg, pszTitle, fuStyle | MB_SETFOREGROUND);
#ifdef SHFUSION_H
    if (uCookie)
    {
        SHDeactivateContext(uCookie);
    }
#endif

    if (pszFormattedMsg != NULL)
    {
        LocalFree(pszFormattedMsg);
    }

    return nRet;
}
#endif // MLUI_MESSAGEBOX

#include "htmlhelp.h"

HWND 
SHHtmlHelpOnDemandWrapA(HWND hwndCaller, 
                       LPCSTR pszFile,
                       UINT uCommand,
                       DWORD_PTR dwData,
                       DWORD dwCrossCodePage)
{
    BOOL    fEnabled;
    ULONG_PTR uCookie = 0;
    HWND hwnd = NULL;
    
#ifdef MLUI_SUPPORT
    fEnabled = TRUE;
#else
    fEnabled = FALSE;
#endif

#ifdef SHFUSION_H
    SHActivateContext(&uCookie);
#endif
    hwnd = SHHtmlHelpOnDemandA(hwndCaller,
                              pszFile,
                              uCommand,
                              dwData,
                              dwCrossCodePage,
                              fEnabled);
#ifdef SHFUSION_H
    if (uCookie)
    {
        SHDeactivateContext(uCookie);
    }
#endif
    return hwnd;
}

HWND 
SHHtmlHelpOnDemandWrapW(HWND hwndCaller, 
                       LPCWSTR pszFile,
                       UINT uCommand,
                       DWORD_PTR dwData,
                       DWORD dwCrossCodePage)
{
    BOOL    fEnabled;
    ULONG_PTR uCookie = 0;
    HWND hwnd = NULL;

#ifdef MLUI_SUPPORT
    fEnabled = TRUE;
#else
    fEnabled = FALSE;
#endif

#ifdef SHFUSION_H
    SHActivateContext(&uCookie);
#endif
    hwnd = SHHtmlHelpOnDemandW(hwndCaller,
                              pszFile,
                              uCommand,
                              dwData,
                              dwCrossCodePage,
                              fEnabled);
#ifdef SHFUSION_H
    if (uCookie)
    {
        SHDeactivateContext(uCookie);
    }
#endif
    return hwnd;
}

HWND
MLHtmlHelpWrap(HWND hwndCaller,
               LPCTSTR pszFile,
               UINT uCommand,
               DWORD dwData,
               DWORD dwCrossCodePage)
{
    HWND    hwnd;

    ULONG_PTR uCookie = 0;
#ifdef SHFUSION_H
    SHActivateContext(&uCookie);
#endif

#ifdef MLUI_SUPPORT
    hwnd = MLHtmlHelp(hwndCaller,
                      pszFile,
                      uCommand,
                      dwData,
                      dwCrossCodePage);
#else
    hwnd = HtmlHelp(hwndCaller,
                    pszFile,
                    uCommand,
                    dwData);
#endif

#ifdef SHFUSION_H
    if (uCookie)
    {
        SHDeactivateContext(uCookie);
    }
#endif
    return hwnd;
}

BOOL
SHWinHelpOnDemandWrapA(HWND hwndCaller,
                      LPCSTR lpszHelp,
                      UINT uCommand,
                      DWORD_PTR dwData)
{
    BOOL    fEnabled;

#ifdef MLUI_SUPPORT
    fEnabled = TRUE;
#else
    fEnabled = FALSE;
#endif

    return SHWinHelpOnDemandA(hwndCaller,
                             lpszHelp,
                             uCommand,
                             dwData,
                             fEnabled);

}

BOOL
SHWinHelpOnDemandWrapW(HWND hwndCaller,
                      LPCWSTR lpszHelp,
                      UINT uCommand,
                      DWORD_PTR dwData)
{
    BOOL    fEnabled;

#ifdef MLUI_SUPPORT
    fEnabled = TRUE;
#else
    fEnabled = FALSE;
#endif

    return SHWinHelpOnDemandW(hwndCaller,
                             lpszHelp,
                             uCommand,
                             dwData,
                             fEnabled);

}

BOOL
MLWinHelpWrap(HWND hwndCaller,
                   LPCTSTR lpszHelp,
                   UINT uCommand,
                   DWORD dwData)
{
    BOOL    fRet;

#ifdef MLUI_SUPPORT
    fRet = MLWinHelp(hwndCaller,
                     lpszHelp,
                     uCommand,
                     dwData);
#else
    fRet = WinHelp(hwndCaller,
                   lpszHelp,
                   uCommand,
                   dwData);
#endif

    return fRet;
}

HRESULT
MLBuildResURLWrapA(LPSTR    pszLibFile,
                   HMODULE  hModule,
                   DWORD    dwCrossCodePage,
                   LPSTR    pszResName,
                   LPSTR    pszResURL,
                   int      nBufSize,
                   LPSTR    pszParentDll)
{
    HRESULT hr;

#ifdef MLUI_SUPPORT
    hr = MLBuildResURLA(pszLibFile,
                        hModule,
                        dwCrossCodePage,
                        pszResName,
                        pszResURL,
                        nBufSize);
#else
    wnsprintfA(pszResURL, nBufSize, "res://%s/%s", pszParentDll, pszResName);
    hr = S_OK;
#endif

    return hr;
}

HRESULT
MLBuildResURLWrapW(LPWSTR   pszLibFile,
                   HMODULE  hModule,
                   DWORD    dwCrossCodePage,
                   LPWSTR   pszResName,
                   LPWSTR   pszResURL,
                   int      nBufSize,
                   LPWSTR   pszParentDll)
{
    HRESULT hr;

#ifdef MLUI_SUPPORT
    hr = MLBuildResURLW(pszLibFile,
                        hModule,
                        dwCrossCodePage,
                        pszResName,
                        pszResURL,
                        nBufSize);
#else
    wnsprintfW(pszResURL, nBufSize, L"res://%s/%s", pszParentDll, pszResName);
    hr = S_OK;
#endif

    return hr;
}

#endif  // MLUI_INIT

#ifdef __cplusplus
};
#endif

#endif  // _INC_MLUISUPP