/*
 * MLANG wrapper functions
 *  Copyright (C) 2000 Microsoft Corporation
 */

#include "precomp.h"

#include "mlang.h"
#include "oleauto.h"


#define CP_USERDEF         50000
#define CP_ISCII_MIN       57002
#define CP_ISCII_MAC       57011

#define IsISCII(cp)  (((cp) >= CP_ISCII_MIN) && ((cp) <= CP_ISCII_MAC))


BOOL fInitializedCom;
IMultiLanguage3 *pml3;


BOOL FLoadMlang()
{
    HRESULT hr;

    if (!fInitializedCom)
    {
        hr = CoInitialize(NULL);

        if (FAILED(hr))
        {
            return(FALSE);
        }

        fInitializedCom = TRUE;
    }

    if (pml3 == NULL)
    {
        hr = CoCreateInstance(&CLSID_CMultiLanguage,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              &IID_IMultiLanguage3,
                              (void **) &pml3);

        if (FAILED(hr))
        {
            return(FALSE);
        }
    }

    return(TRUE);
}


BOOL FValidWin32CodePage(UINT cp)
{
    switch (cp)
    {
    case 50220 :
    case 50221 :
    case 50222 :
    case 50225 :
    case 50227 :
    // case 50229 :
    case 52936 :
        // We don't use WCToMB or MBToWC for these because there are
        // bugs in C_IS2022.DLL and MLANG has built support.

        return(FALSE);
    }

    return(IsValidCodePage(cp));
}


ConvertFromUnicodeMlang(UINT cp, BOOL fNoBestFit, BOOL fWriteEntities, LPCWSTR rgchUtf16, UINT cchUtf16, LPSTR rgchMbcs, UINT cchMbcs, BOOL* pfDefCharUsed)
{
    DWORD dwMode;
    UINT cchSrc;
    UINT cchDst;
    DWORD dwFlags;
    HRESULT hr;

    if (!FLoadMlang())
    {
        return(0);
    }

    dwMode = 0;
    cchSrc = cchUtf16;
    cchDst = cchMbcs;
    dwFlags = fWriteEntities ? MLCONVCHARF_NCR_ENTITIZE : MLCONVCHARF_USEDEFCHAR;

    if (fNoBestFit)
    {
        dwFlags |= MLCONVCHARF_NOBESTFITCHARS;
    }

    hr = pml3->lpVtbl->ConvertStringFromUnicodeEx(pml3,
                                                  &dwMode,
                                                  cp,
                                                  (WCHAR *) rgchUtf16,
                                                  &cchSrc,
                                                  rgchMbcs,
                                                  &cchDst,
                                                  dwFlags,
                                                  NULL);

    if (FAILED(hr))
    {
        return(0);
    }

    if (pfDefCharUsed != NULL)
    {
        *pfDefCharUsed = (hr == S_FALSE);
    }

    return(cchDst);
}


UINT ConvertFromUnicode(UINT cp, BOOL fNoBestFit, BOOL fWriteEntities, LPCWSTR rgchUtf16, UINT cchUtf16, LPSTR rgchMbcs, UINT cchMbcs, BOOL* pfDefCharUsed)
{
    UINT cch;

    if (cchUtf16 == 0)
    {
        return(0);
    }

    if (!fWriteEntities && FValidWin32CodePage(cp))
    {
        cch = WideCharToMultiByte(cp,
                                  fNoBestFit ? WC_NO_BEST_FIT_CHARS : 0,
                                  rgchUtf16,
                                  cchUtf16,
                                  rgchMbcs,
                                  cchMbcs,
                                  NULL,
                                  pfDefCharUsed);

        if (cch != 0)
        {
            return(cch);
        }

        // We retry with MLANG even when WCToMB supports the code page
        // because there are code pages when WCToMB may not support all
        // the features of the API.  I know this is the case for WCToMB
        // and the ISCII encodings.  We try again just for robustness.
    }

    cch = ConvertFromUnicodeMlang(cp, fNoBestFit, fWriteEntities, rgchUtf16, cchUtf16, rgchMbcs, cchMbcs, pfDefCharUsed);

#if DBG
    if (cch == 0)
    {
        DebugBreak();
    }
#endif

    return(cch);
}


