#include "private.h"
#include "mlmain.h"
#include "cpdetect.h"
#include "codepage.h"

STDAPI CMultiLanguage::GetNumberOfCodePageInfo(UINT *pcCodePage)
{
    if (NULL != m_pMimeDatabase)
        return m_pMimeDatabase->GetNumberOfCodePageInfo(pcCodePage);
    else
        return E_FAIL;
}

STDAPI CMultiLanguage::GetCodePageInfo(UINT uiCodePage, PMIMECPINFO pcpInfo)
{

    if (NULL != m_pMimeDatabase)
        return m_pMimeDatabase->GetCodePageInfo(uiCodePage, GetSystemDefaultLangID(), pcpInfo);
    else
        return E_FAIL;
}

STDAPI CMultiLanguage::GetFamilyCodePage(UINT uiCodePage, UINT *puiFamilyCodePage)
{
        HRESULT hr = S_OK;
        int idx = 0;

        DebugMsg(DM_TRACE, TEXT("CMultiLanguage::GetFamilyCodePage called."));

        while(MimeCodePage[idx].uiCodePage)
        {
            if ((uiCodePage == MimeCodePage[idx].uiCodePage) &&
                (MimeCodePage[idx].dwFlags & dwMimeSource))
                break;
            idx++;
        }

        if (MimeCodePage[idx].uiCodePage)
        {
            if (MimeCodePage[idx].uiFamilyCodePage)
                *puiFamilyCodePage = MimeCodePage[idx].uiFamilyCodePage;
            else
                *puiFamilyCodePage = uiCodePage;
        }
        else
        {
            hr = E_FAIL;
            *puiFamilyCodePage = 0;
        }
        return hr;
}

STDAPI CMultiLanguage::EnumCodePages(DWORD grfFlags, IEnumCodePage **ppEnumCodePage)
{
    DebugMsg(DM_TRACE, TEXT("CMultiLanguage::EnumCodePages called."));
    *ppEnumCodePage = NULL;

    // Return IE4 MIME DB data in IMultiLanguage    
    CEnumCodePage *pCEnumCodePage = new CEnumCodePage(grfFlags, GetSystemDefaultLangID(), MIMECONTF_MIME_IE4);

    if (NULL != pCEnumCodePage)
    {
        HRESULT hr = pCEnumCodePage->QueryInterface(IID_IEnumCodePage, (void**)ppEnumCodePage);
        pCEnumCodePage->Release();
        return hr;
    }
    return E_OUTOFMEMORY;
}

STDAPI CMultiLanguage2::EnumCodePages(DWORD grfFlags, LANGID LangId, IEnumCodePage **ppEnumCodePage)
{
    DebugMsg(DM_TRACE, TEXT("CMultiLanguage::EnumCodePages called."));
    *ppEnumCodePage = NULL;

    CEnumCodePage *pCEnumCodePage = new CEnumCodePage(grfFlags, LangId, dwMimeSource);
    if (NULL != pCEnumCodePage)
    {
        HRESULT hr = pCEnumCodePage->QueryInterface(IID_IEnumCodePage, (void**)ppEnumCodePage);
        pCEnumCodePage->Release();
        return hr;
    }
    return E_OUTOFMEMORY;
}

STDAPI CMultiLanguage2::EnumScripts(DWORD dwFlags, LANGID LangId, IEnumScript **ppEnumScript)
{
    DebugMsg(DM_TRACE, TEXT("CMultiLanguage2::EnumScripts called."));
    *ppEnumScript = NULL;

    CEnumScript *pCEnumScript = new CEnumScript(dwFlags, LangId, dwMimeSource);
    if (NULL != pCEnumScript)
    {
        HRESULT hr = pCEnumScript->QueryInterface(IID_IEnumScript, (void**)ppEnumScript);
        pCEnumScript->Release();
        return hr;
    }
    return E_OUTOFMEMORY;
}

STDAPI CMultiLanguage::GetCharsetInfo(BSTR Charset, PMIMECSETINFO pcsetInfo)
{
    if (NULL != m_pMimeDatabase)
        return m_pMimeDatabase->GetCharsetInfo(Charset, pcsetInfo);
    else
        return E_FAIL;
}

STDAPI CMultiLanguage::IsConvertible(DWORD dwSrcEncoding, DWORD dwDstEncoding)
{
    DebugMsg(DM_TRACE, TEXT("CMultiLanguage::IsConvertINetStringAvailable called."));
    return IsConvertINetStringAvailable(dwSrcEncoding, dwDstEncoding);
}

STDAPI CMultiLanguage::ConvertString(LPDWORD lpdwMode, DWORD dwSrcEncoding, DWORD dwDstEncoding, BYTE *pSrcStr, UINT *pcSrcSize, BYTE *pDstStr, UINT *pcDstSize)
{
    DebugMsg(DM_TRACE, TEXT("CMultiLanguage::ConvertStringEx called."));
    return ConvertINetString(lpdwMode, dwSrcEncoding, dwDstEncoding, (LPCSTR)pSrcStr, (LPINT)pcSrcSize, (LPSTR)pDstStr, (LPINT)pcDstSize);
}

