// PlamTypeLibBrowserDlg.cpp : implementation file
//

#include "stdafx.h"
#include "../auto.h"
#include "PlamTypeLibBrowserDlg.h"

using namespace ComSupport;

CPlamTypeLibBrowserDlg::CPlamTypeLibBrowserDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CPlamTypeLibBrowserDlg::IDD, pParent)
	, m_str_SampleCode(_T(""))
	, m_strTpeLib2Load(_T(""))
	{
}

CPlamTypeLibBrowserDlg::~CPlamTypeLibBrowserDlg()
{
}

void CPlamTypeLibBrowserDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST_CATEGORY, m_ctrl_lb_Category);
DDX_Control(pDX, IDC_LIST_OBJECT, m_ctrl_lb_Object);
DDX_Control(pDX, IDC_LIST_TYPEINFO, m_ctrl_lb_TypeInfo);
DDX_Text(pDX, IDC_EDIT_SAMPLE, m_str_SampleCode);
DDX_Text(pDX, IDC_EDIT_TYPELIB2LOAD, m_strTpeLib2Load);
}

BEGIN_MESSAGE_MAP(CPlamTypeLibBrowserDlg, CDialog)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
	ON_LBN_SELCHANGE(IDC_LIST_CATEGORY, OnLbnSelchangeListCategory)
	ON_LBN_SELCHANGE(IDC_LIST_OBJECT, OnLbnSelchangeListObject)
	ON_BN_CLICKED(IDCANCEL, OnBnClickedCancel)
	ON_BN_CLICKED(IDC_BUTTON_LOADTYPELIB, OnBnClickedButtonLoadtypelib)
	ON_NOTIFY (LVN_ITEMCHANGED, IDC_LIST_TYPEINFO, OnLbnSelchangeListTypeinfo)
END_MESSAGE_MAP()

BOOL CPlamTypeLibBrowserDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	m_ctrl_lb_Category.SetHorizontalExtent(9999);
	m_ctrl_lb_Object.SetHorizontalExtent(9999);
	//todo:m_ctrl_lb_TypeInfo. SetHorizontalExtent(9999);

	InitListColumn();

	PopulateCategoryList();

	return TRUE;
}


void CPlamTypeLibBrowserDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

HCURSOR CPlamTypeLibBrowserDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}



///
///
void CPlamTypeLibBrowserDlg::OnBnClickedCancel()
{
	//if(AfxMessageBox(_T("Close"), MB_YESNO)== IDYES)
		OnCancel();
}


void CPlamTypeLibBrowserDlg::OnBnClickedButtonLoadtypelib()
{
	UpdateData();
	HRESULT hr;
	CFileDialog fileDlg(TRUE);
	if( fileDlg.DoModal()!= IDOK )
		return;

	m_strTpeLib2Load = fileDlg.GetPathName();

	CComPtr<ITypeLib> spTypeLib;
	hr = LoadTypeLib(m_strTpeLib2Load.AllocSysString(), &spTypeLib);
	if(hr != S_OK || spTypeLib==NULL)
	{
		m_ctrl_lb_Object.ResetContent();
		m_arr_spti_Objects.RemoveAll();
		m_ctrl_lb_TypeInfo.DeleteAllItems();
		m_str_SampleCode="";
		UpdateData(0);
		return;
	}

	ShowCategory(spTypeLib);
	UpdateData(0);
}
//

void CPlamTypeLibBrowserDlg::OnLbnSelchangeListCategory()
{
	int iItem = m_ctrl_lb_Category.GetCurSel();
	DWORD_PTR nPos = m_ctrl_lb_Category.GetItemData( iItem );
	const CTypeLibraryData& oData = m_arr_tld_Category[nPos];
	ShowCategory(oData);
}

void CPlamTypeLibBrowserDlg::OnLbnSelchangeListObject()
{
	int iItem = m_ctrl_lb_Object.GetCurSel();
	DWORD_PTR nPos = m_ctrl_lb_Object.GetItemData( iItem );
	const CComPtr<ITypeInfo>& spCurrentTypeInfo = m_arr_spti_Objects[nPos];
	SetElements/*ShowObject*/(spCurrentTypeInfo);
}

