// RGSTree.h : Declaration of the CRGSTree

#include "resource.h"       // main symbols
#include <commctrl.h>
#include <statreg.h>
#include "addkey.h"

#define MAX_VALUE           4096
///////////////////////////////////////
#define RET_ON_ERROR(x) \
		if (FAILED(hr = x))\
			return hr;
///////////////////////////////////////
#define BREAK_ON_ERROR(x) \
		if (FAILED(hr = x))\
			break;


LPTSTR StrChr(LPTSTR lpsz, TCHAR ch);

#define IMAGEWIDTH 16
#define IMAGEGROWBY 4
#define IMAGEROOTKEY 1
#define IMAGEKEY 0
#define IMAGEVALTEXT 2
#define IMAGEVALBIN 3
#define IMAGECOUNT 4
#define RGSFILEMAX 0x0002FFFF
#define RGSFILEINIT 0x00000FFF

extern LPCTSTR szReturn;


/////////////////////////////////////////////////////////////////////////////
// CRGSTree
class CRGSTree : 
	public CComObjectRoot,
	public CComCoClass<CRGSTree, &CLSID_CRGSTree>,
	public CComControl<CRGSTree>,
	public IDispatchImpl<IRGSTree, &IID_IRGSTree, &LIBID_RGSEDITLib>,
	public IProvideClassInfo2Impl<&CLSID_CRGSTree, NULL, &LIBID_RGSEDITLib>,
	public IPersistStreamInitImpl<CRGSTree>,
	public IPersistStorageImpl<CRGSTree>,
	public IQuickActivateImpl<CRGSTree>,
	public IOleControlImpl<CRGSTree>,
	public IOleObjectImpl<CRGSTree>,
	public IOleInPlaceActiveObjectImpl<CRGSTree>,
	public IViewObjectExImpl<CRGSTree>,
	public IOleInPlaceObjectWindowlessImpl<CRGSTree>,
	public ISpecifyPropertyPagesImpl<CRGSTree>,
	public CRegParser
{
public:

	CContainedWindow m_TreeCtrl;
	CContainedWindow m_ListView;
	CComBSTR	m_strPath;
	HIMAGELIST  m_hImageList;
	HIMAGELIST  m_hLVImageList;
	LPCTSTR		m_szFilter;
	HMENU		m_hRightMenu;
	HMENU		m_hLoadMenu;


	CRGSTree() : m_TreeCtrl(_T("SysTreeView32"), this, 1),
				 m_ListView(_T("SysListView32"), this, 2),
				 CRegParser(NULL)
	{
		m_bWindowOnly = TRUE;
		m_szFilter = _T("Regitrar Scripts\x00*.RGS\x00");
		m_hImageList = ImageList_LoadBitmap(_Module.GetModuleInstance(),
											MAKEINTRESOURCE(IDR_IMAGELIST), 
											IMAGEWIDTH, IMAGEGROWBY, CLR_NONE);
		m_hLVImageList = ImageList_LoadBitmap(_Module.GetModuleInstance(),
											MAKEINTRESOURCE(IDR_LVIMAGELIST), 
											IMAGEWIDTH, IMAGEGROWBY, CLR_NONE);
		CreatePopup();
	}
	~CRGSTree()
	{
		if (m_hRightMenu != NULL)
			DestroyMenu(m_hRightMenu);
		if(m_hImageList != NULL)
			ImageList_Destroy(m_hImageList);
		if(m_hLVImageList != NULL)
			ImageList_Destroy(m_hLVImageList);
	}

	enum ContentType
	{
		Replacement = 0,
		DefaultVal = 1,
		NamedVal = 2,
		Key = 3,
		RootKey = 4
	};


	void CreatePopup()
	{
		m_hRightMenu = NULL;
		m_hLoadMenu = LoadMenu(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDR_RIGHTMENU));
		if (m_hLoadMenu == NULL)
		{
			ATLTRACE(_T("Failed to load right click menu\n"));
			_ASSERTE(FALSE);
			return;
		}

		m_hRightMenu = GetSubMenu(m_hLoadMenu, 0);
		if (m_hRightMenu == NULL)
		{
			ATLTRACE(_T("Failed to load Popup menu\n"));
			_ASSERTE(FALSE);
			return;
		}

	}