STDAPI CMultiLanguage::ConvertStringToUnicode(LPDWORD lpdwMode, DWORD dwEncoding, CHAR *pSrcStr, UINT *pcSrcSize, WCHAR *pDstStr, UINT *pcDstSize)
{
    DebugMsg(DM_TRACE, TEXT("CMultiLanguage::ConvertStringToUnicode called."));
    return ConvertINetMultiByteToUnicode(lpdwMode, dwEncoding, (LPCSTR)pSrcStr, (LPINT)pcSrcSize, (LPWSTR)pDstStr, (LPINT)pcDstSize);
}

STDAPI CMultiLanguage::ConvertStringFromUnicode(LPDWORD lpdwMode, DWORD dwEncoding, WCHAR *pSrcStr, UINT *pcSrcSize, CHAR *pDstStr, UINT *pcDstSize)
{
    DebugMsg(DM_TRACE, TEXT("CMultiLanguage::ConvertStringFromUnicode called."));
    return ConvertINetUnicodeToMultiByte(lpdwMode, dwEncoding, (LPCWSTR)pSrcStr, (LPINT)pcSrcSize, (LPSTR)pDstStr, (LPINT)pcDstSize);
}

STDAPI CMultiLanguage::ConvertStringReset(void)
{
    DebugMsg(DM_TRACE, TEXT("CMultiLanguage::Reset called."));
    return ConvertINetReset();
}

STDAPI CMultiLanguage::GetRfc1766FromLcid(LCID Locale, BSTR *pbstrRfc1766)
{
    HRESULT hr = E_INVALIDARG;

    DebugMsg(DM_TRACE, TEXT("CMultiLanguage::GetRfc1766FromLcid called."));

    if (NULL != pbstrRfc1766)
    {
        WCHAR wsz[MAX_RFC1766_NAME];

        hr = LcidToRfc1766W(Locale, wsz, ARRAYSIZE(wsz));
        if (SUCCEEDED(hr))
            *pbstrRfc1766 = SysAllocString(wsz);
        else
            *pbstrRfc1766 = NULL;
    }
    return hr;
}

STDAPI CMultiLanguage::GetLcidFromRfc1766(PLCID pLocale, BSTR bstrRfc1766)
{
    DebugMsg(DM_TRACE, TEXT("CMultiLanguage::GetLcidFromRfc1766 called."));
    return Rfc1766ToLcidW(pLocale, bstrRfc1766);   
}

STDAPI CMultiLanguage::EnumRfc1766(IEnumRfc1766 **ppEnumRfc1766)
{
    DebugMsg(DM_TRACE, TEXT("CMultiLanguage::EnumRfc1766 called."));
    *ppEnumRfc1766 = NULL;

    CEnumRfc1766 *pCEnumRfc1766 = new CEnumRfc1766(dwMimeSource,GetSystemDefaultLangID());
    if (NULL != pCEnumRfc1766)
    {
        HRESULT hr = pCEnumRfc1766->QueryInterface(IID_IEnumRfc1766, (void**)ppEnumRfc1766);
        pCEnumRfc1766->Release();
        return hr;
    }
    return E_OUTOFMEMORY;
}

STDAPI CMultiLanguage2::EnumRfc1766(LANGID LangId, IEnumRfc1766 **ppEnumRfc1766)
{
    DebugMsg(DM_TRACE, TEXT("CMultiLanguage::EnumRfc1766 called."));
    *ppEnumRfc1766 = NULL;

    CEnumRfc1766 *pCEnumRfc1766 = new CEnumRfc1766(dwMimeSource, LangId);
    if (NULL != pCEnumRfc1766)
    {
        HRESULT hr = pCEnumRfc1766->QueryInterface(IID_IEnumRfc1766, (void**)ppEnumRfc1766);
        pCEnumRfc1766->Release();
        return hr;
    }
    return E_OUTOFMEMORY;
}

STDAPI CMultiLanguage::GetRfc1766Info(LCID Locale, PRFC1766INFO pRfc1766Info)
{
    UINT i;
    HRESULT hr = E_INVALIDARG;

    DebugMsg(DM_TRACE, TEXT("CMultiLanguage::GetRfc1766Info called."));


    if (NULL != pRfc1766Info)
    {
        for (i = 0; i < g_cRfc1766; i++)
        {
            if (MimeRfc1766[i].LcId == Locale)
                break;
        }
        if (i < g_cRfc1766)
        {
            pRfc1766Info->lcid = MimeRfc1766[i].LcId;
            MLStrCpyNW(pRfc1766Info->wszRfc1766, MimeRfc1766[i].szRfc1766, MAX_RFC1766_NAME);
            _LoadStringExW(g_hInst, MimeRfc1766[i].uidLCID, pRfc1766Info->wszLocaleName, 
                 MAX_LOCALE_NAME, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US));
            hr = S_OK;
        }
        else
            hr = E_FAIL;
    }
    return hr;
}

