//
// 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.
//
// 
// MetadataTablesPage.cpp : plik implementacji
//

#include "stdafx.h"

#include "AssemblyDoc.h"
#include "AssemblyView.h"
#include "MetadataTablesPage.h"


// Okno dialogowe CMetadataTablesPage

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

CMetadataTablesPage::~CMetadataTablesPage()
{
}

void CMetadataTablesPage::DoDataExchange(CDataExchange* pDX)
{
	CPropertyPage::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_TABLETREE, m_ctrlTableTree);
}

BEGIN_MESSAGE_MAP(CMetadataTablesPage, CPropertyPage)
END_MESSAGE_MAP()

const char *DumpRawNameOfType(IMetaDataTables *pTables, ULONG iType)
{
    if (IsRidType(iType))
    {
        const char *pNameTable;
        pTables->GetTableInfo(iType, 0,0,0,0, &pNameTable);
        return pNameTable;
    }
    else
    // Czy pole jest zakodowanym żetonem?
    if (IsCodedTokenType(iType))
    {
        int iCdTkn = iType - iCodedToken;
        const char *pNameCdTkn;
        pTables->GetCodedTokenInfo(iCdTkn, 0,0, &pNameCdTkn);
        return pNameCdTkn;
    }

    // Stały typ.
    switch (iType)
    {
    case iBYTE:
        return "BYTE";
    case iSHORT:
        return "short";
    case iUSHORT:
        return "USHORT";
    case iLONG:
        return "long";
    case iULONG:
        return "ULONG";
    case iSTRING:
        return "ciąg";
    case iGUID:
        return "GUID";
    case iBLOB:
        return "blob";
    }
    // domyślne:
    static char buf[30];
    sprintf(buf, "nieznany typ 0x%02x", iType);
    return buf;
}

LPWSTR GUIDAsString(GUID inGuid, LPWSTR guidString, ULONG bufLen)
{
    StringFromGUID2(inGuid, guidString, bufLen);
    return guidString;
}

void DumpRawCol(IMetaDataTables *pTables,
				ULONG ixTbl, ULONG ixCol, ULONG rid,
				CTreeCtrl& treeCtrl, HTREEITEM hTreeItem)
{
    ULONG       ulType;                 // Typ kolumny.
    ULONG       ulVal;                  // Wartość kolumny.
    LPCUTF8     pString;                // Wskaźnik ciągu.
    const void  *pBlob;                 // Wskaźnik obiektu blob.
    ULONG       cb;                     // Wielkość czegoś.
	WCHAR		lBuffer[1024];
	WCHAR		tBuffer[1024];

    pTables->GetColumn(ixTbl, ixCol, rid, &ulVal);
    pTables->GetColumnInfo(ixTbl, ixCol, 0, 0, &ulType, 0);

    if (ulType <= iRidMax)
    {
        const char *pNameTable;
        pTables->GetTableInfo(ulType, 0,0,0,0, &pNameTable);
 		mbstowcs(tBuffer, pNameTable, strlen(pNameTable) + 1);
        wsprintf(lBuffer, _T("%s[%x]"), tBuffer, ulVal);
 		treeCtrl.InsertItem(lBuffer, hTreeItem);
    }
    else
    // Czy pole jest zakodowanym żetonem?
    if (ulType <= iCodedTokenMax)
    {
        int iCdTkn = ulType - iCodedToken; 
        const char *pNameCdTkn;
        pTables->GetCodedTokenInfo(iCdTkn, 0,0, &pNameCdTkn);
 		mbstowcs(tBuffer, pNameCdTkn, strlen(pNameCdTkn) + 1);
        wsprintf(lBuffer, _T("%s[%08x]"), tBuffer, ulVal);
 		treeCtrl.InsertItem(lBuffer, hTreeItem);
    }
    else
    {
        // Stały typ.
        switch (ulType)
        {
        case iBYTE:
            wsprintf(lBuffer, _T("%02x"), ulVal);
	  		treeCtrl.InsertItem(lBuffer, hTreeItem);
           break;
        case iSHORT:
        case iUSHORT:
            wsprintf(lBuffer, _T("%04x"), ulVal);
	  		treeCtrl.InsertItem(lBuffer, hTreeItem);
            break;
        case iLONG:
        case iULONG:
            wsprintf(lBuffer, _T("%08x"), ulVal);
	  		treeCtrl.InsertItem(lBuffer, hTreeItem);
            break;
        case iSTRING:
            // jeśli (ulVal)
            {
                pTables->GetString(ulVal, &pString);
                cb = strlen(pString) + 1;
 				mbstowcs(tBuffer, pString, strlen(pString) + 1);
				wsprintf(lBuffer, _T("ciąg#%x (%d) %s"), ulVal, cb, tBuffer);
	  			treeCtrl.InsertItem(lBuffer, hTreeItem);
            }
            break;
        case iGUID:
			{
				const GUID *pGuid = NULL;
				pTables->GetGuid(ulVal, &pGuid);
				wsprintf(lBuffer, _T("guid#%x %s"), ulVal, GUIDAsString(*pGuid, tBuffer, sizeof(tBuffer)));
	  			treeCtrl.InsertItem(lBuffer, hTreeItem);
			}
            break;
        case iBLOB:
            // jeśli (ulVal)
            {
                pTables->GetBlob(ulVal, &cb, &pBlob);
                cb += 1;
                if (cb > 128)
                    cb += 1;
                if (cb > 16535)
                    cb += 1;
  				wsprintf(lBuffer, _T("blob#%x (%d)"), ulVal, cb);
	  			treeCtrl.InsertItem(lBuffer, hTreeItem);
          }
            break;
        default:
			wsprintf(lBuffer, _T("nieznany typ 0x%04x"), ulVal);
  			treeCtrl.InsertItem(lBuffer, hTreeItem);
            break;
        }
    }
}