DECLARE_REGISTRY_RESOURCEID(IDR_RGSTree)


BEGIN_COM_MAP(CRGSTree)
	COM_INTERFACE_ENTRY(IRGSTree)
	COM_INTERFACE_ENTRY(IDispatch)
	COM_INTERFACE_ENTRY_IMPL(IViewObjectEx)
	COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject2, IViewObjectEx)
	COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject, IViewObjectEx)
	COM_INTERFACE_ENTRY_IMPL(IOleInPlaceObjectWindowless)
	COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleInPlaceObject, IOleInPlaceObjectWindowless)
	COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleWindow, IOleInPlaceObjectWindowless)
	COM_INTERFACE_ENTRY_IMPL(IOleInPlaceActiveObject)
	COM_INTERFACE_ENTRY_IMPL(IOleControl)
	COM_INTERFACE_ENTRY_IMPL(IOleObject)
	COM_INTERFACE_ENTRY_IMPL(IQuickActivate)
	COM_INTERFACE_ENTRY_IMPL(IPersistStorage)
	COM_INTERFACE_ENTRY_IMPL(IPersistStreamInit)
	COM_INTERFACE_ENTRY_IMPL(ISpecifyPropertyPages)
	COM_INTERFACE_ENTRY(IProvideClassInfo)
	COM_INTERFACE_ENTRY(IProvideClassInfo2)
END_COM_MAP()

BEGIN_PROPERTY_MAP(CRGSTree)
	PROP_ENTRY("RGSPath", 100, CLSID_NULL)
END_PROPERTY_MAP()


BEGIN_MSG_MAP(CRGSTree)
	MESSAGE_HANDLER(WM_CREATE, OnCreate)
ALT_MSG_MAP(1)
	MESSAGE_HANDLER(WM_RBUTTONDOWN, OnRButtonDown)
	COMMAND_ID_HANDLER(IDM_NEW, OnNew)
	COMMAND_ID_HANDLER(IDM_SAVE, OnSave)
	COMMAND_ID_HANDLER(IDM_OPEN, OnOpen)
	COMMAND_ID_HANDLER(IDM_INSERT, OnOpen)
	COMMAND_ID_HANDLER(IDM_ADDKEY, OnAddKey)
	COMMAND_ID_HANDLER(IDM_ADDVALUE, OnAddValue)
	COMMAND_ID_HANDLER(IDM_ADDROOT, OnAddRootKey)
ALT_MSG_MAP(2)
END_MSG_MAP()