void CPlamTypeLibBrowserDlg::OnLbnSelchangeListTypeinfo(NMHDR *,LRESULT *)
{
	UpdateData();

	CString strMethod;
	CString strParams;
	CString strResult;

	int nItem = m_ctrl_lb_TypeInfo.GetNextItem(-1, LVNI_FOCUSED);
	if(nItem != -1)
	{
		strMethod = m_ctrl_lb_TypeInfo.GetItemText(nItem,0);
		strResult = m_ctrl_lb_TypeInfo.GetItemText(nItem,2);
		strParams = m_ctrl_lb_TypeInfo.GetItemText(nItem,3);
	}


	if(m_ctrl_lb_TypeInfo.GetItemText(nItem,1) !="function" )
	{
		strParams.Empty();
		if(!strResult.IsEmpty())
			strResult = strResult.MakeUpper() + _T(" is ");
		else
			strResult = "Y is ";
	}
	else
	{
		strParams = _T("(")+strParams.MakeUpper() + _T(")");
		if(!strResult.IsEmpty())
			strResult = strResult.MakeUpper() + _T(" is ");
	}

	m_str_SampleCode.Format( 
		_T("%% using library %s\r\n")
		_T("?-\r\n")
		_T("\tX is server(\"%s\"),\r\n")
		_T("\t%sX.'%s'%s.\r\n" ),
		m_strTpeLib2Load,
		m_strProgID,
		strResult,
		strMethod,
		strParams);
	UpdateData(0);

}

///////////////////////////////////////////////////////////////////////////////
// app logic
///////////////////////////////////////////////////////////////////////////////
void CPlamTypeLibBrowserDlg::PopulateCategoryList()
{
	m_ctrl_lb_Category.ResetContent();
	m_arr_tld_Category.RemoveAll();

	m_ctrl_lb_Object.ResetContent();
	m_arr_spti_Objects.RemoveAll();

	m_ctrl_lb_TypeInfo.DeleteAllItems();


	TCHAR  tchTypeCLSID[128];
	TCHAR  tchVersionInformation[128];
	long lRegResult = 0;


	//Open the TypeLib Key under HKCR
	HKEY   hKeyTypeLib = NULL;
	lRegResult = RegOpenKeyEx(HKEY_CLASSES_ROOT,_T("TypeLib"), 0, KEY_READ, &hKeyTypeLib) ;
	if(lRegResult!=ERROR_SUCCESS || hKeyTypeLib==NULL) return;
	

	//Open each CLSID Subkey  under the TypeLib key
	for(DWORD  dwSubKeyIndex = 0; ERROR_SUCCESS == RegEnumKey(hKeyTypeLib,dwSubKeyIndex,tchTypeCLSID,sizeof(tchTypeCLSID)); dwSubKeyIndex++)
	{
		HKEY   hKeyClassTLB = NULL;
		lRegResult = RegOpenKeyEx(hKeyTypeLib,tchTypeCLSID,0, KEY_READ, &hKeyClassTLB) ;       
		if(	lRegResult != ERROR_SUCCESS) continue;
			
		//Get each Subkey under the CLSID subkey,We should be able to get version information 
		for(DWORD dwCLSIDSubKeyIndex = 0; ERROR_SUCCESS == RegEnumKey(hKeyClassTLB,dwCLSIDSubKeyIndex,tchVersionInformation,sizeof(tchVersionInformation));dwCLSIDSubKeyIndex++)
		{
			//Get the version information
			//DWORD dwLibMajorVersion = atoi(tchVersionInformation);
			
			//Open the default key under version information
			HKEY hKeyVersion = NULL;
			lRegResult = RegOpenKeyEx(hKeyClassTLB,	tchVersionInformation,0,KEY_READ,&hKeyVersion);    
			if(	lRegResult != ERROR_SUCCESS) continue;

			//What's the size of the string we are looking for ?
			long  dwSizeOfDataBlock = 0;
			lRegResult = RegQueryValue(hKeyVersion, NULL, NULL, &dwSizeOfDataBlock);
			if(	lRegResult != ERROR_SUCCESS) continue;
			
			//Got the string size, Allocate Memory and query for description
			TCHAR* pDescription = new TCHAR[dwSizeOfDataBlock + 1];
			lRegResult = RegQueryValue(hKeyVersion, NULL, pDescription, &dwSizeOfDataBlock) ;
			if(lRegResult != ERROR_SUCCESS)
			{
				delete [] pDescription;
				continue;
			}

			//append version information
			CString sName(pDescription);
			if(pDescription) delete [] pDescription;
			sName += " ";
			sName += tchVersionInformation;


			CTypeLibraryData oData;
			oData.m_strDescription = sName;
			oData.m_strCLSIDTypeLib = CString(tchTypeCLSID);
			oData.m_lMajorVersion = _tstoi(tchVersionInformation);

			CString strMinor = CString(tchVersionInformation);
			int posMinor = strMinor.Find(_T('.'));
			oData.m_lMinorVersion = (posMinor == -1) ? 0 : _tstoi(strMinor.Mid(posMinor));

			int iItem = m_ctrl_lb_Category.AddString(sName);
			INT_PTR nPos = m_arr_tld_Category.Add(oData);
			m_ctrl_lb_Category.SetItemData(iItem,nPos);
		}//Inner Enumeration
		
		RegCloseKey(hKeyClassTLB);
	}//Outer Enumeration

	RegCloseKey(hKeyTypeLib) ;
}


