/**
 * @param uNotifyCode - notification code if the message is from a control. If the message is from an accelerator, this value is 1. If the message is from a menu, this value is zero.
 * @param nID - specifies the identifier of the menu item, control, or accelerator.
 * @param hWndCtl - handle to the control sending the message if the message is from a control. Otherwise, this parameter is NULL.
 */
void CManualModeDlg::OnChangeMapPdbFile(UINT /*uNotifyCode*/, int /*nID*/, HWND /*hWndCtl*/)
{
	try
	{
		ClearProcessor();
		CString strMapPdbFile;
		m_txtMapPdbFile.GetWindowText(strMapPdbFile);
		if (strMapPdbFile.IsEmpty())
			return;
		if (GetFileAttributes(strMapPdbFile) != INVALID_FILE_ATTRIBUTES)
		{
			CWaitCursor wait;
			CBaseProcessor::PROCESSED_FILE_TYPE eProcessedFileType = CBaseProcessor::GetFileType(strMapPdbFile);
			switch (eProcessedFileType)
			{
			case CBaseProcessor::PFT_MAP:
				{
					CMapProcessor* pMapProcessor = new CMapProcessor();
					m_pProcessor.reset(pMapProcessor);
					m_eProcessedFileType = CBaseProcessor::PFT_MAP;
					pMapProcessor->LoadMapText(strMapPdbFile);
					CNumEdit& txtBaseAddress = m_txtBaseAddress.GetNumEdit();
					PVOID pPreferredBaseAddress = pMapProcessor->GetBaseAddress();
					if (pPreferredBaseAddress)
					{
						CString strPreferredBaseAddress;
						strPreferredBaseAddress.Format(_T("%08X"), pPreferredBaseAddress);
						txtBaseAddress.SetWindowText(strPreferredBaseAddress);
					}
					else
						txtBaseAddress.SetWindowText(NULL);
				}
				break;
			case CBaseProcessor::PFT_PDB:
				{
					m_eProcessedFileType = CBaseProcessor::PFT_PDB;
					CNumEdit& txtModuleSize = m_txtModuleSize.GetNumEdit();
					txtModuleSize.EnableWindow(TRUE);
					CRadixMenu& btnModuleSize = m_txtModuleSize.GetRadixMenu();
					btnModuleSize.EnableWindow(TRUE);
				}
				break;
			default:
				MsgTip::ShowMessage(m_txtMapPdbFile, IDS_INVALIDMAPPDBFILE);
				return;
			}
		}
	}
	catch (std::exception& error)
	{
		CString strProjectTitle;
		strProjectTitle.LoadString(IDS_PROJECTTITLE);
		MessageBox(CA2CT(error.what()), strProjectTitle, MB_OK | MB_ICONERROR);
	}
}
/**
 * @param uNotifyCode - notification code if the message is from a control. If the message is from an accelerator, this value is 1. If the message is from a menu, this value is zero.
 * @param nID - specifies the identifier of the menu item, control, or accelerator.
 * @param hWndCtl - handle to the control sending the message if the message is from a control. Otherwise, this parameter is NULL.
 */