UINT ConvertToUnicodeMlang(UINT cp, LPCSTR rgchMbcs, UINT cchMbcs, LPWSTR rgchUtf16, UINT cchUtf16)
{
    DWORD dwMode;
    UINT cchSrc;
    UINT cchDst;
    HRESULT hr;

    if (!FLoadMlang())
    {
        return(0);
    }

    dwMode = 0;
    cchSrc = cchMbcs;
    cchDst = cchUtf16;

    hr = pml3->lpVtbl->ConvertStringToUnicode(pml3,
                                              &dwMode,
                                              cp,
                                              (CHAR *) rgchMbcs,
                                              &cchSrc,
                                              rgchUtf16,
                                              &cchDst);

    if (FAILED(hr))
    {
        return(0);
    }

    return(cchDst);
}


UINT ConvertToUnicode(UINT cp, LPCSTR rgchMbcs, UINT cchMbcs, LPWSTR rgchUtf16, UINT cchUtf16)
{
    UINT cch;

    if (cchMbcs == 0)
    {
        return(0);
    }

    if (FValidWin32CodePage(cp))
    {
        cch = MultiByteToWideChar(cp, 0, rgchMbcs, cchMbcs, rgchUtf16, cchUtf16);

        if (cch != 0)
        {
            return(cch);
        }

        // We retry with MLANG even when MBToWC supports the code page
        // because there are code pages when MBToWC may not support all
        // the features of the API.  I know this is the case for WCToMB
        // and the ISCII encodings.  We try again just for robustness.
    }

    cch = ConvertToUnicodeMlang(cp, rgchMbcs, cchMbcs, rgchUtf16, cchUtf16);

#if DBG
    if (cch == 0)
    {
        DebugBreak();
    }
#endif

    return(cch);
}


BOOL FDetectEncodingA(LPCSTR rgch, UINT cch, UINT* pcp)
{
    INT cb;
    DetectEncodingInfo dei;
    INT cdei;
    HRESULT hr;

    if (!FLoadMlang())
    {
        return(0);
    }

    cb = (INT) cch;
    cdei = 1;

    hr = pml3->lpVtbl->DetectInputCodepage(pml3,
                                           0,
                                           0,
                                           (LPSTR) rgch,
                                           &cb,
                                           &dei,
                                           &cdei);

    if (hr != S_OK)
    {
        return(FALSE);
    }

    if (cdei == 0)
    {
        return(FALSE);
    }

    *pcp = dei.nCodePage;

    return(TRUE);
}


BOOL FLookupCodepageNameW(LPCWSTR rgchEncoding, UINT cch, UINT* pcp)
{
    BSTR bstrEncoding;
    MIMECSETINFO mci;
    HRESULT hr;

    if (cch == 0)
    {
        return(FALSE);
    }

    if (rgchEncoding[0] == L'_')
    {
        // Don't allow internal MLANG encodings

        return(FALSE);
    }

    if (!FLoadMlang())
    {
        return(FALSE);
    }

    bstrEncoding = SysAllocStringLen(rgchEncoding, cch);

    if (bstrEncoding == NULL)
    {
        return(FALSE);
    }

    hr = pml3->lpVtbl->GetCharsetInfo(pml3, bstrEncoding, &mci);

    SysFreeString(bstrEncoding);

    *pcp = mci.uiInternetEncoding;

    if (SUCCEEDED(hr))
    {
        return(TRUE);
    }

    return(FALSE);
}


BOOL FLookupCodepageNameA(LPCSTR rgchEncoding, UINT cch, UINT* pcp)
{
    WCHAR rgwchEncoding[MAX_MIMECSET_NAME];
    UINT ich;

    if (cch > MAX_MIMECSET_NAME)
    {
        return(FALSE);
    }

    for (ich = 0; ich < cch; ich++)
    {
        // Assume input is ASCII or Latin-1 and zero extend each character

        rgwchEncoding[ich] = (WCHAR) (BYTE) rgchEncoding[ich];
    }

    return(FLookupCodepageNameW(rgwchEncoding, cch, pcp));
}


BOOL FSupportWriteEntities(UINT cp)
{
    if (IsISCII(cp))
    {
        return(FALSE);
    }

    return(TRUE);
}


BOOL FValidateCodepage(HWND hwnd, UINT cp)
{
    HRESULT hr;

    if (IsValidCodePage(cp))
    {
        return(TRUE);
    }

    if ((cp == CP_USERDEF) || (cp == CP_AUTO) || (cp == CP_MACCP) || (cp == CP_THREAD_ACP))
    {
        return(FALSE);
    }

    if (!FLoadMlang())
    {
        return(FALSE);
    }

    hr = pml3->lpVtbl->ValidateCodePage(pml3, cp, hwnd);

    if (SUCCEEDED(hr) && (hr != S_FALSE))
    {
        return(TRUE);
    }

    return(FALSE);
}