void CPlamTypeLibBrowserDlg::ShowCategory(const CTypeLibraryData& oData)
{
	HRESULT hr = S_OK;

	CLSID clsidTypeLib;
	_bstr_t bstrCLSID = oData.m_strCLSIDTypeLib.AllocSysString();
	hr = CLSIDFromString(bstrCLSID,&clsidTypeLib);
	if(hr != S_OK) return;

	CComPtr<ITypeLib> spTypeLib;
	hr = LoadRegTypeLib(clsidTypeLib,(unsigned short)oData.m_lMajorVersion,(unsigned short)oData.m_lMinorVersion,0,&spTypeLib);
	if(hr != S_OK || spTypeLib==NULL) 
	{
		m_ctrl_lb_Object.ResetContent();
		m_arr_spti_Objects.RemoveAll();
		m_ctrl_lb_TypeInfo.DeleteAllItems();
		m_str_SampleCode="";
		UpdateData(0);
		return;
	}

	m_strTpeLib2Load=oData.m_strDescription;

	ShowCategory(spTypeLib);
}

void CPlamTypeLibBrowserDlg::ShowCategory(const CComPtr<ITypeLib>& spTypeLib)
{
	m_ctrl_lb_Object.ResetContent();
	m_arr_spti_Objects.RemoveAll();
	m_ctrl_lb_TypeInfo.DeleteAllItems();
	m_str_SampleCode="";
	UpdateData(0);

	HRESULT hr = S_OK;

	//Get the type information count,	Make sure that we have type information
	long lTypeInfoCount = spTypeLib->GetTypeInfoCount();
	if(lTypeInfoCount == 0) return;

	for(long lIter = 0; lIter  < lTypeInfoCount ; lIter++)
	{
		//check if it is COCLASS
		TYPEKIND typeKind;
		hr = spTypeLib->GetTypeInfoType(lIter, &typeKind);
		if(hr != S_OK) continue;
		if(typeKind != TKIND_COCLASS && typeKind != TKIND_DISPATCH) continue;


		//Get the Type information ptr (ITypeInfo ptr)
		CComPtr<ITypeInfo> spCurrentTypeInfo;
		hr = spTypeLib->GetTypeInfo(lIter, &spCurrentTypeInfo);
		if(hr != S_OK || spCurrentTypeInfo==NULL) continue;


		_bstr_t strName;
		spTypeLib->GetDocumentation(lIter, strName.GetAddress(), NULL, NULL, NULL);

		if(typeKind != TKIND_COCLASS ) strName = strName + " *";


		int iItem = m_ctrl_lb_Object.AddString(strName);
		INT_PTR nPos = m_arr_spti_Objects.Add(spCurrentTypeInfo);
		m_ctrl_lb_Object.SetItemData(iItem,nPos);
	}  
}

void CPlamTypeLibBrowserDlg::ShowObject(const CComPtr<ITypeInfo>& spCoClass)
{
	m_ctrl_lb_TypeInfo.DeleteAllItems();

	HRESULT hr = S_OK;

	MyAuto_TYPEATTR  pTypeAttr(spCoClass);
	if(!pTypeAttr) return;

	LPOLESTR pwszProgID = NULL;;
	ProgIDFromCLSID( pTypeAttr->guid, &pwszProgID);
	m_strProgID=pwszProgID;
	CoTaskMemFree(pwszProgID);

	for( int j = 0 ; j < pTypeAttr->cImplTypes ; j++ )
	{
		HREFTYPE hRefType;
		hr = spCoClass->GetRefTypeOfImplType( j, &hRefType );
		if(hr != S_OK) continue;

		CComPtr<ITypeInfo> spCurrentTypeInfo;
		hr = spCoClass->GetRefTypeInfo(hRefType, &spCurrentTypeInfo);
		if(hr != S_OK || spCurrentTypeInfo==NULL) continue;

		MyAuto_TYPEATTR ptypeattr(spCurrentTypeInfo);
		if(!ptypeattr) continue;

		for(int i=0;i<ptypeattr->cFuncs;i++)
		{
			MyAuto_FUNCDESC pfuncdesc(spCurrentTypeInfo,i);
			if(!pfuncdesc) continue;

			MEMBERID memid = pfuncdesc->memid;
			_bstr_t strName;
			hr = spCurrentTypeInfo->GetDocumentation(memid,strName.GetAddress(),NULL,NULL,NULL);

			//todo
			//if(m_ctrl_lb_TypeInfo.FindStringExact(0,strName) == -1 )
			//	m_ctrl_lb_TypeInfo.AddString(strName);
		}//for i : Funcs
	}//for j : ImplTypes

	OnLbnSelchangeListTypeinfo(NULL,NULL);
}