void CManualModeDlg::OnCalculate(UINT /*uNotifyCode*/, int /*nID*/, HWND /*hWndCtl*/)
{
	try
	{
		CString strMapPdbFile;
		m_txtMapPdbFile.GetWindowText(strMapPdbFile);
		if (strMapPdbFile.IsEmpty())
		{
			MsgTip::ShowMessage(m_txtMapPdbFile, IDS_INVALIDMAPPDBFILE);
			return;
		}

		CNumEdit& txtBaseAddress = m_txtBaseAddress.GetNumEdit();
		DWORD64 dwBaseAddress64;
		txtBaseAddress.GetValue(dwBaseAddress64);
		PVOID ptrBaseAddress = (PVOID)dwBaseAddress64;
		if (! ptrBaseAddress)
		{
			MsgTip::ShowMessage(txtBaseAddress, IDS_INVALIDBASEADDRESS);
			return;
		}

		CNumEdit& txtCrashAddress = m_txtCrashAddress.GetNumEdit();
		DWORD64 dwCrashAddress64;
		txtCrashAddress.GetValue(dwCrashAddress64);
		PVOID ptrCrashAddress = (PVOID)dwCrashAddress64;
		if (! ptrCrashAddress)
		{
			MsgTip::ShowMessage(txtCrashAddress, IDS_INVALIDCRASHADDRESS);
			return;
		}

		CWaitDialog wait(m_hWnd);
		switch (m_eProcessedFileType)
		{
		case CBaseProcessor::PFT_MAP:
			{
				CBaseProcessor* pBaseProcessor = m_pProcessor.get();
				if (! pBaseProcessor || pBaseProcessor->GetRuntimeClass() != RUNTIME_CLASS(CMapProcessor))
				{
					MsgTip::ShowMessage(m_txtMapPdbFile, IDS_INVALIDMAPPDBFILE);
					return;
				}
				CMapProcessor* pMapProcessor = STATIC_DOWNCAST(CMapProcessor, pBaseProcessor);
				pMapProcessor->SetBaseAddress(ptrBaseAddress);
			}
			break;
		case CBaseProcessor::PFT_PDB:
			{
				CNumEdit& txtModuleSize = m_txtModuleSize.GetNumEdit();
				DWORD32 dwModuleSize;
				txtModuleSize.GetValue(dwModuleSize);
				if (! dwModuleSize)
				{
					MsgTip::ShowMessage(txtModuleSize, IDS_INVALIDMODULESIZE);
					return;
				}
				m_pProcessor.reset(); // unload old modules
				CPdbProcessor* pPdbProcessor = new CPdbProcessor();
				m_pProcessor.reset(pPdbProcessor);
				pPdbProcessor->LoadModule(strMapPdbFile, ptrBaseAddress, dwModuleSize);
			}
			break;
		default:
			MsgTip::ShowMessage(m_txtMapPdbFile, IDS_INVALIDMAPPDBFILE);
			return;
		}

		CString strFunctionInfo;
		boost::shared_ptr<CBaseFnInfo> pFnInfo;
		DWORD64 dwDisplacement64;
		if (m_pProcessor->FindFunctionInfo(ptrCrashAddress, pFnInfo, dwDisplacement64))
		{
			CString strFunctionName(CA2CT(pFnInfo->GetName().c_str()));
			if (dwDisplacement64 != 0)
				strFunctionInfo.Format(_T("%s+%I64u bytes"), strFunctionName, dwDisplacement64);
			else
				strFunctionInfo = strFunctionName;
		}
		else
			strFunctionInfo.LoadString(IDS_NOFUNCTIONINFO);
		m_txtFunctionName.SetWindowText(strFunctionInfo);

		CString strSourceFile, strLineInfo;
		boost::shared_ptr<CBaseFileInfo> pFileInfo;
		boost::shared_ptr<CBaseLineInfo> pLineInfo;
		DWORD dwDisplacement32;
		if (m_pProcessor->FindLineInfo(ptrCrashAddress, pFileInfo, pLineInfo, dwDisplacement32))
		{
			strSourceFile = CA2CT(pFileInfo->GetFileName().c_str());
			UINT uLineNumber = pLineInfo->GetNumber();
			if (dwDisplacement32 != 0)
				strLineInfo.Format(_T("line %u+%I32u bytes"), uLineNumber, dwDisplacement32);
			else
				strLineInfo.Format(_T("line %u"), uLineNumber);
		}
		else
		{
			strSourceFile.LoadString(IDS_NOSOURCEFILEINFO);
			strLineInfo.LoadString(IDS_NOLINEINFO);
		}
		m_txtSourceFile.SetWindowText(strSourceFile);
		m_txtLineNumber.SetWindowText(strLineInfo);
	}
	catch (std::exception& error)
	{
		CString strProjectTitle;
		strProjectTitle.LoadString(IDS_PROJECTTITLE);
		MessageBox(CA2CT(error.what()), strProjectTitle, MB_OK | MB_ICONERROR);
	}
}
/**
 * @param strModule - module file name.
 * @return pointer to module processor.
 */
