/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    dbcs.c

Abstract:

    This module contains the code for console DBCS font dialog

Author:

    kazum Feb-27-1995

Revision History:

--*/

#include "shellprv.h"
#pragma hdrstop

#include "lnkcon.h"

#ifdef DBCS

// This definition shares in windows\inc\wincon.w file
//
#define MACHINE_REGISTRY_CONSOLE_TTFONT (L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Console\\TrueTypeFont")

#define MACHINE_REGISTRY_CONSOLE_NLS    (L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Console\\Nls")


NTSTATUS
MyRegOpenKey(
    IN HANDLE hKey,
    IN LPWSTR lpSubKey,
    OUT PHANDLE phResult
    )
{
    OBJECT_ATTRIBUTES   Obja;
    UNICODE_STRING      SubKey;

    //
    // Convert the subkey to a counted Unicode string.
    //

    RtlInitUnicodeString( &SubKey, lpSubKey );

    //
    // Initialize the OBJECT_ATTRIBUTES structure and open the key.
    //

    InitializeObjectAttributes(
        &Obja,
        &SubKey,
        OBJ_CASE_INSENSITIVE,
        hKey,
        NULL
        );

    return NtOpenKey(
              phResult,
              KEY_READ,
              &Obja
              );
}

NTSTATUS
MyRegEnumValue(
    IN HANDLE hKey,
    IN DWORD dwIndex,
    OUT DWORD dwValueLength,
    OUT LPWSTR lpValueName,
    OUT DWORD dwDataLength,
    OUT LPBYTE lpData
    )
{
    ULONG BufferLength;
    ULONG ResultLength;
    PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
    NTSTATUS Status;

    //
    // Convert the subkey to a counted Unicode string.
    //

    BufferLength = sizeof(KEY_VALUE_FULL_INFORMATION) + dwValueLength + dwDataLength;
    KeyValueInformation = LocalAlloc(LPTR,BufferLength);
    if (KeyValueInformation == NULL)
        return STATUS_NO_MEMORY;

    Status = NtEnumerateValueKey(
                hKey,
                dwIndex,
                KeyValueFullInformation,
                KeyValueInformation,
                BufferLength,
                &ResultLength
                );
    if (NT_SUCCESS(Status)) {
        ASSERT(KeyValueInformation->NameLength <= dwValueLength);
        RtlMoveMemory(lpValueName,
                      KeyValueInformation->Name,
                      KeyValueInformation->NameLength);
        lpValueName[ KeyValueInformation->NameLength >> 1 ] = UNICODE_NULL;


        ASSERT(KeyValueInformation->DataLength <= dwDataLength);
        RtlMoveMemory(lpData,
            (PBYTE)KeyValueInformation + KeyValueInformation->DataOffset,
            KeyValueInformation->DataLength);
        if (KeyValueInformation->Type == REG_SZ ||
            KeyValueInformation->Type == REG_MULTI_SZ
           ) {
            if (KeyValueInformation->DataLength + sizeof(WCHAR) > dwDataLength) {
                KeyValueInformation->DataLength -= sizeof(WCHAR);
            }
            lpData[KeyValueInformation->DataLength++] = 0;
            lpData[KeyValueInformation->DataLength] = 0;
        }
    }
    LocalFree(KeyValueInformation);
    return Status;
}





WORD
ConvertStringToDec(
    LPWSTR lpch,
    LPWSTR *endptr
    )
{
    WCHAR ch;
    WORD val = 0;

    while ( (ch=*lpch) != L'\0')
    {
        if (L'0' <= ch && ch <= L'9')
            val = (val * 10) + (ch - L'0');
        else
            break;

        lpch++;
    }

    if (endptr)
        *endptr = lpch;
    return val;
}

WORD
ConvertStringToHex(
    LPWSTR lpch,
    LPWSTR *endptr
    )
{
    WCHAR ch;
    WORD val = 0;

    while ( (ch=*lpch) != L'\0')
    {
        if (L'0' <= ch && ch <= L'9')
            val = (val << 4) + (ch - L'0');
        else if (L'A' <= ch && ch <= L'F')
            val = (val << 4) + (ch - L'A' + 10);
        else if (L'a' <= ch && ch <= L'f')
            val = (val << 4) + (ch - L'a' + 10);
        else
            break;

        lpch++;
    }

    if (endptr)
        *endptr = lpch;
    return val;
}