void CPlamTypeLibBrowserDlg::InitListColumn()
{
  HICON hIcon;

  m_ImgList.Create(16,16,ILC_COLOR4 | ILC_MASK,3,0);

  hIcon=::LoadIcon(AfxGetResourceHandle(),MAKEINTRESOURCE(IDI_OBJECT));
  m_ImgList.Add(hIcon);

  hIcon=::LoadIcon(AfxGetResourceHandle(),MAKEINTRESOURCE(IDI_METHOD));
  m_ImgList.Add(hIcon);

  hIcon=::LoadIcon(AfxGetResourceHandle(),MAKEINTRESOURCE(IDI_PROPERTY));
  m_ImgList.Add(hIcon);

  LV_COLUMN Col0,Col1,Col2,Col3,Col4;

  Col0.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
  Col0.fmt = LVCFMT_LEFT;
  Col0.cx = 120;
  Col0.iSubItem = 0;
  Col0.pszText = _T("Name");

  Col1.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
  Col1.fmt = LVCFMT_LEFT;
  Col1.cx = 60;
  Col1.iSubItem = 1;
  Col1.pszText = _T("Kind of");

  Col2.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
  Col2.fmt = LVCFMT_LEFT;
  Col2.cx = 50;
  Col2.iSubItem = 2;
  Col2.pszText = _T("Type");

  Col3.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
  Col3.fmt = LVCFMT_LEFT;
  Col3.cx = 250;
  Col3.iSubItem = 3;
  Col3.pszText = _T("Parameters");

  Col4.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
  Col4.fmt = LVCFMT_LEFT;
  Col4.cx = 350;
  Col4.iSubItem = 4;
  Col4.pszText = _T("Help string");

  m_ctrl_lb_TypeInfo.SetImageList(&m_ImgList,LVSIL_SMALL);
  
  m_ctrl_lb_TypeInfo.InsertColumn(0, &Col0);
  m_ctrl_lb_TypeInfo.InsertColumn(1, &Col1);
  m_ctrl_lb_TypeInfo.InsertColumn(2, &Col2);
  m_ctrl_lb_TypeInfo.InsertColumn(3, &Col3);
  m_ctrl_lb_TypeInfo.InsertColumn(4, &Col4);

}


void CPlamTypeLibBrowserDlg::SetElements(const CComPtr<ITypeInfo>& spCoClass)
{
	m_ctrl_lb_TypeInfo.DeleteAllItems();
	HRESULT hr S_OK;

	MyAuto_TYPEATTR pTypeAttr(spCoClass);
	if(!pTypeAttr) return;

	if(pTypeAttr->typekind != TKIND_COCLASS)
	{
		SetElements_internal(spCoClass);
		OnLbnSelchangeListTypeinfo(NULL,NULL);
		return;
	}

	LPOLESTR pwszProgID = NULL;;
	ProgIDFromCLSID( pTypeAttr->guid, &pwszProgID);
	m_strProgID=pwszProgID;
	CoTaskMemFree(pwszProgID);

	for( int jj = 0 ; jj < pTypeAttr->cImplTypes ; jj++ )
	{
		HREFTYPE hRefType;
		hr = spCoClass->GetRefTypeOfImplType( jj, &hRefType );
		if(hr != S_OK) continue;

		CComPtr<ITypeInfo> spCurrentTypeInfo;
		hr = spCoClass->GetRefTypeInfo(hRefType, &spCurrentTypeInfo);
		if(hr != S_OK || spCurrentTypeInfo==NULL) continue;

		SetElements_internal(spCurrentTypeInfo);
	}//for jj
	OnLbnSelchangeListTypeinfo(NULL,NULL);
}