STDAPI CMultiLanguage2::GetRfc1766Info(LCID Locale, LANGID LangId, PRFC1766INFO pRfc1766Info)
{
    UINT i;
    HRESULT hr = E_INVALIDARG;

    DebugMsg(DM_TRACE, TEXT("CMultiLanguage::GetRfc1766Info called."));


    if (NULL != pRfc1766Info)
    {
        for (i = 0; i < g_cRfc1766; i++)
        {
            if (MimeRfc1766[i].LcId == Locale)
                break;
        }
        if (i < g_cRfc1766)
        {
            if (!LangId)
                LangId = GetSystemDefaultLangID();

            pRfc1766Info->lcid = MimeRfc1766[i].LcId;
            MLStrCpyNW(pRfc1766Info->wszRfc1766, MimeRfc1766[i].szRfc1766, MAX_RFC1766_NAME);

            if (!_LoadStringExW(g_hInst, MimeRfc1766[i].uidLCID, pRfc1766Info->wszLocaleName, 
                 MAX_LOCALE_NAME, LangId))
            {
                    _LoadStringExW(g_hInst, MimeRfc1766[i].uidLCID, pRfc1766Info->wszLocaleName, 
                         MAX_LOCALE_NAME, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US));
            }
            hr = S_OK;
        }
        else
            hr = E_FAIL;
    }
    return hr;
}

STDAPI CMultiLanguage::CreateConvertCharset(UINT uiSrcCodePage, UINT uiDstCodePage, DWORD dwProperty, IMLangConvertCharset **ppMLangConvertCharset)
{
    HRESULT hr;
    IClassFactory* pClassObj;

    DebugMsg(DM_TRACE, TEXT("CMultiLanguage::CreateCharsetConvert called."));

    if (SUCCEEDED(hr = _Module.GetClassObject(CLSID_CMLangConvertCharset, IID_IClassFactory, (void**)&pClassObj)))
    {
        hr = pClassObj->CreateInstance(NULL, IID_IMLangConvertCharset, (void**)ppMLangConvertCharset);
        pClassObj->Release();
    }

    if (ppMLangConvertCharset && FAILED(hr))
        *ppMLangConvertCharset = NULL;

    if (NULL != *ppMLangConvertCharset)
        hr = (*ppMLangConvertCharset)->Initialize(uiSrcCodePage, uiDstCodePage, dwProperty);

    return hr;
}

STDAPI CMultiLanguage2::ConvertStringInIStream(LPDWORD lpdwMode, DWORD dwFlag, WCHAR *lpFallBack, DWORD dwSrcEncoding, DWORD dwDstEncoding, IStream *pstmIn, IStream *pstmOut)
{
    DebugMsg(DM_TRACE, TEXT("CMultiLanguage2::ConvertStringInIStream called."));
    return ConvertINetStringInIStream(lpdwMode,dwSrcEncoding,dwDstEncoding,pstmIn,pstmOut,dwFlag,lpFallBack);
}

STDAPI CMultiLanguage2::ConvertStringToUnicodeEx(LPDWORD lpdwMode, DWORD dwEncoding, CHAR *pSrcStr, UINT *pcSrcSize, WCHAR *pDstStr, UINT *pcDstSize, DWORD dwFlag, WCHAR *lpFallBack)
{
    DebugMsg(DM_TRACE, TEXT("CMultiLanguage2::ConvertBufferStringToUnicodeEx called."));
    return ConvertINetMultiByteToUnicodeEx(lpdwMode, dwEncoding, (LPCSTR)pSrcStr, (LPINT)pcSrcSize, (LPWSTR)pDstStr, (LPINT)pcDstSize, dwFlag, lpFallBack);
}

STDAPI CMultiLanguage2::ConvertStringFromUnicodeEx(LPDWORD lpdwMode, DWORD dwEncoding, WCHAR *pSrcStr, UINT *pcSrcSize, CHAR *pDstStr, UINT *pcDstSize, DWORD dwFlag, WCHAR *lpFallBack)
{
    DebugMsg(DM_TRACE, TEXT("CMultiLanguage2::ConvertBufferStringFromUnicodeEx called."));
    return ConvertINetUnicodeToMultiByteEx(lpdwMode, dwEncoding, (LPCWSTR)pSrcStr, (LPINT)pcSrcSize, (LPSTR)pDstStr, (LPINT)pcDstSize, dwFlag, lpFallBack);
}

STDAPI CMultiLanguage2::DetectCodepageInIStream(DWORD dwFlag, DWORD uiPrefWinCodepage, IStream *pstmIn, DetectEncodingInfo *lpEncoding, INT *pnScores)
{
    DebugMsg(DM_TRACE, TEXT("CMultiLanguage2::DetectCodepageInIStream called. "));
    return _DetectCodepageInIStream(dwFlag, uiPrefWinCodepage, pstmIn, lpEncoding, pnScores);
}

STDAPI CMultiLanguage2::DetectInputCodepage(DWORD dwFlag, DWORD uiPrefWinCodepage, CHAR *pSrcStr, INT *pcSrcSize, DetectEncodingInfo *lpEncoding, INT *pnScores)
{
    DebugMsg(DM_TRACE, TEXT("CMultiLanguage2::DetectInputCodepage called. "));
    return _DetectInputCodepage(dwFlag, uiPrefWinCodepage, pSrcStr, pcSrcSize, lpEncoding, pnScores);
}