NTSTATUS
MakeAltRasterFont(
    CONSOLEPROP_DATA * pcpd,
    UINT CodePage,
    COORD *AltFontSize,
    BYTE  *AltFontFamily,
    ULONG *AltFontIndex,
    LPTSTR AltFaceName
    )
{
    DWORD i;
    DWORD Find;
    ULONG FontIndex;
    COORD FontSize = pcpd->FontInfo[pcpd->DefaultFontIndex].Size;
    COORD FontDelta;
    BOOL  fDbcsCharSet = IS_ANY_DBCS_CHARSET( CodePageToCharSet( CodePage ) );

    FontIndex = 0;
    Find = (DWORD)-1;
    for (i=0; i < pcpd->NumberOfFonts; i++)
    {
        if (!TM_IS_TT_FONT(pcpd->FontInfo[i].Family) &&
            IS_ANY_DBCS_CHARSET(pcpd->FontInfo[i].tmCharSet) == fDbcsCharSet
           )
        {
            FontDelta.X = (SHORT)abs(FontSize.X - pcpd->FontInfo[i].Size.X);
            FontDelta.Y = (SHORT)abs(FontSize.Y - pcpd->FontInfo[i].Size.Y);
            if (Find > (DWORD)(FontDelta.X + FontDelta.Y))
            {
                Find = (DWORD)(FontDelta.X + FontDelta.Y);
                FontIndex = i;
            }
        }
    }

    *AltFontIndex = FontIndex;
    lstrcpy(AltFaceName, pcpd->FontInfo[*AltFontIndex].FaceName);
    *AltFontSize = pcpd->FontInfo[*AltFontIndex].Size;
    *AltFontFamily = pcpd->FontInfo[*AltFontIndex].Family;

    return STATUS_SUCCESS;
}

NTSTATUS
InitializeDbcsMisc(
    CONSOLEPROP_DATA * pcpd
    )
{
    HANDLE hkRegistry = NULL;
    NTSTATUS Status;
    WCHAR awchValue[ 512 ];
    WCHAR awchData[ 512 ];
    DWORD dwIndex;
    LPWSTR pwsz;

    pcpd->gTTFontList.Next = NULL;

    Status = MyRegOpenKey(NULL,
                          MACHINE_REGISTRY_CONSOLE_TTFONT,
                          &hkRegistry);
    if (NT_SUCCESS( Status )) {
        TTFONTLIST *pTTFontList;

        for( dwIndex = 0; ; dwIndex++) {
            Status = MyRegEnumValue(hkRegistry,
                                    dwIndex,
                                    sizeof(awchValue), (LPWSTR)&awchValue,
                                    sizeof(awchData),  (PBYTE)&awchData);
            if (!NT_SUCCESS( Status )) {
                break;
            }

            pTTFontList = LocalAlloc(LPTR, sizeof(TTFONTLIST));
            if (pTTFontList == NULL) {
                break;
            }

            pTTFontList->List.Next = NULL;
            pTTFontList->CodePage = ConvertStringToDec(awchValue, NULL);
            pwsz = awchData;
            if (*pwsz == BOLD_MARK) {
                pTTFontList->fDisableBold = TRUE;
                pwsz++;
            }
            else
                pTTFontList->fDisableBold = FALSE;
#ifdef UNICODE
            lstrcpyW(pTTFontList->FaceName1, pwsz);

            pwsz += lstrlenW(pwsz) + 1;
            if (*pwsz == BOLD_MARK)
            {
                pTTFontList->fDisableBold = TRUE;
                pwsz++;
            }
            lstrcpyW(pTTFontList->FaceName2, pwsz);
#else
            // if we're the ANSI shell, we need to convert FACENAME
            // over to ASCII before saving...
            {
                CHAR szFaceName[LF_FACESIZE];
                SHUnicodeToAnsi(pwsz, szFaceName, ARRAYSIZE(szFaceName));
                lstrcpyA(pTTFontList->FaceName1, szFaceName);

                pwsz += lstrlenW(pwsz) + 1;
                if (*pwsz == BOLD_MARK)
                {
                    pTTFontList->fDisableBold = TRUE;
                    pwsz++;
                }
                SHUnicodeToAnsi(pwsz, szFaceName, ARRAYSIZE(szFaceName));
                lstrcpyA(pTTFontList->FaceName2, szFaceName);
            }
#endif

            PushEntryList(&pcpd->gTTFontList, &(pTTFontList->List));
        }

        NtClose(hkRegistry);
    }

    pcpd->fChangeCodePage = FALSE;
    pcpd->uOEMCP = GetOEMCP();

    return STATUS_SUCCESS;
}

