//
// Copyright (c) 2002
// Ronald Kevin Burton
//
// Z poniższym kodem nie jest związana żadna gwarancja poprawności działania.
// Program został dołączony do książki ".NET CLR. Księga eksperta" w celu
// ilustracji koncepcji i zasad przedstawionych w tej książce. Program może być 
// używany na własne ryzyko.
//
// Przyznaje się prawo do użycia lub kopiowania tego oprogramowania do dowolnego celu
// bez konieczności ponoszenia żadnych opłat pod warunkiem, że powyższe uwagi zostaną 
// zachowane we wszystkich kopiach. Przyznaje się także prawo do modyfikacji kodu
// i dystrybucji zmodyfikowanego kodu pod warunkiem zachowania powyższych uwag
// oraz dołączenia informacji mówiącej o modyfikacji kodu.
//
// 
// UserStringHeap.cpp : plik implementacji
//

#include "stdafx.h"
#include "AssemblyDoc.h"
#include "AssemblyView.h"
#include "UserStringHeapPage.h"


// Okno dialogowe CUserStringHeap

IMPLEMENT_DYNAMIC(CUserStringHeapPage, CPropertyPage)
CUserStringHeapPage::CUserStringHeapPage()
	: CPropertyPage(CUserStringHeapPage::IDD)
{
}

CUserStringHeapPage::~CUserStringHeapPage()
{
}

void CUserStringHeapPage::DoDataExchange(CDataExchange* pDX)
{
	CPropertyPage::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_USERSTRINGLIST, m_ctrlUserStringList);
}


BEGIN_MESSAGE_MAP(CUserStringHeapPage, CPropertyPage)
END_MESSAGE_MAP()


// Procedury obsługi wiadomości CUserStringHeap

ULONG CountCharacters(WCHAR *str, ULONG strLength, bool *bUnprint)
{
	ULONG count = 0;
	*bUnprint = false;
	while (strLength)
	{   
		switch (*str)
		{
		case 0:
		case L'\r':
		case L'\n':
		case L'\t':
			count += 2;
			break;
		default:
			if (iswprint(*str))
				count += 1;
			else 
			{
				*bUnprint = true;
				count += 1;
			}
			break;
		}
		++str;
		--strLength;
	}
	return count;
}

void CUserStringHeapPage::DumpUserStrings(IMetaDataImport* pImport, CListCtrl *pList)
{
    HCORENUM    stringEnum = NULL;      // Enumerator ciągu.
    mdString    Strings[ENUM_BUFFER_SIZE]; // Żetony ciągów z enumeratora.
    WCHAR		rUserString[MAX_STRING_LENGTH];			// Bufor do odbioru ciągów.
    WCHAR       *szUserString;          // Roboczy wskaźnik bufora.
    ULONG       chUserString;           // Długość ciągu użytkownika.
    ULONG       chBuf;                  // Zapisana długość ciągu użytkownika.
    ULONG       count;                  // Elementy zwrócone z enumeratora.
    ULONG       totalCount = 1;         // Bieżący licznik ciągów.
	ULONG       itemIndex = (ULONG)-1;
	TCHAR       cBuffer[64];
    bool        bUnprint = false;       // Czy znaleziono znak, który nie jest wyświetlany?
    HRESULT     hr;                     // Wynik.

	memset(rUserString, 0, sizeof(rUserString));

    while (SUCCEEDED(hr = pImport->EnumUserStrings(&stringEnum,
                                                   Strings,
												   NumItems(Strings),
												   &count)) &&
           count > 0)
    {
        if (totalCount == 1)
        {
			// Jeśli licznik ma wartość 1, to jest to pusty ciąg, który nie musi być wyświetlany.
        }
        for (ULONG i = 0; i < count; i++, totalCount++)
        {
            do
			{ 
				// Umieszczenie ciągu w istniejącym buforze.
                hr = pImport->GetUserString( Strings[i],
					                         rUserString,
											 sizeof(rUserString), 
											 &chUserString);
                if (hr == CLDB_S_TRUNCATION)
                {   
					// Za mały bufor, próba powiększenia.
					TRACE(_T("Za mały bufor, próba powiększenia.\n"));
                    continue;
                }
            } while (0);

            if (FAILED(hr))
			{
				TRACE(_T("Działanie GetUserString nie powiodło się.\n"));
			}

			// Ciąg
            szUserString = rUserString;
			// Długość ciągu
            chBuf = chUserString;

			wsprintf(cBuffer, _T("%d"), i);
			itemIndex = pList->InsertItem(itemIndex + 1, cBuffer);

			wsprintf(cBuffer, _T("0x%8X"),  Strings[i]);
			pList->SetItemText(itemIndex, 1, cBuffer);

			ULONG stringLength = CountCharacters(szUserString, chUserString, &bUnprint);

			LPWSTR stringBuffer = new WCHAR[stringLength + 1];
			memset(stringBuffer, 0, stringLength + 1);
            while (chUserString)
            {   
                switch (*szUserString)
                {
                case 0:
                    wcscat(stringBuffer, L"\\0");
					break;
                case L'\r':
                    wcscat(stringBuffer, L"\\r");
					break;
                case L'\n':
                    wcscat(stringBuffer, L"\\n");
					break;
                case L'\t':
                    wcscat(stringBuffer, L"\\t");
					break;
                default:
                    if (iswprint(*szUserString))
					{
 					   wsprintf(cBuffer, _T("%lc"),  *szUserString);
                       wcscat(stringBuffer, cBuffer);
					}
                    else 
                    {
                        bUnprint = true;
						wcscat(stringBuffer, L".");
                    }
                    break;
                }
                ++szUserString;
                --chUserString;
            }
			pList->SetItemText(itemIndex, 2, stringBuffer);
			delete [] stringBuffer;
		}
    }
    if (stringEnum != NULL)
        pImport->CloseEnum(stringEnum);
}


BOOL CUserStringHeapPage::OnInitDialog()
{
	CPropertyPage::OnInitDialog();

	CString strItemLabel = _T("Item");
	CString strIndex = _T("Token");
	CString strString = _T("String");

	CRect rect;
	// Wstawienie trzech kolumn (tryb raportu) i modyfikacja nowych elementów nagłówka
	m_ctrlUserStringList.GetWindowRect(&rect);
	m_ctrlUserStringList.InsertColumn(0, strItemLabel, LVCFMT_LEFT,
		                              rect.Width() * 1/6, 0);
	m_ctrlUserStringList.InsertColumn(1, strIndex, LVCFMT_LEFT,
		                              rect.Width() * 1/6, 1);
	m_ctrlUserStringList.InsertColumn(2, strString, LVCFMT_LEFT,
		                              rect.Width() * 2/3, 2);

	// Wypełnienie danymi
	CPropertySheet *pPropertySheet = STATIC_DOWNCAST(CPropertySheet, GetParent());
	CAssemblyView* pView = STATIC_DOWNCAST(CAssemblyView, pPropertySheet->GetParent());
	CAssemblyDoc* pDoc = pView->GetDocument();
	IMetaDataImport* pImport  = pDoc->MetaDataImportInterface();

	DumpUserStrings(pImport, &m_ctrlUserStringList);

	pImport->Release();

	return TRUE;  // zwrócenie TRUE, chyba że ognisko ustawiono na element sterujący
}