STDAPI CMultiLanguage2::ValidateCodePage(UINT uiCodePage, HWND hwnd)
{
    return ValidateCodePageEx(uiCodePage, hwnd, 0);
}
// this is private function to serve both for IML2 and IML3
STDAPI CMultiLanguage2::ValidateCodePageEx(UINT uiCodePage, HWND hwnd, DWORD dwfIODControl)
{
    MIMECPINFO cpInfo;
    CLSID      clsid;
    UINT       uiFamCp;
    HRESULT    hr;
    
    DebugMsg(DM_TRACE, TEXT("CMultiLanguage2::ValidateCodePage called. "));
    
    if (NULL != g_pMimeDatabase)
        hr = g_pMimeDatabase->GetCodePageInfo(uiCodePage, 0x409, &cpInfo);
    else
        hr = E_OUTOFMEMORY;

    if (FAILED(hr))
        return E_INVALIDARG;

    EnterCriticalSection(&g_cs);
    if (NULL == g_pCpMRU)
        if (g_pCpMRU = new CCpMRU)
            g_pCpMRU->Init();
    LeaveCriticalSection(&g_cs);

    if (g_pCpMRU && g_pCpMRU->dwCpMRUEnable)
        g_pCpMRU->UpdateCPMRU(uiCodePage);

    if (cpInfo.dwFlags & MIMECONTF_VALID)
        return S_OK;

    // always handle family codepage because a caller
    // of this function is not generally aware if
    // the codepage is primary one. i.e., they can
    // call with cp=20268 to validate the entire 1251
    // family.
    //
    uiFamCp = cpInfo.uiFamilyCodePage;

    // Bug 394904, IOD won't be able to get us gb18030 support, 
    // so we won't ask UrlMon for CHS langpack if gb2312 is valid
    if (uiCodePage == CP_18030)
    {
        g_pMimeDatabase->GetCodePageInfo(uiFamCp, 0x409, &cpInfo);

        if (cpInfo.dwFlags & MIMECONTF_VALID)
            return S_FALSE;
    }
    
    // Ignore IOD check on NT5
    if (g_bIsNT5)
    {
        // Currently, NT5 doesn't install 20127 and 28605 NLS files.
        // We should prevent langpack installation loop and let clients resolve 
        // them with CP_ACP in case of 20127 and 28605 validation.
        // This hack can be removed once NT5 bundles these NLS files by default.
        if ((uiCodePage == CP_20127 || uiCodePage == CP_ISO_8859_15) && IsValidCodePage(uiFamCp))
            return E_FAIL;
        hr = IsNTLangpackAvailable(uiFamCp);
        if (hr != S_OK)
            return hr;
    }
    else
    {
        // check if JIT langpack is enabled.
        //
        hr = EnsureIEStatus();
        if (hr == S_OK) 
        {
            if (!m_pIEStat || m_pIEStat->IsJITEnabled() != TRUE)
            {
                // the codepage is neither valid or installable
                return S_FALSE;
            }
        }
    }    

    if (hwnd == NULL)
    {
        hwnd = GetForegroundWindow();   
    }

    // Special handling for NT.
    if (g_bIsNT5)
    {
        DWORD   dwInstallLpk = 1;
        HKEY    hkey;
        DWORD   dwAction = 0;

        // HKCR\\Software\\Microsoft\internet explorer\\international
        if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_CURRENT_USER, 
                         REGSTR_PATH_INTERNATIONAL,
                         NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, &dwAction)) 
        {
            DWORD dwType = REG_DWORD;
            DWORD dwSize = sizeof(DWORD);

            if (ERROR_SUCCESS != RegQueryValueEx(hkey, REG_KEY_NT5LPK, 0, &dwType, (LPBYTE)&dwInstallLpk, &dwSize))
            {
                dwInstallLpk = 1;
                RegSetValueEx(hkey, REG_KEY_NT5LPK, 0, REG_DWORD, (LPBYTE)&dwInstallLpk, sizeof(dwInstallLpk));
            }
            RegCloseKey(hkey);
        }

        hr = S_FALSE;

        // Pops up NT5 langpack dialog box if langpack is enabled or user selects it from encoding menu
        if (dwInstallLpk || (dwfIODControl & CPIOD_FORCE_PROMPT))
        {
            LPCDLGTEMPLATE pTemplate;
            HRSRC   hrsrc;
            INT_PTR iRet;
            LANGID  LangId = GetNT5UILanguage();

            dwInstallLpk |= uiFamCp << 16;

            // Load correct resource to match NT5 UI language
            hrsrc = FindResourceExW(g_hInst, (LPCWSTR) RT_DIALOG, (LPCWSTR) MAKEINTRESOURCE(IDD_DIALOG_LPK), LangId);

            ULONG_PTR uCookie = 0;
            SHActivateContext(&uCookie);
            
            // Pack LPARAM, code page value in HIWORD, installation flag in LOWORD
            if (hrsrc &&
                (pTemplate = (LPCDLGTEMPLATE)LoadResource(g_hInst, hrsrc)))
            {
                iRet = DialogBoxIndirectParamW(g_hInst, pTemplate,
                   hwnd, LangpackDlgProc, (LPARAM) dwInstallLpk);
            }
            else 
                iRet = DialogBoxParamW(g_hInst, (LPCWSTR) MAKEINTRESOURCE(IDD_DIALOG_LPK), hwnd, LangpackDlgProc, (LPARAM) dwInstallLpk); 

            if (iRet)
            {
                hr = _InstallNT5Langpack(hwnd, uiFamCp);
                if (S_OK == hr)
                {                
                    WCHAR wszLangInstall[MAX_PATH];
                    WCHAR wszNT5LangPack[1024];
                    

                    // Fall back to English (US) if we don't have a specific language resource
                    if (!_LoadStringExW(g_hInst, IDS_LANGPACK_INSTALL, wszLangInstall, ARRAYSIZE(wszLangInstall), LangId) ||
                        !_LoadStringExW(g_hInst, IDS_NT5_LANGPACK, wszNT5LangPack, ARRAYSIZE(wszNT5LangPack), LangId))
                    {
                        LangId = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
                        _LoadStringExW(g_hInst, IDS_LANGPACK_INSTALL, wszLangInstall, ARRAYSIZE(wszLangInstall), LangId);
                        _LoadStringExW(g_hInst, IDS_NT5_LANGPACK, wszNT5LangPack, ARRAYSIZE(wszNT5LangPack), LangId);
                    }
                
                    MessageBoxW(hwnd, wszNT5LangPack, wszLangInstall, MB_OK);
                }
            }
            if (uCookie)
            {
                SHDeactivateContext(uCookie);
            }
        }


        goto SKIP_IELANGPACK;
    }

    // Initiate JIT using CLSID give to the langpack
    hr = _GetJITClsIDForCodePage(uiFamCp, &clsid);
    if (SUCCEEDED(hr))
    {
        hr = InstallIEFeature(hwnd, &clsid, dwfIODControl);
    }

    // if JIT returns S_OK, we now have everything installed
    // then we'll validate the codepage and add font
    // NOTE: there can be more than codepage to validate here,
    //      for example, PE langpack contains more than one 
    //      NLS file to get greek, cyrillic and Turkish at the
    //      same time.
    if (hr == S_OK)
    {
        hr = _ValidateCPInfo(uiFamCp);
        if (SUCCEEDED(hr))
        {
            _AddFontForCP(uiFamCp);
        }
    }
    
