// MonitoringHostSampleDlg.cpp : implementation file
//
// created by Unwinder
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "MonitoringHostSample.h"
#include "MonitoringHostSampleDlg.h"
/////////////////////////////////////////////////////////////////////////////
#include <io.h>
#include <float.h>
#include <shlwapi.h>
/////////////////////////////////////////////////////////////////////////////
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About
/////////////////////////////////////////////////////////////////////////////
class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CAboutDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	//{{AFX_MSG(CAboutDlg)
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};
/////////////////////////////////////////////////////////////////////////////
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
}
/////////////////////////////////////////////////////////////////////////////
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	//}}AFX_DATA_MAP
}
/////////////////////////////////////////////////////////////////////////////
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
		// No message handlers
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CMonitoringHostSampleDlg dialog
/////////////////////////////////////////////////////////////////////////////
CMonitoringHostSampleDlg::CMonitoringHostSampleDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CMonitoringHostSampleDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CMonitoringHostSampleDlg)
	m_strStatus = _T("");
	//}}AFX_DATA_INIT
	m_hIcon				= AfxGetApp()->LoadIcon(IDR_MAINFRAME);

	m_hPlugin			= NULL;
	m_pGetSourceData	= NULL;
	m_nTimerID			= NULL;
}
/////////////////////////////////////////////////////////////////////////////
void CMonitoringHostSampleDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CMonitoringHostSampleDlg)
	DDX_Control(pDX, IDC_PLUGINS_COMBO, m_pluginsCombo);
	DDX_Text(pDX, IDC_STATUS, m_strStatus);
	//}}AFX_DATA_MAP
}
/////////////////////////////////////////////////////////////////////////////
BEGIN_MESSAGE_MAP(CMonitoringHostSampleDlg, CDialog)
	//{{AFX_MSG_MAP(CMonitoringHostSampleDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_WM_DESTROY()
	ON_CBN_SELCHANGE(IDC_PLUGINS_COMBO, OnSelchangePluginsCombo)
	ON_WM_TIMER()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CMonitoringHostSampleDlg message handlers