// Procedury obsługi wiadomości CMetadataTablesPage
BOOL CMetadataTablesPage::OnInitDialog()
{
	CPropertyPage::OnInitDialog();

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

	IMetaDataTables *pTables = NULL;
	HRESULT hr = pImport->QueryInterface(IID_IMetaDataTables, (void**)&pTables);

	ULONG cTables;                // Tablice w bazie danych.
    ULONG ulSize;
    ULONG cCols;                  // Kolumny w tablicy.
    ULONG cRows;                  // Wiersze w tablicy.
    ULONG cbRow;                  // Bajty w wierszu tablicy.
    ULONG iKey;                   // Kolumna klucza w tablicy.
    ULONG oCol;                   // Przesunięcie kolumny.
    ULONG cbCol;                  // Wielkość kolumny.
	ULONG cValidTables = 0;
	WCHAR lBuffer[256];
	WCHAR tBuffer[128];
	WCHAR sBuffer[128];
	const char *pNameTable;      // Nazwa tablicy.
	const char *pTypeName;
	const char *pNameColumn;
	ULONG ulType;

    hr = pTables->GetNumTables(&cTables);

	HTREEITEM treeRowItem;
	TVINSERTSTRUCT tvInsert;
    for (ULONG i = 0; i < cTables; ++i)
    {
        hr = pTables->GetTableInfo(i, &cbRow, &cRows, &cCols, &iKey, &pNameTable);
		if(cRows > 0)
		{
			mbstowcs(lBuffer, pNameTable, strlen(pNameTable) + 1);
			tvInsert.hParent = NULL;
			tvInsert.hInsertAfter = NULL;
			tvInsert.item.mask = TVIF_TEXT;
			tvInsert.item.pszText = lBuffer;
			treeRowItem = m_ctrlTableTree.InsertItem(lBuffer);
			// Zrzucenie definicji kolumn w tablicy.
			for (ULONG j=0; j<cCols; ++j)
			{
				pTables->GetColumnInfo(i, j, &oCol, &cbCol, &ulType, &pNameColumn);
				pTypeName = DumpRawNameOfType(pTables, ulType);
				mbstowcs(sBuffer, pNameColumn, strlen(pNameColumn) + 1);
				mbstowcs(tBuffer, pTypeName, strlen(pTypeName) + 1);
				wsprintf(lBuffer, _T("%s (%s)"), sBuffer, tBuffer);
				m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
			}
			if (m_ctrlTableTree.ItemHasChildren(treeRowItem))
			{
				for(ULONG j = 1; j <= cRows; ++j)
				{
					HTREEITEM hNextItem;
					HTREEITEM hChildItem = m_ctrlTableTree.GetChildItem(treeRowItem);
					for(ULONG k = 0; k < cCols; ++k)
					{
						DumpRawCol(pTables, i, k, j, m_ctrlTableTree, hChildItem);
						hNextItem = m_ctrlTableTree.GetNextItem(hChildItem, TVGN_NEXT);
						hChildItem = hNextItem;
					}
				}
			}
			cValidTables++;
		}
	}
	pTables->Release();
	pTables = NULL;
	pImport->Release();

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