SKIP_IELANGPACK:      
    return hr;
}


// IMultiLanguage2::GetCodePageDescription
//
// Provide native code page description in UNICODE.
// If not resource is vailable for the specified LCID, 
// we'll try the primary language first, then English.
// In this case, we'll return S_FALSE to caller.
STDAPI CMultiLanguage2::GetCodePageDescription(
    UINT uiCodePage,        // Specifies the required code page for description.
    LCID lcid,              // Specifies locale ID for prefered language.
    LPWSTR lpWideCharStr,   // Points to a buffer that receives the code page description.
    int cchWideChar)        // Specifies the size, in wide characters, of the buffer 
                            // pointed by lpWideCharStr.
{
    HRESULT hr = E_FAIL;
    UINT    CountCPId;
    UINT    i = 0, j = 0;

    g_pMimeDatabase->GetNumberOfCodePageInfo(&CountCPId);
    
    if (cchWideChar == 0)
    {
        return E_INVALIDARG;
    }        

    while (i < CountCPId)
    {
        if (MimeCodePage[j].dwFlags & dwMimeSource)
        {
            if ((MimeCodePage[j].uiCodePage == uiCodePage))
            {
                if (_LoadStringExW(g_hInst, MimeCodePage[j].uidDescription, lpWideCharStr,
                            cchWideChar, LANGIDFROMLCID(lcid))) 
                {
                    hr = S_OK;
                }
                else // Resource not find in the specificed language
                {
                        if (_LoadStringExW(g_hInst, MimeCodePage[j].uidDescription, lpWideCharStr,
                            cchWideChar, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)))
                        {
                            hr = S_FALSE;                
                        }
                }
                break;
            }  
            i++;
        }
        j++;
    }
    
    if (i >= CountCPId) // Code page description is not available in MLANG
    {
        hr = E_INVALIDARG;
    }

    return (hr);
}

STDAPI CMultiLanguage2::IsCodePageInstallable(UINT uiCodePage)
{
    MIMECPINFO cpInfo;
    UINT       uiFamCp;
    HRESULT    hr;
    
    DebugMsg(DM_TRACE, TEXT("CMultiLanguage::IsCPInstallable called. "));

    if (NULL != g_pMimeDatabase)
        hr = g_pMimeDatabase->GetCodePageInfo(uiCodePage, 0x409, &cpInfo);
    else
        hr = E_OUTOFMEMORY;

    if (FAILED(hr))
        return E_INVALIDARG;

    // if it's already valid, no need to check if it's installable
    if (cpInfo.dwFlags & MIMECONTF_VALID)
    {
        hr = S_OK;
    }
    else
    {        
        uiFamCp = cpInfo.uiFamilyCodePage;

        // it is currently not valid, if NT5, ignore IOD check
        if (g_bIsNT5)
        {
            hr = IsNTLangpackAvailable(uiFamCp);
        }
        else
        {
            // now check to see if the cp can be IOD
            hr = EnsureIEStatus();
        
            // we'll return FALSE if we couldn't get IOD status
            if (hr == S_OK)
            {
                if (!m_pIEStat || !m_pIEStat->IsJITEnabled())
                    hr = S_FALSE;
            }

            // then see if we have langpack for
            // the family codepage
            if (hr == S_OK)
            {
                CLSID      clsid;
                // clsid is just used for place holder
                hr = _GetJITClsIDForCodePage(uiFamCp, &clsid);
            }
        }
    }
    return hr;
}