void PopulateCodePages(HWND hWnd, BOOL fSelectEncoding, UINT cpSelect, UINT cpExtra)
{
    IEnumCodePage *pecp;
    UINT msg_ADDSTRING;
    UINT msg_SETITEMDATA;
    UINT msg_GETCOUNT;
    UINT msg_GETITEMDATA;
    UINT msg_SETCURSEL;
    HRESULT hr;
    LRESULT lr;

    if (!FLoadMlang())
    {
        return;
    }

    hr = pml3->lpVtbl->EnumCodePages(pml3,
                                     MIMECONTF_VALID_NLS | MIMECONTF_EXPORT,
                                     GetUserDefaultUILanguage(),
                                     &pecp);

    if (FAILED(hr))
    {
        return;
    }

    msg_ADDSTRING   = fSelectEncoding ? LB_ADDSTRING   : CB_ADDSTRING;
    msg_SETITEMDATA = fSelectEncoding ? LB_SETITEMDATA : CB_SETITEMDATA;
    msg_GETCOUNT    = fSelectEncoding ? LB_GETCOUNT    : CB_GETCOUNT;
    msg_GETITEMDATA = fSelectEncoding ? LB_GETITEMDATA : CB_GETITEMDATA;
    msg_SETCURSEL   = fSelectEncoding ? LB_SETCURSEL   : CB_SETCURSEL;

    for (;;)
    {
        MIMECPINFO mci;
        ULONG c;

        hr = pecp->lpVtbl->Next(pecp, 1, &mci, &c);

        if (FAILED(hr))
        {
            break;
        }

        if (c == 0)
        {
            break;
        }

        if (mci.uiCodePage == CP_USERDEF)
        {
            // Ignore "User Defined"

            continue;
        }

        if (!fSelectEncoding)
        {
            if (mci.uiCodePage == cpSelect)
            {
            }

            else if (mci.uiCodePage == cpExtra)
            {
            }

            else if (mci.uiCodePage == CP_UTF16)
            {
            }

            else if (mci.uiCodePage == 1252)
            {
            }

            else if (mci.uiCodePage == CP_UTF8)
            {
            }

            else if (mci.uiCodePage == g_cpDefault)
            {
            }

            else if (mci.uiCodePage == g_cpANSI)
            {
               // Don't filter ANSI codepage used by system
            }

            else if (mci.uiCodePage == g_cpOEM)
            {
               // Don't filter OEM codepage used by system
            }

            else if (mci.uiCodePage == g_cpUserLangANSI)
            {
               // Don't filter ANSI codepage associated with user's default UI language
            }

            else if (mci.uiCodePage == g_cpUserLangOEM)
            {
               // Don't filter OEM codepage associated with user's default UI language
            }

            else if (mci.uiCodePage == g_cpUserLocaleANSI)
            {
               // Don't filter ANSI codepage associated with user's default locale
            }

            else if (mci.uiCodePage == g_cpUserLocaleOEM)
            {
               // Don't filter OEM codepage associated with user's default locale
            }

            else if (mci.uiCodePage == g_cpKeyboardANSI)
            {
               // Don't filter ANSI codepage associated with the current active keyboard
            }

            else if (mci.uiCodePage == g_cpKeyboardOEM)
            {
               // Don't filter OEM codepage associated with the current active keyboard
            }

            else
            {
                continue;
            }
        }

        lr = SendDlgItemMessage(hWnd, IDC_CODEPAGE, msg_ADDSTRING, 0, (LPARAM) mci.wszDescription);

        if (lr < 0)
        {
            break;
        }

        SendDlgItemMessage(hWnd, IDC_CODEPAGE, msg_SETITEMDATA, (WPARAM) lr, (LPARAM) mci.uiCodePage);
    }

    pecp->lpVtbl->Release(pecp);

    lr = SendDlgItemMessage(hWnd, IDC_CODEPAGE, msg_GETCOUNT, 0, 0);

    while (--lr >= 0)
    {
        UINT cp = (UINT) SendDlgItemMessage(hWnd, IDC_CODEPAGE, msg_GETITEMDATA, (WPARAM) lr, 0);

        if (cp == cpSelect)
        {
            SendDlgItemMessage(hWnd, IDC_CODEPAGE, msg_SETCURSEL, (WPARAM) lr, 0);
            break;
        }
    }
}


void UnloadMlang()
{
    if (pml3 != NULL)
    {
        pml3->lpVtbl->Release(pml3);

        pml3 = NULL;
    }

    if (fInitializedCom)
    {
        CoUninitialize();

        fInitializedCom = FALSE;
    }
}