BYTE
CodePageToCharSet(
    UINT CodePage
    )
{
    CHARSETINFO csi;

    if (!TranslateCharsetInfo((DWORD *)UIntToPtr( CodePage ), &csi, TCI_SRCCODEPAGE)) // Sundown: valid zero-extension of CodePage for TCI_SRCCOPAGE.
        csi.ciCharset = OEM_CHARSET;

    return (BYTE)csi.ciCharset;
}

TTFONTLIST *SearchTTFont(CONSOLEPROP_DATA * pcpd, LPTSTR ptszFace, BOOL fCodePage, UINT CodePage)
{
    PSINGLE_LIST_ENTRY pTemp = pcpd->gTTFontList.Next;

    if (ptszFace) {
        while (pTemp != NULL) {
            TTFONTLIST *pTTFontList = (TTFONTLIST *)pTemp;

            if (wcscmp(ptszFace, pTTFontList->FaceName1) == 0 ||
                wcscmp(ptszFace, pTTFontList->FaceName2) == 0    ) {
                if (fCodePage)
                    if (pTTFontList->CodePage == CodePage )
                        return pTTFontList;
                    else
                        return NULL;
                else
                    return pTTFontList;
            }

            pTemp = pTemp->Next;
        }
    }

    return NULL;
}

BOOL
IsAvailableTTFont(
    CONSOLEPROP_DATA * pcpd,
    LPTSTR ptszFace
    )
{
    if (SearchTTFont(pcpd, ptszFace, FALSE, 0))
        return TRUE;
    else
        return FALSE;
}

BOOL
IsAvailableTTFontCP(
    CONSOLEPROP_DATA * pcpd,
    LPTSTR ptszFace,
    UINT CodePage
    )
{
    if (SearchTTFont(pcpd, ptszFace, TRUE, CodePage))
        return TRUE;
    else
        return FALSE;
}

BOOL
IsDisableBoldTTFont(
    CONSOLEPROP_DATA * pcpd,
    LPTSTR ptszFace
    )
{
    TTFONTLIST *pTTFontList;

    pTTFontList = SearchTTFont(pcpd, ptszFace, FALSE, 0);
    if (pTTFontList != NULL)
        return pTTFontList->fDisableBold;
    else
        return FALSE;
}

LPTSTR
GetAltFaceName(
    CONSOLEPROP_DATA * pcpd,
    LPTSTR ptszFace
    )
{
    TTFONTLIST *pTTFontList;

    pTTFontList = SearchTTFont(pcpd, ptszFace, FALSE, 0);
    if (pTTFontList) {
        if (wcscmp(ptszFace, pTTFontList->FaceName1) == 0) {
            return pTTFontList->FaceName2;
        }
        if (wcscmp(ptszFace, pTTFontList->FaceName2) == 0) {
            return pTTFontList->FaceName1;
        }
        return NULL;
    }
    else
        return NULL;
}

NTSTATUS DestroyDbcsMisc(CONSOLEPROP_DATA * pcpd)
{
    while (pcpd->gTTFontList.Next != NULL) 
    {
        TTFONTLIST *pTTFontList = (TTFONTLIST *)PopEntryList(&pcpd->gTTFontList);

        if (pTTFontList != NULL)
            LocalFree(pTTFontList);
    }

    return STATUS_SUCCESS;
}

typedef struct _LC_List {
    struct _LC_List* Next;
    BOOL   FindFlag;
    WCHAR  LC_String[9];
} LC_List, *PLC_List;

static PLC_List LocaleList;

BOOL CALLBACK
EnumProc(
    LPWSTR LC_String
    )
{
    PLC_List TmpList;

    if (lstrlenW(LC_String) <= (sizeof(LocaleList->LC_String)/sizeof(WCHAR))-1)
    {
        TmpList = (PLC_List)&LocaleList;

        while(TmpList->Next != NULL)
            TmpList = TmpList->Next;

        TmpList->Next = LocalAlloc(LPTR, sizeof(LC_List));
        if (TmpList->Next != NULL)
        {
            TmpList = TmpList->Next;
            lstrcpyW(TmpList->LC_String, LC_String);
        }
    }
    return TRUE;
}


int
LanguageListCreate(
    HWND hDlg,
    UINT CodePage
    )

/*++

    Initializes the Language list by enumerating all Locale Information.

    Returns
--*/