STDAPI CMultiLanguage2::SetMimeDBSource(MIMECONTF dwSource)
{        
        if ((dwSource != MIMECONTF_MIME_IE4) &&
            (dwSource != MIMECONTF_MIME_LATEST) &&
            (dwSource != MIMECONTF_MIME_REGISTRY))
        {
            return E_INVALIDARG;
        }

        if (dwSource & MIMECONTF_MIME_REGISTRY)
        {
            EnterCriticalSection(&g_cs);
            if (!g_pMimeDatabaseReg)
            {
                g_pMimeDatabaseReg = new CMimeDatabaseReg;
            }
            LeaveCriticalSection(&g_cs);
        }

        dwMimeSource = dwSource;
        if (NULL != m_pMimeDatabase)
            m_pMimeDatabase->SetMimeDBSource(dwSource);
        return S_OK;
}

CMultiLanguage2::CMultiLanguage2(void)
{
        DllAddRef();
        
        CComCreator< CComPolyObject< CMultiLanguage > >::CreateInstance( NULL, IID_IMultiLanguage, (void **)&m_pIML );

        m_pMimeDatabase = new CMimeDatabase;
        dwMimeSource = MIMECONTF_MIME_LATEST;
        if (m_pMimeDatabase)
            m_pMimeDatabase->SetMimeDBSource(MIMECONTF_MIME_LATEST);

        m_pIEStat = NULL;
}

CMultiLanguage2::~CMultiLanguage2(void)
{
        if (m_pIML)
        {
            m_pIML->Release();
            m_pIML = NULL;
        }

        if (m_pMimeDatabase)
        {
            delete m_pMimeDatabase;
        }

        if (m_pIEStat)
        {
            delete m_pIEStat;
        }

        DllRelease();
}

STDAPI CMultiLanguage2::GetNumberOfCodePageInfo(UINT *pcCodePage)
{
        if (dwMimeSource &  MIMECONTF_MIME_REGISTRY)         
        {
            if (NULL != g_pMimeDatabaseReg)
                return g_pMimeDatabaseReg->GetNumberOfCodePageInfo(pcCodePage);
            else
                return E_FAIL;
        }    
        else
        {    
            if (NULL != m_pMimeDatabase)
                return m_pMimeDatabase->GetNumberOfCodePageInfo(pcCodePage);
            else
                return E_FAIL;
        }
}

STDAPI CMultiLanguage2::GetNumberOfScripts(UINT *pnScripts)
{
    if (pnScripts)
        *pnScripts = g_cScript;

    return NOERROR;
}


STDAPI CMultiLanguage2::GetCodePageInfo(UINT uiCodePage, LANGID LangId, PMIMECPINFO pcpInfo)
{
    if (dwMimeSource &  MIMECONTF_MIME_REGISTRY)
    {
        if (NULL != g_pMimeDatabaseReg)
            return g_pMimeDatabaseReg->GetCodePageInfo(uiCodePage, pcpInfo);
        else
            return E_FAIL;
    }
    else
    {
        if (m_pMimeDatabase)
            return m_pMimeDatabase->GetCodePageInfo(uiCodePage, LangId, pcpInfo);
        else
            return E_FAIL;
    }
}

// Optimized for performance
// Skip unecessary resource loading
STDAPI CMultiLanguage2::GetFamilyCodePage(UINT uiCodePage, UINT *puiFamilyCodePage)
{
        HRESULT hr = S_OK;
        int idx = 0;

        if (puiFamilyCodePage)
            *puiFamilyCodePage = 0;
        else
            return E_INVALIDARG;

        DebugMsg(DM_TRACE, TEXT("CMultiLanguage2::GetFamilyCodePage called."));
        // Keep registry version IE4 implementation
        if (dwMimeSource &  MIMECONTF_MIME_REGISTRY)
        {

            if (NULL != g_pMimeDatabaseReg)
            {
                MIMECPINFO cpInfo;
                hr = g_pMimeDatabaseReg->GetCodePageInfo(uiCodePage, &cpInfo);
                if (S_OK == hr)
                    *puiFamilyCodePage = cpInfo.uiFamilyCodePage;
            }
        }
        else
        {
            while(MimeCodePage[idx].uiCodePage)
            {
                if ((uiCodePage == MimeCodePage[idx].uiCodePage) &&
                    (MimeCodePage[idx].dwFlags & dwMimeSource))
                    break;
                idx++;
            }

            if (MimeCodePage[idx].uiCodePage)
            {
                if (MimeCodePage[idx].uiFamilyCodePage)
                    *puiFamilyCodePage = MimeCodePage[idx].uiFamilyCodePage;
                else
                    *puiFamilyCodePage = uiCodePage;
            }
            else
            {
                hr = E_FAIL;
            }
        }
        return hr;
}

STDAPI CMultiLanguage2::GetCharsetInfo(BSTR Charset, PMIMECSETINFO pcsetInfo)
{
    if (dwMimeSource &  MIMECONTF_MIME_REGISTRY)
    {
        if (NULL != g_pMimeDatabaseReg)
            return g_pMimeDatabaseReg->GetCharsetInfo(Charset, pcsetInfo);
        else
            return E_FAIL;
    }

    if (NULL != m_pMimeDatabase)
        return m_pMimeDatabase->GetCharsetInfo(Charset, pcsetInfo);
    else
        return E_FAIL;
}