// IAtlEdit
public:

	HRESULT OnDraw(ATL_DRAWINFO&) 
	{
		return S_OK;
	}
	LRESULT OnNew(WORD wNotifyCode, WORD wID, HWND hWndCtlr, BOOL& bHandled)
	{
		if (::MessageBox(m_TreeCtrl.m_hWnd, _T("RGS Image has changed, Save?"), _T("Confirmation"), MB_YESNO) == IDYES)
			SaveRegFile();
		TreeView_DeleteAllItems(m_TreeCtrl.m_hWnd);
		return 0;
	}
	LRESULT OnOpen(WORD wNotifyCode, WORD wID, HWND hWndCtlr, BOOL& bHandled)
	{
		USES_CONVERSION;
		TCHAR szFullPath[_MAX_PATH];
		szFullPath[0] = NULL;

		if (m_TreeCtrl.m_hWnd == NULL)
			return S_OK;
		
		OPENFILENAME ofn;
		memset(&ofn, 0, sizeof(OPENFILENAME));
		ofn.lStructSize = sizeof(OPENFILENAME);
		ofn.hwndOwner = m_TreeCtrl.m_hWnd;
		ofn.lpstrFilter = m_szFilter;
		ofn.lpstrFile = szFullPath;
		ofn.nMaxFile = _MAX_PATH;

		if (!GetOpenFileName(&ofn))
		{
			ATLTRACE(_T("Failed to get open file from Common Dlg\n"));
			return 0;
		}

		m_strPath = szFullPath;

		if (wID == IDM_OPEN)
			OnNew(wNotifyCode, wID, hWndCtlr, bHandled);
		EditRegFile();
		return 0;
	}
	LRESULT OnAddRootKey(WORD wNotifyCode, WORD wID, HWND hWndCtlr, BOOL& bHandled)
	{

		CAddRootKey addRootKeyDlg;
		if (addRootKeyDlg.DoModal() == IDOK)
			InsertItem(m_TreeCtrl.m_hWnd, addRootKeyDlg.m_szRootKey, IMAGEROOTKEY, MAKELPARAM(RootKey, 0));
		return 0;
	}
	LRESULT OnAddKey(WORD wNotifyCode, WORD wID, HWND hWndCtlr, BOOL& bHandled)
	{
		HTREEITEM hSel = TreeView_GetSelection(m_TreeCtrl.m_hWnd);
		if (hSel)
		{
			CAddKey addKeyDlg;
			if (addKeyDlg.DoModal() == IDOK)
				InsertItem(m_TreeCtrl.m_hWnd, addKeyDlg.m_szPath, IMAGEKEY, MAKELPARAM(Key, 0), hSel);
		}
		return 0;
	}
	LRESULT OnSave(WORD wNotifyCode, WORD wID, HWND hWndCtlr, BOOL& bHandled)
	{
		SaveRegFile();
		return 0;
	}
	LRESULT OnAddValue(WORD wNotifyCode, WORD wID, HWND hWndCtlr, BOOL& bHandled)
	{
		HTREEITEM hSel = TreeView_GetSelection(m_TreeCtrl.m_hWnd);
		if (hSel)
		{
			CAddValue addValueDlg;
			if (addValueDlg.DoModal() == IDOK)
			{
				if (addValueDlg.m_bDefault)
				{
					InsertItem(m_TreeCtrl.m_hWnd, addValueDlg.m_szValue, IMAGEVALTEXT, 
											  MAKELPARAM(DefaultVal,0), hSel, TVI_FIRST);
					SetLParam(hSel, MAKELPARAM(Key, 1));
				}
				else
				{
					HTREEITEM hName = InsertItem(m_TreeCtrl.m_hWnd, addValueDlg.m_szValueName, 
												 IMAGEVALTEXT, MAKELPARAM(NamedVal, 0), hSel);
					InsertItem(m_TreeCtrl.m_hWnd, addValueDlg.m_szValue, IMAGEVALTEXT, 
											  MAKELPARAM(DefaultVal,0), hName);
				}
					
			}
		}
		return 0;
	}
	LRESULT OnRButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		POINT ptClick;
		ptClick.x = LOWORD(lParam);
		ptClick.y = HIWORD(lParam);
		::ClientToScreen(m_TreeCtrl.m_hWnd, &ptClick);
		TrackPopupMenuEx(m_hRightMenu, TPM_VERTICAL, ptClick.x, ptClick.y, m_TreeCtrl.m_hWnd, NULL);
		return 0;
	}
	LRESULT OnSF(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		return 0;
	}
	LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		RECT rc;
		LONG oldRight;
		GetClientRect(&rc);
		oldRight = rc.right;
		rc.right /= 2;
		m_TreeCtrl.Create(m_hWnd, rc, _T("hello"), WS_CHILD | WS_VISIBLE | TVS_HASLINES |
							TVS_LINESATROOT | TVS_HASBUTTONS | TVS_EDITLABELS, 0, NULL);
		rc.right = oldRight;
		rc.left = oldRight/2 + 1;
		m_ListView.Create(m_hWnd, rc, _T("hello"), WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_SMALLICON, 0, NULL);
		_ASSERTE(m_TreeCtrl.m_hWnd != NULL);
		_ASSERTE(m_ListView.m_hWnd != NULL);
		_ASSERTE(m_hImageList != NULL);
		TreeView_SetImageList(m_TreeCtrl.m_hWnd, m_hImageList, TVSIL_NORMAL);
		ListView_SetImageList(m_ListView.m_hWnd, m_hLVImageList, LVSIL_NORMAL);
		if (m_strPath != NULL)
			EditRegFile();
		return 0;
	}

	BOOL SetLVContents(LPCTSTR szEntry, int iImage)
	{
		ListView_DeleteAllItems(m_ListView.m_hWnd);
		return SetItem(m_ListView.m_hWnd, 0, szEntry, iImage, 0);
	}

	BOOL AppendLVContents(LPCTSTR szEntry, int iImage)
	{
		return SetItem(m_ListView.m_hWnd, ListView_GetItemCount(m_ListView.m_hWnd), 
					   szEntry, iImage, 0);
	}

	BOOL SetItem(HWND hWnd, int nItem, LPCTSTR lpszItem, int nImage, LPARAM lParam)
	{
		_ASSERTE(::IsWindow(hWnd));
		LV_ITEM lvi;
		lvi.mask = LVIF_TEXT | LVIF_IMAGE;
		lvi.iItem = nItem;
		lvi.iSubItem = 0;
		lvi.stateMask = 0;
		lvi.state = 0;
		lvi.pszText = (LPTSTR) lpszItem;
		lvi.iImage = nImage;
		lvi.lParam = lParam;
		return (BOOL) ::SendMessage(hWnd, LVM_SETITEM, 0, (LPARAM)&lvi);
	}


	HTREEITEM InsertItem(HWND hWnd, LPCTSTR lpszItem, int iImage, LPARAM lParam,
						 HTREEITEM hParent = TVI_ROOT, HTREEITEM hAfter = TVI_LAST)
	{
		_ASSERTE(::IsWindow(hWnd));
		TV_INSERTSTRUCT tvis;
		tvis.hParent = hParent;
		tvis.hInsertAfter = hAfter;
		tvis.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
		tvis.item.pszText = (LPTSTR) lpszItem;
		tvis.item.iImage = iImage;
		tvis.item.iSelectedImage = iImage;
		tvis.item.state = 0;
		tvis.item.stateMask = 0;
		tvis.item.lParam = lParam;
		return (HTREEITEM)::SendMessage(hWnd, TVM_INSERTITEM, 0, (LPARAM)&tvis);
	}

	void SetLParam(HTREEITEM hItem, LPARAM lParam)
	{
		TV_ITEM tvi;

		tvi.mask = TVIF_PARAM;
		tvi.hItem = hItem;
		tvi.lParam = lParam;
		TreeView_SetItem(m_TreeCtrl.m_hWnd, &tvi);
	}

	void GetLParam(HTREEITEM hItem, LPARAM& lParam)
	{
		TV_ITEM tvi;

		tvi.mask = TVIF_PARAM;
		tvi.hItem = hItem;
		TreeView_GetItem(m_TreeCtrl.m_hWnd, &tvi);
		lParam = tvi.lParam;
	}

	HRESULT SubkeysToTree(HTREEITEM hTreeParent)
	{
		TCHAR   szToken[MAX_VALUE];
		HTREEITEM	hTreeCur = hTreeParent;
		HRESULT hr = S_OK;

		RET_ON_ERROR(NextToken(szToken))  // Should be key name
		while (*szToken != chRightBracket) // Continue till we see a }
		{
			if (!lstrcmpi(szToken, szDelete)) // Found Delete Token
			{
				ATLTRACE(_T("Want to place an X in key image\n"));
			}

			if (!lstrcmpi(szToken, szForceRemove))
			{

				ATLTRACE(_T("Want to place an FX in key image\n"));
				BREAK_ON_ERROR(NextToken(szToken))

				if (StrChr(szToken, chDirSep) != NULL)
					return GenerateError(E_ATL_COMPOUND_KEY);

			}

			if (!lstrcmpi(szToken, szNoRemove))
			{
				ATLTRACE(_T("Want to place an NX in key image\n"));
				BREAK_ON_ERROR(NextToken(szToken))
			}

			if (!lstrcmpi(szToken, szValToken)) // need to add a value to hkParent
			{
				ATLTRACE(_T("Want to place value image\n"));
				TCHAR  szValueName[_MAX_PATH];

				BREAK_ON_ERROR(NextToken(szValueName))
				hTreeCur = InsertItem(m_TreeCtrl.m_hWnd, szValueName, IMAGEVALTEXT, 
									  MAKELPARAM(NamedVal, 0), hTreeCur);
//				AppendLVContents(szValueName, IMAGEVALTEXT);
				
				BREAK_ON_ERROR(NextToken(szToken))
				if (*szToken != chEquals)
					return GenerateError(E_ATL_EXPECTING_EQUAL);
			}
			else
			{
				if (StrChr(szToken, chDirSep) != NULL)
					return GenerateError(E_ATL_COMPOUND_KEY);
				hTreeCur = InsertItem(m_TreeCtrl.m_hWnd, szToken, IMAGEKEY, 
									  MAKELPARAM(Key,0), hTreeParent);
				BREAK_ON_ERROR(NextToken(szToken))
			}

			if (*szToken == chEquals) // Skip assignment
			{
				TCHAR szValue[MAX_VALUE];

				RET_ON_ERROR(NextToken(szToken))
				SkipWhiteSpace();
				RET_ON_ERROR(NextToken(szValue)) //szToken has value type
				InsertItem(m_TreeCtrl.m_hWnd, szValue, IMAGEVALTEXT, 
										  MAKELPARAM(DefaultVal,0), hTreeCur);
				LPARAM lOld;
				GetLParam(hTreeCur, lOld);
				SetLParam(hTreeCur, MAKELPARAM(LOWORD(lOld), 1));
				RET_ON_ERROR(NextToken(szToken))
			}

			if (*szToken == chLeftBracket)
			{
				BREAK_ON_ERROR(SubkeysToTree(hTreeCur))
				BREAK_ON_ERROR(NextToken(szToken))
			}

		}

		return hr;
	}

	HRESULT RegFileToTree(LPTSTR szRegFile)
	{
		TCHAR   szToken[_MAX_PATH];
		HRESULT hr = S_OK;
		HTREEITEM hTreeCur;

		m_pchCur = szRegFile;

		while (chEOS != *m_pchCur)
		{
			hr = NextToken(szToken);
			if (FAILED(hr))
				break;

			hTreeCur = InsertItem(m_TreeCtrl.m_hWnd, szToken, 
								  IMAGEROOTKEY, MAKELPARAM(RootKey,0));
			RET_ON_ERROR(NextToken(szToken))

			if (chLeftBracket != *szToken)
			{
				ATLTRACE(_T("Syntax error, expecting a {, found a %s\n"), szToken);
				hr = GenerateError(E_ATL_MISSING_OPENKEY_TOKEN);
				hr = E_FAIL;
				break;
			}
			HRESULT hr = SubkeysToTree(hTreeCur);

			SkipWhiteSpace();
		}
		return hr;
	}

	void InitTVItem(TV_ITEM& tvi, HTREEITEM hItem, LPTSTR szDest, int cchMax = MAX_VALUE)
	{
		tvi.mask = TVIF_TEXT | TVIF_PARAM;
		tvi.hItem = hItem;
		tvi.pszText = szDest;
		tvi.cchTextMax = cchMax;
	}

	void CatString(LPTSTR& szDest, LPCTSTR szSrc)
	{
		lstrcpy(szDest, szSrc);
		szDest += lstrlen(szSrc);
	}

	void CatChar(LPTSTR& szDest, TCHAR ch)
	{
		*szDest = ch;
		szDest += sizeof(TCHAR);
	}

	void PrepareBufferForItem(LPTSTR& szDest, TV_ITEM& tvi, int iLevel)
	{
		switch (LOWORD(tvi.lParam))
		{
			case Replacement:
				CatChar(szDest, chQuote);
				break;
			case DefaultVal:
				break;
			case NamedVal:
				Tabify(szDest, iLevel);
				CatString(szDest, _T("val"));
				CatChar(szDest, chSpace);
				CatChar(szDest, chQuote);
				break;
			case Key:
				Tabify(szDest, iLevel);
				CatChar(szDest, chQuote);
				break;
			default:
				ATLTRACE(_T("Unexpected value of %d in lParam\n"));
				_ASSERTE(FALSE);
		}
	}
	void Tabify(LPTSTR& szDest, int iTabs)
	{
		for (int i = 0; i < iTabs; i++)
			CatChar(szDest, chTab);
	}
	void CatRet(LPTSTR& szDest)
	{
		CatChar(szDest, chCR);
		CatChar(szDest, chLF);
	}
	BOOL TVItemToStream(LPTSTR& szDest, TV_ITEM& tvi, int iLevel)
	{
		BOOL bRet = FALSE;
		ATLTRACE(_T("lParam = %d\n"), LOWORD(tvi.lParam));
		switch (LOWORD(tvi.lParam))
		{
			case Replacement:
				CatChar(szDest, chQuote);
			case DefaultVal:
				if (TreeView_GetNextSibling(m_TreeCtrl.m_hWnd, tvi.hItem))
				{
					CatChar(szDest, chQuote);
					CatRet(szDest);
					Tabify(szDest, iLevel-1);
					CatChar(szDest, chLeftBracket);
					bRet = TRUE;
					CatRet(szDest);
				}
				else
				{
					CatChar(szDest, chQuote);
					CatRet(szDest);
				}
				break;
			case NamedVal:
				CatChar(szDest, chQuote);
				CatChar(szDest, chSpace);
				if (HIWORD(tvi.lParam))
					CatString(szDest, _T(" = s '"));
				break;
			case Key:
				CatChar(szDest, chQuote);
				CatChar(szDest, chSpace);
				if (HIWORD(tvi.lParam))
					CatString(szDest, _T(" = s '"));
				else
					CatRet(szDest);
				break;
			default:
				ATLTRACE(_T("Unexpected value of %d in lParam\n"));
				_ASSERT(FALSE);
		}
		return bRet;
	}

	HRESULT TreeToSubkeys(HTREEITEM hTreeParent, LPTSTR& szDest, int iLevel)
	{
		HRESULT hr;
		HTREEITEM hCur = TreeView_GetChild(m_TreeCtrl.m_hWnd, hTreeParent);

		TV_ITEM tvi;
		tvi.mask = TVIF_PARAM;
		tvi.hItem = hCur;
		BOOL bRequiresClose = FALSE;
		if (hCur != NULL)
		{
			if(!TreeView_GetItem(m_TreeCtrl.m_hWnd, &tvi))
			{
				ATLTRACE(_T("Failed to aquire lParam\n"));
				return E_FAIL;
			}
			if (LOWORD(tvi.lParam) == Key)
			{
				Tabify(szDest, iLevel-1);
				CatChar(szDest, chLeftBracket);
				CatRet(szDest);
				bRequiresClose = TRUE;
			}
		}
		while (hCur != NULL)
		{
			tvi.mask = TVIF_PARAM;
			tvi.hItem = hCur;
			if(!TreeView_GetItem(m_TreeCtrl.m_hWnd, &tvi))
			{
				ATLTRACE(_T("Failed to aquire lParam\n"));
				hr = E_FAIL;
				break;
			}
			PrepareBufferForItem(szDest, tvi, iLevel);
			InitTVItem(tvi, hCur, szDest);
			if(!TreeView_GetItem(m_TreeCtrl.m_hWnd, &tvi))
			{
				ATLTRACE(_T("Failed to write to Mapfile\n"));
				hr = E_FAIL;
				break;
			}
			szDest += lstrlen(szDest);
			if(TVItemToStream(szDest,tvi, iLevel)) // Need to not erase case above
				bRequiresClose = TRUE; 
			if (FAILED(hr = TreeToSubkeys(hCur, szDest, iLevel+1)))
				return hr;
			hCur = TreeView_GetNextSibling(m_TreeCtrl.m_hWnd, hCur);
		}
		if (bRequiresClose)
		{
			Tabify(szDest, iLevel-1);
			CatChar(szDest, chRightBracket);
			CatRet(szDest);
		}
		return S_OK;
		
	}


	HRESULT TreeToRegFile(LPTSTR& szDest)
	{
		HRESULT hr = S_OK;
		LPTSTR szBase = szDest;
		int iLevel = 0;
		for (HTREEITEM hCur = TreeView_GetRoot(m_TreeCtrl.m_hWnd); hCur != NULL; 
			 hCur = TreeView_GetNextSibling(m_TreeCtrl.m_hWnd, hCur))
		{
			TV_ITEM tvi;
			InitTVItem(tvi, hCur, szDest);
			if(!TreeView_GetItem(m_TreeCtrl.m_hWnd, &tvi))
			{
				ATLTRACE(_T("Failed to write to Mapfile\n"));
				hr = E_FAIL;
				break;
			}
			szDest += lstrlen(szDest);
			CatRet(szDest);
			hr = TreeToSubkeys(hCur, szDest, iLevel+1);
			if(FAILED(hr))
				return hr;
		}
		return hr;
	}

	STDMETHOD(put_RGSPath)(BSTR pPath)
	{
		m_strPath = pPath;
		return S_OK;
	}

	STDMETHOD(get_RGSPath)(BSTR* ppPath)
	{
		*ppPath = m_strPath.Copy();
		if (*ppPath == NULL)
			return E_OUTOFMEMORY;
		return S_OK;
	}
	HRESULT SaveRegFile()
	{
		USES_CONVERSION;
		TCHAR szFullPath[_MAX_PATH];
		szFullPath[0] = NULL;

		if (m_TreeCtrl.m_hWnd == NULL)
			return S_OK;
		
		OPENFILENAME ofn;
		memset(&ofn, 0, sizeof(OPENFILENAME));
		ofn.lStructSize = sizeof(OPENFILENAME);
		ofn.hwndOwner = m_TreeCtrl.m_hWnd;
		ofn.lpstrFilter = m_szFilter;
		ofn.lpstrFile = szFullPath;
		ofn.nMaxFile = _MAX_PATH;

		if (!GetSaveFileName(&ofn))
		{
			ATLTRACE(_T("Failed to get save file from Common Dlg\n"));
			return E_FAIL;
		}

		HANDLE hFile = CreateFile(szFullPath, GENERIC_READ | GENERIC_WRITE, 
								  0, NULL, OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);

		if (NULL == hFile)
		{
			ATLTRACE(_T("Failed to CreateFile on %s\n"), OLE2T(m_strPath));
			return HRESULT_FROM_WIN32(GetLastError());
		}

		m_strPath = szFullPath;

		HANDLE hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, RGSFILEMAX, NULL);

		if (NULL == hMapping)
		{
			ATLTRACE(_T("Failed to CreateFileMapping\n"));
			return HRESULT_FROM_WIN32(GetLastError());
		}

		LPVOID pMap = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, RGSFILEINIT);

		if (NULL == pMap)
		{
			ATLTRACE(_T("Failed to MapViewOfFile\n"));
			return HRESULT_FROM_WIN32(GetLastError());
		}

		HRESULT hr;
		LPTSTR szMap = (LPTSTR)pMap;
		hr = TreeToRegFile(szMap);
		SetFilePointer(hFile, (BYTE*)szMap - (BYTE*)pMap, 0, FILE_BEGIN);
		UnmapViewOfFile(pMap);
		CloseHandle(hMapping);
		SetEndOfFile(hFile);
		CloseHandle(hFile);

		return hr;
	}
	HRESULT EditRegFile()
	{
		USES_CONVERSION;

		if (m_TreeCtrl.m_hWnd == NULL)
			return S_OK;

		HANDLE hFile = CreateFile(OLE2T(m_strPath), GENERIC_READ, 0, NULL,
								  OPEN_EXISTING,
								  FILE_ATTRIBUTE_READONLY,
								  NULL);

		if (NULL == hFile)
		{
			ATLTRACE(_T("Failed to CreateFile on %s\n"), OLE2T(m_strPath));
			return HRESULT_FROM_WIN32(GetLastError());
		}

		DWORD cbFile = GetFileSize(hFile, NULL); // No HiOrder DWORD required

		HANDLE hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);

		if (NULL == hMapping)
		{
			ATLTRACE(_T("Failed to CreateFileMapping\n"));
			return HRESULT_FROM_WIN32(GetLastError());
		}

		LPVOID pMap = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);

		if (NULL == pMap)
		{
			ATLTRACE(_T("Failed to MapViewOfFile\n"));
			return HRESULT_FROM_WIN32(GetLastError());
		}

		LPTSTR szReg = A2T((char*)pMap);

		if (chEOS != szReg[cbFile]) //ensure buffer is NULL terminated
		{
			ATLTRACE(_T("ERROR : Bad or missing End of File\n"));
			return E_FAIL; // make a real error
		}

	#ifdef _DEBUG
		OutputDebugString(szReg); //would call ATLTRACE but szReg is > 512 bytes
		OutputDebugString(_T("\n"));
	#endif //_DEBUG

		HRESULT hr;
		hr = RegFileToTree(szReg);
		UnmapViewOfFile(pMap);
		CloseHandle(hMapping);
		CloseHandle(hFile);

		return hr;
	}
};