{
    HWND hWndLanguageCombo;
    HANDLE hkRegistry = NULL;
    NTSTATUS Status;
    WCHAR awchValue[ 512 ];
    WCHAR awchData[ 512 ];
    DWORD dwIndex;
    PLC_List TmpList;
    WORD LangID;
    LCID Locale;
    int  cchData;
    LONG lListIndex;
    UINT cp;

    ENTERCRITICAL;

    /*
     * Enumrate system locale information
     */
    EnumSystemLocalesW( EnumProc, CP_INSTALLED );

    /*
     * Enumrate registory key
     */
    Status = MyRegOpenKey(NULL,
                          MACHINE_REGISTRY_CONSOLE_NLS,
                          &hkRegistry);
    if (NT_SUCCESS( Status )) {
        for( dwIndex = 0; ; dwIndex++)
        {
            Status = MyRegEnumValue(hkRegistry,
                                    dwIndex,
                                    sizeof(awchValue), (LPWSTR)&awchValue,
                                    sizeof(awchData),  (PBYTE)&awchData);
            if (!NT_SUCCESS( Status ))
            {
                break;
            }

            TmpList = (PLC_List)&LocaleList;
            while(TmpList->Next != NULL)
            {
                TmpList = TmpList->Next;
                if (lstrcmpW(awchValue, TmpList->LC_String) == 0)
                {
                    TmpList->FindFlag = TRUE;
                    break;
                }
            }
        }

        NtClose(hkRegistry);

    }

    /*
     * Create ComboBox items
     */
    hWndLanguageCombo = GetDlgItem(hDlg, IDC_CNSL_LANGUAGELIST);
    SendMessage(hWndLanguageCombo, CB_RESETCONTENT, 0, 0L);

    TmpList = (PLC_List)&LocaleList;
    while(TmpList->Next != NULL)
    {
        TmpList = TmpList->Next;

        if (TmpList->FindFlag)
        {
            LangID = ConvertStringToHex(TmpList->LC_String, NULL);
            Locale = MAKELCID( LangID, SORT_DEFAULT );

            awchValue[0] = L'\0';
            cp = 0;

            {
                #define KERNEL32    _T("KERNEL32.DLL")

                #ifdef UNICODE
                #define GETCPINFOEX "GetCPInfoExW"
                #else
                #define GETCPINFOEX "GetCPInfoExA"
                #endif

                typedef BOOL (CALLBACK *LPFNGETCPINFOEX)(UINT, DWORD, LPCPINFOEX);
                LPFNGETCPINFOEX lpfnGetCPInfoEx;

                BOOL fRet = FALSE;
                CPINFOEX cpinfo;

                HMODULE hMod;

                cchData = GetLocaleInfoW(Locale, LOCALE_IDEFAULTCODEPAGE,
                                         awchData, sizeof(awchData)/sizeof(TCHAR));
                if (cchData)
                {
                    cp = ConvertStringToDec(awchData, NULL);

                    hMod = GetModuleHandle(KERNEL32);
                    if (hMod) {
                        lpfnGetCPInfoEx = (LPFNGETCPINFOEX)GetProcAddress(hMod,GETCPINFOEX);
                        if (lpfnGetCPInfoEx)
                            fRet = (*lpfnGetCPInfoEx)(cp, 0, &cpinfo);
                    }
                    if (fRet) {
                        lListIndex = (LONG) SendMessageW(hWndLanguageCombo, CB_ADDSTRING, 0, (LPARAM)cpinfo.CodePageName);
                        SendMessage(hWndLanguageCombo, CB_SETITEMDATA, (DWORD)lListIndex, cp);

                        if (CodePage == cp) {
                            SendMessage(hWndLanguageCombo, CB_SETCURSEL, lListIndex, 0L);
                        }
                    }
                }
            }

            if (CodePage == cp) {
                SendMessage(hWndLanguageCombo, CB_SETCURSEL, lListIndex, 0L);
            }

        }
    }

    {
        PLC_List Tmp;

        TmpList = (PLC_List)&LocaleList;
        while(TmpList->Next != NULL)
        {
            Tmp = TmpList;
            TmpList = TmpList->Next;

            if (Tmp != (PLC_List)&LocaleList)
                LocalFree(Tmp);
        }

        LocaleList = NULL;
    }

    LEAVECRITICAL;

    /*
     * Get the LocaleIndex from the currently selected item.
     * (i will be LB_ERR if no currently selected item).
     */
    lListIndex = (LONG) SendMessage(hWndLanguageCombo, CB_GETCURSEL, 0, 0L);
    return (int) SendMessage(hWndLanguageCombo, CB_GETITEMDATA, lListIndex, 0L);
}
#endif // DBCS