/////////////////////////////////////////////////////////////////////////////
BOOL CMonitoringHostSampleDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	SetIcon(m_hIcon, TRUE);	

	InitPluginsList();
	InitPluginsCombo();
	LoadPlugin();
	UpdateStatus();

	m_nTimerID = SetTimer(0x1234, 1000, NULL);
	
	return TRUE;  // return TRUE  unless you set the focus to a control
}
/////////////////////////////////////////////////////////////////////////////
void CMonitoringHostSampleDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}
/////////////////////////////////////////////////////////////////////////////
// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.
/////////////////////////////////////////////////////////////////////////////
void CMonitoringHostSampleDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (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 CMonitoringHostSampleDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}
/////////////////////////////////////////////////////////////////////////////
// This helper function enumerates plugin libraries, probes all data sources
// exported by a plugin and builds a list of available plugin modules
/////////////////////////////////////////////////////////////////////////////
void CMonitoringHostSampleDlg::InitPluginsList()
{
	//first, we'll need to delete the previously initialized list

	UninitPluginsList();

	//get fully qualified path to the sample application

	char szPluginsFolder[MAX_PATH];
	GetModuleFileName(NULL, szPluginsFolder, MAX_PATH);
	PathRemoveFileSpec(szPluginsFolder);

	//save path in CString variable for further convenient processing

	CString strPluginsFolder = szPluginsFolder;

	//enumerate the plugins by probing all *.dll files in the application's
	//folder

	_finddata_t findData;

	DWORD dwHandle = _findfirst(strPluginsFolder + "\\*.dll", &findData);

	if (dwHandle != 0xffffffff)
	{
		do 
		{
			//get fully qualified path to the currently enumerated .dll library

			CString strPluginPath = strPluginsFolder + "\\" + findData.name;

			//try to load library to see if it is a plugin library

			HMODULE hModule = LoadLibrary(strPluginPath);

			if (hModule)
			{
				//try to get ptrs to all required plugin APL functions
				
				GET_SOURCES_NUM_PROC pGetSourcesNum = (GET_SOURCES_NUM_PROC)GetProcAddress(hModule, "GetSourcesNum");
				GET_SOURCE_DESC_PROC pGetSourceDesc = (GET_SOURCE_DESC_PROC)GetProcAddress(hModule, "GetSourceDesc");
				GET_SOURCE_DATA_PROC pGetSourceData = (GET_SOURCE_DATA_PROC)GetProcAddress(hModule, "GetSourceData");

				//if the library exports all these funstions - we've found the plugin

				if (pGetSourcesNum &&
					pGetSourceDesc &&
					pGetSourceData)
				{
					//get number of data sources provided by the plugin

					DWORD dwSources = pGetSourcesNum();
				
					//loop through all avaiable sources and skip unsupported sources

					for (DWORD dwSource=0; dwSource<dwSources; dwSource++)
					{
						//allocate and zap source descriptor

						MONITORING_SOURCE_DESC SourceDesc;
						ZeroMemory(&SourceDesc, sizeof(MONITORING_SOURCE_DESC));

						//init source descriptor version to allow the plugin
						//to determine which descriptor's fields are to be filled

						SourceDesc.dwVersion = 0x00010001;

						//try to fill source descriptor

						//take a note that this function will
						//succeed only if the source is supported by the system

						if (pGetSourceDesc(dwSource, &SourceDesc))
						{
							//allocate and zap new plugin descriptor

							LPMONITORING_PLUGIN_DESC pPluginDesc = new MONITORING_PLUGIN_DESC;
							ZeroMemory(pPluginDesc, sizeof(MONITORING_PLUGIN_DESC));

							//store path to the plugin and source index in the descriptor's fields

							strcpy(pPluginDesc->szPath, strPluginPath);
							pPluginDesc->dwIndex = dwSource;

							//Unfortunately MONITORING_SOURCE_DESC_V_1_0 structure (used by RivaTuner
							//v2.0 RC 15.6 plugins) and subsequent MONITORING_SOURCE_DESC structures 
							//(used by RivaTuner v2.0 RC 15.7 and newer plugins) have non-compatible
							//layouts, however we can support both types of plugins via the following
							//trick:
							//New layout of MONITORING_SOURCE_DESC introduced dwVersion field in the
							//begining of the structure, allowing a plugin to determine which fields
							//are to be filled during GetSourceDesc call.
							//MONITORING_SOURCE_DESC_V_1_0 doesn't have dwVersion field and starts 
							//from szName field, so old plugins will always distort dwVersion field
							//on return. So we can use this condition to check which plugin type we
							//are loading, and perform required data correction if we're loading old
							//plugin compiled with MONITORING_SOURCE_DESC_V_1_0

							if (SourceDesc.dwVersion == 0x00010001)
								memcpy(&pPluginDesc->SourceDesc, &SourceDesc, sizeof(MONITORING_SOURCE_DESC));
							else
								memcpy(&pPluginDesc->SourceDesc.szName, &SourceDesc, sizeof(MONITORING_SOURCE_DESC_V_1_0));

							//add plugin descriptor to the list

							m_pluginsList.AddTail(pPluginDesc);
						}
					}
				}

				FreeLibrary(hModule);
			}
		}
		while (_findnext(dwHandle, &findData) != -1);

		_findclose(dwHandle);
	}
}
/////////////////////////////////////////////////////////////////////////////
// This helper function uninitializes the previously initialized list of the
// plugin modules
/////////////////////////////////////////////////////////////////////////////
void CMonitoringHostSampleDlg::UninitPluginsList()
{
	POSITION pos = m_pluginsList.GetHeadPosition();

	while (pos)
		delete m_pluginsList.GetNext(pos);

	m_pluginsList.RemoveAll();
}
/////////////////////////////////////////////////////////////////////////////
void CMonitoringHostSampleDlg::OnDestroy() 
{
	//kill timer and ensure that there are no timer messages in the queue

	if (m_nTimerID)
		KillTimer(m_nTimerID);

	m_nTimerID = NULL;

	MSG msg; 
	while (PeekMessage(&msg, m_hWnd, WM_TIMER, WM_TIMER, PM_REMOVE));

	//unload currently loaded plugin

	UnloadPlugin();

	//destroy plugins list

	UninitPluginsList();

	CDialog::OnDestroy();
}
/////////////////////////////////////////////////////////////////////////////
// This helper function initializes cobmo box control using the previously
// filled list of the plugins
/////////////////////////////////////////////////////////////////////////////
void CMonitoringHostSampleDlg::InitPluginsCombo()
{
	//reset combo box contents

	m_pluginsCombo.ResetContent();

	//loop through previously filled list of the plugins

	POSITION pos = m_pluginsList.GetHeadPosition();

	while (pos)
	{
		//get ptr to plugin descriptor

		LPMONITORING_PLUGIN_DESC pDesc = (LPMONITORING_PLUGIN_DESC)m_pluginsList.GetNext(pos);

		//format <PLUGIN_LIBRARY_NAME>:<DATA_SOURCE_INDEX> string for GUI

		char szPlugin[MAX_PATH];
		strcpy(szPlugin, pDesc->szPath);
		PathStripPath(szPlugin);

		CString strPlugin;
		strPlugin.Format("%s:%d", szPlugin, pDesc->dwIndex);

		//add new string to combo box and use item data to save ptr to plugin descriptor
		//associated with this item
		
		m_pluginsCombo.SetItemData(m_pluginsCombo.AddString(strPlugin), (DWORD)pDesc);
	}

	//if the combo box is empty we'll disable it and notify user about lack of plugins

	if (!m_pluginsCombo.GetCount())
	{
		m_pluginsCombo.AddString("There are no plugins detected");
		m_pluginsCombo.EnableWindow(FALSE);
	}

	//select the first plugin

	m_pluginsCombo.SetCurSel(0);
}
/////////////////////////////////////////////////////////////////////////////
void CMonitoringHostSampleDlg::OnSelchangePluginsCombo() 
{
	//load the plugin associated with the currently selected item in the combo box

	LoadPlugin();

	//update status window

	UpdateStatus();
}
/////////////////////////////////////////////////////////////////////////////
// This helper function is used to load the plugin associated with the currently
// selected item in the combo box
/////////////////////////////////////////////////////////////////////////////
void CMonitoringHostSampleDlg::LoadPlugin()
{
	//unload the previously loaded plugin module

	UnloadPlugin();

	//check if there is selected plugin module

	int iSel = m_pluginsCombo.GetCurSel();

	if (iSel != -1)
	{
		//get plugin descriptor from the item's user data

		m_pPluginDesc		= (LPMONITORING_PLUGIN_DESC)m_pluginsCombo.GetItemData(iSel);

		//try to load plugin

		m_hPlugin			= LoadLibrary(m_pPluginDesc->szPath);

		if (m_hPlugin)
		{
			//try to get ptr to the plugin's GetSourceDesc function

			//IMPORTANT NOTE: in fact, we alrady have the source descriptor stored in the 
			//plugins list, however we _MUST_ call this function after loading the plugin 
			//at least once, because some plugins perform internal initialization during
			//this call

			GET_SOURCE_DESC_PROC pGetSourceDesc = (GET_SOURCE_DESC_PROC)GetProcAddress(m_hPlugin, "GetSourceDesc");

			if (pGetSourceDesc)
			{
				//allocate and zap temporary source descriptor

				MONITORING_SOURCE_DESC SourceDesc;
				ZeroMemory(&SourceDesc, sizeof(MONITORING_SOURCE_DESC));

				//init source descriptor version to allow the plugin
				//to determine which descriptor's fields are to be filled

				SourceDesc.dwVersion = 0x00010001;

				//if GetSourceDesc succeeds, get ptr to the plugin's GetSourceData
				//function

				if (pGetSourceDesc(m_pPluginDesc->dwIndex, &SourceDesc))
					m_pGetSourceData = (GET_SOURCE_DATA_PROC)GetProcAddress(m_hPlugin, "GetSourceData");
			}
		}
	}
}
/////////////////////////////////////////////////////////////////////////////
// This helper function is used to unload currently loaded  plugin
/////////////////////////////////////////////////////////////////////////////
void CMonitoringHostSampleDlg::UnloadPlugin()
{
	if (m_hPlugin)
		FreeLibrary(m_hPlugin);

	m_hPlugin			= NULL;
	m_pGetSourceData	= NULL;
	m_pPluginDesc		= NULL;
}
/////////////////////////////////////////////////////////////////////////////
// This helper function is used to update status window text
/////////////////////////////////////////////////////////////////////////////
void CMonitoringHostSampleDlg::UpdateStatus()
{
	//check if a plugin is currently selected

	if (m_pPluginDesc)
	{
		//try to get source data via the plugin's GetSourceData

		FLOAT data = FLT_MAX;
		if (m_pGetSourceData)
			data = m_pGetSourceData(m_pPluginDesc->dwIndex);

		//format temporary string containing data/dimension

		CString strData;

		if (data == FLT_MAX)
			strData = "N/A";
		else
			strData.Format("%.1f %s", data, m_pPluginDesc->SourceDesc.szDim);

		//format the following status string:

		//<SOURCE NAME> : <DATA>
		//
		//<DESCRIPTION>

		m_strStatus.Format("%-32s : %s\n\n%s", m_pPluginDesc->SourceDesc.szName, strData, m_pPluginDesc->SourceDesc.szDesc);
	}
	else
		m_strStatus = "There is no plugin selected";

	UpdateData(FALSE);
}
/////////////////////////////////////////////////////////////////////////////
void CMonitoringHostSampleDlg::OnTimer(UINT nIDEvent) 
{
	UpdateStatus();
	
	CDialog::OnTimer(nIDEvent);
}
/////////////////////////////////////////////////////////////////////////////