void CPlamTypeLibBrowserDlg::SetElements_internal(const CComPtr<ITypeInfo>& spCurrentTypeInfo)
{
	LV_ITEM       item;
	item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_PARAM;
	item.state = 0;
	item.stateMask = 0;
	item.iImage = 0;
	item.iItem = 0;
	item.iSubItem = 0;
	item.pszText = NULL;
	item.lParam = 0;

	MyAuto_TYPEATTR ptypeattr(spCurrentTypeInfo);
	if(!ptypeattr) return;
	for(int i=0;i<ptypeattr->cFuncs;i++)
	{
		MyAuto_FUNCDESC pfuncdesc(spCurrentTypeInfo,i);
		if(!pfuncdesc) continue;

		MEMBERID memid = pfuncdesc->memid;
		_bstr_t buff;
		_bstr_t helpStr;
		HRESULT hr = spCurrentTypeInfo->GetDocumentation(memid,buff.GetAddress(),helpStr.GetAddress(),NULL,NULL);
		if(FAILED(hr))continue;

		LVFINDINFO fi;
		fi.flags = LVFI_STRING;
		fi.psz = buff;
		if( -1 != m_ctrl_lb_TypeInfo.FindItem(&fi)) continue;

		_bstr_t typestr;
		VARTYPE retValue = pfuncdesc->elemdescFunc.tdesc.vt;
		switch(retValue)
		{
			case VT_I2      : typestr = _T("I2");
								break;
			case VT_I4      : typestr = _T("I4");
								break; 
			case VT_R4      : typestr = _T("R4");
								break; 
			case VT_R8      : typestr = _T("R8");
								break; 
			case VT_CY      : typestr = _T("CY");
								break; 
			case VT_DATE    : typestr = _T("DATE");
								break; 
			case VT_BSTR    : typestr = _T("BSTR");
								break;
			case VT_ERROR   : typestr = _T("ERROR");
								break;
			case VT_BOOL    : typestr = _T("BOOL");
								break;
			case VT_VARIANT : typestr = _T("VARIANT");
								break;
			case VT_UI1     : typestr = _T("UI1");
								break;
		}//switch(retValue)

		if(pfuncdesc->invkind == INVOKE_FUNC)
		{
			CString paramStr;
			// Get the names of function parameters
			UINT nParams = pfuncdesc->cParams;
			BSTR* paramNames = new BSTR[nParams];
			spCurrentTypeInfo->GetNames(memid,paramNames,nParams,&nParams);
			for(DWORD ii = 1; ii < nParams; ii++)
			{
				_bstr_t paramName;
				paramName.Attach(paramNames[ii]);
				paramStr += paramNames[ii];//paramName;
				paramStr += _T(",");
			}//for ii
			delete[] paramNames;

			if(!paramStr.IsEmpty())//remove last ',' if any
				paramStr = paramStr.Left(paramStr.GetLength()-1);

			item.pszText = buff;
			item.iImage = _METHOD_;
			item.iItem++;
			item.lParam = memid;

			int j = m_ctrl_lb_TypeInfo.InsertItem(&item);
			m_ctrl_lb_TypeInfo.SetItemText(j,1,_T("function"));
			m_ctrl_lb_TypeInfo.SetItemText(j,2,typestr);
			m_ctrl_lb_TypeInfo.SetItemText(j,3,paramStr);
			m_ctrl_lb_TypeInfo.SetItemText(j,4,helpStr);
		}
		else if (pfuncdesc->invkind == INVOKE_PROPERTYGET)
		{
			if ((retValue==VT_PTR) || (retValue==VT_DISPATCH))         
			{
				item.pszText = buff;
				item.iImage = _OBJECT_;
				item.iItem++;
				item.lParam = memid;

				int j = m_ctrl_lb_TypeInfo.InsertItem(&item);
				m_ctrl_lb_TypeInfo.SetItemText(j,1,_T("object"));
				m_ctrl_lb_TypeInfo.SetItemText(j,4,helpStr);
			}
			else
			{
				item.pszText = buff;
				item.iImage = _PROPERTY_;
				item.iItem++;
				item.lParam = memid;

				int j = m_ctrl_lb_TypeInfo.InsertItem(&item);
				m_ctrl_lb_TypeInfo.SetItemText(j,1,_T("property"));
				m_ctrl_lb_TypeInfo.SetItemText(j,2,typestr);
				m_ctrl_lb_TypeInfo.SetItemText(j,4,helpStr);
			}
		}
	}//for i
}