//
// System default code page stack
//
// We support following code pages for outbound encoding detection
//      Windows  : 1252, 1250, 1251, 1253, 1254, 1257, 1258, 1256, 1255, 874, 932, 949, 950, 936
//      Unicode  : 65001, 65000, 1200
//      ISO      : 28591, 28592, 20866, 21866, 28595, 28597, 28593, 28594, 28596, 28598, 38598, 28605, 28599
//      Others   : 20127, 50220, 50221, 50222, 51932, 51949, 50225, 52936
//
// Default priorities
//       20127 > Windows single byte code page> ISO > Windows DBCS code page > Others > Unicode
//
UINT SysPreCp[] = 
    {20127, 
    1252, 1250, 1251, 1253, 1254, 1257, 1258, 1256, 1255, 874, 
    28591, 28592, 20866, 21866, 28595, 28597, 28593, 28594, 28596, 28598, 38598, 28605, 28599,
    932, 949, 950, 936,
    50220, 50221, 50222, 51932, 51949, 50225, 52936, 
    65001, 65000, 1200 };
            
//
// IMultiLanguage3
// Outbound encoding detection for plain Unicode text encoding detection
// We ride on CMultiLanguage2 class to implement this funciton
//
STDAPI CMultiLanguage2::DetectOutboundCodePage(
            DWORD   dwFlags,                // Flags control our behaviour
            LPCWSTR lpWideCharStr,          // Source Unicode string
            UINT    cchWideChar,            // Source Unicode character size
            UINT*   puiPreferredCodePages,  // Preferred code page array  
            UINT    nPreferredCodePages,    // Number of preferred code pages
            UINT*   puiDetectedCodePages,   // Detected code page arrayNumber of detected code pages
            UINT*   pnDetectedCodePages,    // [in] Maxium number of code pages we can return
                                            // [out] Num of detected code pages
            WCHAR*  lpSpecialChar           // Optional NULL terminated Unicode string for client specified special chars
            )
{
    DWORD dwCodePages = 0, dwCodePagesExt = 0;
    LONG lNum1 = 0, lNum2 = 0;
    HRESULT hr = E_FAIL;
    UINT ui;
    DWORD dwStrFlags;
    LPWSTR lpwszTmp = NULL;

    // Parameter checks
    if (!cchWideChar || !lpWideCharStr || !puiDetectedCodePages || !*pnDetectedCodePages)
        return E_INVALIDARG;

    // We need extra buffer to perform best fit char filtering
    if (dwFlags & MLDETECTF_FILTER_SPECIALCHAR)
        lpwszTmp = (LPWSTR) LocalAlloc(LMEM_FIXED, sizeof(WCHAR)*cchWideChar);

    // String sniffing for CJK, HINDI and BESTFIT
    dwStrFlags = OutBoundDetectPreScan((WCHAR *)lpWideCharStr, cchWideChar, lpwszTmp, lpSpecialChar);

    hr = GetStrCodePagesEx(lpwszTmp? lpwszTmp:lpWideCharStr, cchWideChar, 0, &dwCodePages, &lNum1, CPBITS_WINDOWS|CPBITS_STRICT);
    if (SUCCEEDED(hr))
        hr = GetStrCodePagesEx(lpwszTmp? lpwszTmp:lpWideCharStr, cchWideChar, 0, &dwCodePagesExt,&lNum2, CPBITS_EXTENDED|CPBITS_STRICT);

    // Clear bits if it is not a complete pass
    if ((UINT)lNum1 != cchWideChar)
        dwCodePages = 0;

    if ((UINT)lNum2 != cchWideChar)
        dwCodePagesExt = 0;

    if (lpwszTmp)
        LocalFree(lpwszTmp);

    // If Hindi, we don't return any non-Unicode code pages since there is no offical ones 
    // and we don't recomment client to render Hindi text in ANSI
    if (dwStrFlags & (FS_HINDI|FS_PUA))
    {
        dwCodePages = 0;
        dwCodePagesExt = 0;
    }    

    dwCodePagesExt |= FS_MLANG_65001;
    dwCodePagesExt |= FS_MLANG_65000;
    dwCodePagesExt |= FS_MLANG_1200;

    if (dwCodePagesExt & FS_MLANG_28598)
        dwCodePagesExt |= FS_MLANG_38598;
    if (dwCodePagesExt & FS_MLANG_50220)
        dwCodePagesExt |= FS_MLANG_50221|FS_MLANG_50222;


    if (SUCCEEDED(hr))
    {    
        DWORD dwTempCodePages;
        DWORD dwTempCodePages2;
        UINT nCp = 0;

        // Pick preferred code pages first
        if (nPreferredCodePages && puiPreferredCodePages)
        for (ui=0; nCp<*pnDetectedCodePages && (dwCodePages | dwCodePagesExt) && ui<nPreferredCodePages; ui++)
        {
            if (S_OK == CodePageToCodePagesEx(puiPreferredCodePages[ui], &dwTempCodePages, &dwTempCodePages2))
            {
                if (dwTempCodePages & dwCodePages)
                {
                    puiDetectedCodePages[nCp] = puiPreferredCodePages[ui];
                    dwCodePages &= ~dwTempCodePages;
                    nCp++;

                }
                else if (dwTempCodePages2 & dwCodePagesExt)
                {
                    puiDetectedCodePages[nCp] = puiPreferredCodePages[ui];
                    dwCodePagesExt &= ~dwTempCodePages2;
                    nCp++;
                }
            }
        }

        // Fill in non-preferred code pages if we still have space in destination buffer
        if (!((dwFlags & MLDETECTF_PREFERRED_ONLY) && nPreferredCodePages && puiPreferredCodePages))
        {
            for (ui=0; nCp<*pnDetectedCodePages && (dwCodePages | dwCodePagesExt) && ui < sizeof(SysPreCp)/sizeof(UINT); ui++)
            {
                if (S_OK == CodePageToCodePagesEx(SysPreCp[ui], &dwTempCodePages, &dwTempCodePages2))
                {
                    if (dwTempCodePages & dwCodePages)
                    {
                        puiDetectedCodePages[nCp] = SysPreCp[ui];
                        dwCodePages &= ~dwTempCodePages;
                        nCp++;

                    }
                    else if (dwTempCodePages2 & dwCodePagesExt)
                    {
                        puiDetectedCodePages[nCp] = SysPreCp[ui];
                        dwCodePagesExt &= ~dwTempCodePages2;
                        nCp++;
                    }
                }
            }
        }

        
        // Smart adjustment for DBCS
        // If string doesn't contains CJK characters, we bump up UTF8
        if (!(dwFlags & MLDETECTF_PRESERVE_ORDER) && !(dwStrFlags & FS_CJK) && (puiDetectedCodePages[0] == 932||
            puiDetectedCodePages[0] == 936||puiDetectedCodePages[0] == 950||puiDetectedCodePages[0] == 949))
        {
            for (ui = 1; ui < nCp; ui++)
            {
                if (puiDetectedCodePages[ui] == 65001) //Swap
                {
                    MoveMemory((LPVOID)(puiDetectedCodePages+1), (LPVOID)(puiDetectedCodePages), ui*sizeof(UINT));
                    puiDetectedCodePages[0] = 65001;
                    break;
                }
            }
        }

        // Check validation
        if (dwFlags & MLDETECTF_VALID || dwFlags & MLDETECTF_VALID_NLS)
        {
            MIMECPINFO cpInfo;
            UINT * puiBuffer = puiDetectedCodePages;

            if (!g_pMimeDatabase)
                BuildGlobalObjects();

            if (g_pMimeDatabase)
            {
                for (ui = 0; ui < nCp; ui++)
                {
                    if (SUCCEEDED(g_pMimeDatabase->GetCodePageInfo(puiDetectedCodePages[ui], 0x0409, &cpInfo)))
                    {
                        if ((cpInfo.dwFlags & MIMECONTF_VALID)  || 
                            ((cpInfo.dwFlags & MIMECONTF_VALID_NLS) && (dwFlags & MLDETECTF_VALID_NLS)))
                        {
                            // In place adjustment
                            *puiBuffer = puiDetectedCodePages[ui];
                            puiBuffer++;
                        }
                    }
                }
                nCp =(UINT) (puiBuffer-puiDetectedCodePages);                
            }
        }

        // Be nice, clean up detection buffer for client
        if (nCp < *pnDetectedCodePages)
            ZeroMemory(&puiDetectedCodePages[nCp], *pnDetectedCodePages-nCp);

        *pnDetectedCodePages = nCp;
    }

    return hr;
    
}