boost::shared_ptr<CBaseProcessor> CExpressModeDlg::GetModuleInfo(const CString& strModule)
{
	boost::shared_ptr<CBaseProcessor> pBaseProcessor;
	if (strModule.IsEmpty())
		return pBaseProcessor;
	CModuleMap::iterator itModule = m_mapModules.find(strModule);
	if (itModule == m_mapModules.end())
	{
		if (m_pXMLNodeProcess != NULL)
		{
			CString strExpression;
			strExpression.Format(_T("./modules/module[name=\"%s\"]"), strModule);
			CComPtr<IXMLDOMNode> pXMLNodeModule;
			if (SelectXMLNode(m_pXMLNodeProcess, CT2CW(strExpression), pXMLNodeModule))
			{
				CString strBaseAddress;
				GetXMLNodeText(pXMLNodeModule, OLESTR("./base"), strBaseAddress);
				PVOID ptrBaseAddress = (PVOID)_tcstoui64(strBaseAddress, NULL, 0);
				CString strModuleSize;
				GetXMLNodeText(pXMLNodeModule, OLESTR("./size"), strModuleSize);
				DWORD dwModuleSize = _tcstoul(strModuleSize, NULL, 0);
				if (ptrBaseAddress != NULL && dwModuleSize != 0)
				{
					TCHAR szModuleName[MAX_PATH];
					_tcscpy_s(szModuleName, countof(szModuleName), PathFindFileName(strModule));
					TCHAR szMapPdbFolder[MAX_PATH];
					m_txtMapPdbFolder.GetWindowText(szMapPdbFolder, countof(szMapPdbFolder));
					TCHAR szSystemFolder[MAX_PATH];
					GetSystemDirectory(szSystemFolder, countof(szSystemFolder));
					TCHAR szMapPdbFile[MAX_PATH];
					if (! FindFileByPattern(szMapPdbFolder, szModuleName, _T(".map"), szMapPdbFile) &&
						! FindFileByPattern(szMapPdbFolder, szModuleName, _T(".pdb"), szMapPdbFile) &&
						! FindFileByPattern(szSystemFolder, szModuleName, _T(".map"), szMapPdbFile) &&
						! FindFileByPattern(szSystemFolder, szModuleName, _T(".pdb"), szMapPdbFile))
					{
						return pBaseProcessor;
					}
					CBaseProcessor::PROCESSED_FILE_TYPE eProcessedFileType = CBaseProcessor::GetFileType(szMapPdbFile);
					switch (eProcessedFileType)
					{
					case CBaseProcessor::PFT_MAP:
						{
							CMapProcessor* pMapProcessor = STATIC_DOWNCAST(CMapProcessor, m_pMapProcessor.get());
							if (pMapProcessor == NULL)
							{
								pMapProcessor = new CMapProcessor();
								pBaseProcessor.reset(pMapProcessor);
							}
							pBaseProcessor = m_pMapProcessor;
							pMapProcessor->LoadMapText(szMapPdbFile);
							pMapProcessor->SetBaseAddress(ptrBaseAddress);
						}
						break;
					case CBaseProcessor::PFT_PDB:
						{
							CPdbProcessor* pPdbProcessor = STATIC_DOWNCAST(CPdbProcessor, m_pPdbProcessor.get());
							if (pPdbProcessor == NULL)
							{
								pPdbProcessor = new CPdbProcessor();
								m_pPdbProcessor.reset(pPdbProcessor);
							}
							pBaseProcessor = m_pPdbProcessor;
							pPdbProcessor->LoadModule(szMapPdbFile, ptrBaseAddress, dwModuleSize);
						}
						break;
					}
					if (pBaseProcessor.get() != NULL)
						m_mapModules.insert(CModuleMap::value_type(strModule, pBaseProcessor));
				}
			}
		}
	}
	else
		pBaseProcessor = itModule->second;
	return pBaseProcessor;
}