// IStream object
STDAPI CMultiLanguage2::DetectOutboundCodePageInIStream(
            DWORD        dwFlags,                // Detection flags
            IStream*     pStmIn,                 // IStream object pointer
            UINT*        puiPreferredCodePages,  // Preferred code page array  
            UINT         nPreferredCodePages,    // Num of preferred code pages
            UINT*        puiDetectedCodePages,   // Buffer for detection result
            UINT*        pnDetectedCodePages,    // [in] Maxium number of code pages we can return
                                                 // [out] Num of detected code pages
            WCHAR*       lpSpecialChar           // Optional NULL terminated Unicode string for client specified special chars
            )                               

{
    HRESULT hr;
    LARGE_INTEGER  libOrigin = { 0, 0 };
    ULARGE_INTEGER ulPos = {0, 0};
    ULONG   ulSrcSize;
    CHAR    *pStr;    

    // Get buffer size
    hr = pStmIn->Seek(libOrigin, STREAM_SEEK_END,&ulPos);

    if (SUCCEEDED(hr))
    {
        ulSrcSize = ulPos.LowPart ;
        if (pStr=(char *)LocalAlloc(LPTR, ulSrcSize))
        {
            // Reset the pointer
            hr = pStmIn->Seek(libOrigin, STREAM_SEEK_SET, NULL);
            if (S_OK == hr)
            {
                hr = pStmIn->Read(pStr, ulSrcSize, &ulSrcSize);
                if (S_OK == hr)
                    hr = DetectOutboundCodePage(dwFlags, (LPCWSTR)pStr, ulSrcSize/sizeof(WCHAR), puiPreferredCodePages, 
                                                nPreferredCodePages, puiDetectedCodePages, pnDetectedCodePages, lpSpecialChar);
            }
            LocalFree(pStr);
        }
        else
            hr = E_OUTOFMEMORY;
    }

    return hr;
}