// NateNMEAInstallerDlg.cpp : implementation file
//

#include "stdafx.h"
#include "NateNMEAInstaller.h"
#include "NateNMEAInstallerDlg.h"
#include "Registry.h"

#include <winioctl.h>

#define IOCTL_HAL_REBOOT CTL_CODE(FILE_DEVICE_HAL, 15, METHOD_BUFFERED, FILE_ANY_ACCESS)

extern "C" __declspec(dllimport) BOOL KernelIoControl(
    DWORD dwIoControlCode, 
    LPVOID lpInBuf, 
    DWORD nInBufSize, 
    LPVOID lpOutBuf, 
    DWORD nOutBufSize, 
    LPDWORD lpBytesReturned);

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

typedef struct com_info {
	WCHAR	wszPortName[256];
	int		nIndex;
} ComPortInfo_t;

/////////////////////////////////////////////////////////////////////////////
// CNateNMEAInstallerDlg dialog

CNateNMEAInstallerDlg::CNateNMEAInstallerDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CNateNMEAInstallerDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CNateNMEAInstallerDlg)
		// NOTE: the ClassWizard will add member initialization here
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
	nComboIndex = 0;
	ComPort[0] = 0;
	ComPort[1] = 0;
	ComPort[2] = 0;
	ComPort[3] = 0;
	ComPort[4] = 0;
	ComPort[5] = 0;
	ComPort[6] = 0;
	ComPort[7] = 0;
	ComPort[8] = 0;
	ComPort[9] = 0;
	ComPort[10] = 0;
	ComPort[11] = 0;
	memset(wstrConvertPort, 0, sizeof(WCHAR)*256);
	nTargetPort = -1;
}

void CNateNMEAInstallerDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CNateNMEAInstallerDlg)
	DDX_Control(pDX, IDC_CHECK_DEBUG, m_checkDebug);
	DDX_Control(pDX, IDC_COMBO_CONVERT_PORT, m_comboConvertPort);
	DDX_Control(pDX, IDC_COMBO_NATE_PORT, m_comboComList);
		// NOTE: the ClassWizard will add DDX and DDV calls here
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CNateNMEAInstallerDlg, CDialog)
	//{{AFX_MSG_MAP(CNateNMEAInstallerDlg)
	ON_BN_CLICKED(IDC_BUTTON_SET, OnButtonSet)
	ON_BN_CLICKED(IDC_BUTTON_CANCEL, OnButtonCancel)
	ON_BN_CLICKED(IDC_BUTTON_REMOVE, OnButtonRemove)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CNateNMEAInstallerDlg message handlers

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

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	CenterWindow(GetDesktopWindow());	// center to the hpc screen

	// TODO: Add extra initialization here
	// 사용 가능한 유효한 포트 리스트를 얻어온다.
	GetComPortList();
	if(m_comboComList.GetCount() > 0) {
		int	nSelItem = 0;
		if(nTargetPort > 0) {
			for(int i = 0; i < m_comboComList.GetCount(); i++) {
				ComPortInfo_t *pPort = (ComPortInfo_t *)m_comboComList.GetItemDataPtr(i);
				if(pPort->nIndex == nTargetPort) {
					nSelItem = i;
					break;
				}
			}
		}
		m_comboComList.SetCurSel(nSelItem);

		// 지정되지 않은 포트 정보를 설정한다.
		int	nListCount = 0;
		nSelItem = 0;
		for(int i = 0; i < 8; i++) {
			CString strComName;

			if(ComPort[i] == 0) {
				ComPortInfo_t *pPort = (ComPortInfo_t *)calloc(1, sizeof(ComPortInfo_t));
				pPort->nIndex = i+1;
				wsprintf(pPort->wszPortName, _T("Serial%d"), pPort->nIndex);
				strComName.Format(_T("Com %d - Unused"), i+1);
				m_comboConvertPort.InsertString(nListCount, strComName);
				m_comboConvertPort.SetItemDataPtr(nListCount, (void *)pPort);
				nListCount++;
			} else if(ComPort[i] == 100) {
				ComPortInfo_t *pPort = (ComPortInfo_t *)calloc(1, sizeof(ComPortInfo_t));
				pPort->nIndex = i+1;
				wsprintf(pPort->wszPortName, _T("Serial%d"), pPort->nIndex);
				strComName.Format(_T("Com %d - Assigned"), i+1);
				m_comboConvertPort.InsertString(nListCount, strComName);
				m_comboConvertPort.SetItemDataPtr(nListCount, (void *)pPort);
				nSelItem = nListCount;
				nListCount++;
			}
		}
		m_comboConvertPort.SetCurSel(nSelItem);
	}

	// 디버깅 상태 정보를 확인한다.
	if(wstrConvertPort[0] != '\0') {
		Registry reg;
		CString	path;
		DWORD	dwDebug = 0;
		path.Format(_T("Drivers\\BuiltIn\\%s"), wstrConvertPort);
		reg.RegGetIntValue(HKEY_LOCAL_MACHINE, path.GetBuffer(0), _T("Debug"), &dwDebug);
		if(dwDebug == 1)
			m_checkDebug.SetCheck(BST_CHECKED);
	}

	return FALSE;  // return TRUE  unless you set the focus to a control
}

#define	MAX_KEY_LENGTH			512

void CNateNMEAInstallerDlg::GetComPortList()
{
	HKEY	hKey;
	LONG	nRtn;

	// 레지스트리에서 통신 포트 리스트를 얻어온다.
	nRtn = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Drivers\\BuiltIn"), 
		0, KEY_READ, &hKey);
	if(nRtn != ERROR_SUCCESS)
		return;

	WCHAR    achKey[MAX_KEY_LENGTH];   // buffer for subkey name
	DWORD    cbName;                   // size of name string 
	TCHAR    achClass[MAX_PATH] = TEXT("");  // buffer for class name 
	DWORD    cchClassName = MAX_PATH;  // size of class string 
	DWORD    cSubKeys=0;               // number of subkeys 
	DWORD    cbMaxSubKey;              // longest subkey size 
	DWORD    cchMaxClass;              // longest class string 
	DWORD    cValues;              // number of values for key 
	DWORD    cchMaxValue;          // longest value name 
	DWORD    cbMaxValueData;       // longest value data 
	DWORD    cbSecurityDescriptor; // size of security descriptor 
	FILETIME ftLastWriteTime;      // last write time 

	// Get the class name and the value count. 
	nRtn = RegQueryInfoKey(
		hKey,                    // key handle 
		achClass,                // buffer for class name 
		&cchClassName,           // size of class string 
		NULL,                    // reserved 
		&cSubKeys,               // number of subkeys 
		&cbMaxSubKey,            // longest subkey size 
		&cchMaxClass,            // longest class string 
		&cValues,                // number of values for this key 
		&cchMaxValue,            // longest value name 
		&cbMaxValueData,         // longest value data 
		&cbSecurityDescriptor,   // security descriptor 
		&ftLastWriteTime);       // last write time 

	// 하위 모든 키를 얻어온다.
	if (cSubKeys) {
		for (unsigned int i = 0; i < cSubKeys; i++) { 
			cbName = MAX_KEY_LENGTH;
			nRtn = RegEnumKeyEx(hKey, i,
				achKey, 
				&cbName, 
				NULL, 
				NULL, 
				NULL, 
				&ftLastWriteTime); 
			if (nRtn == ERROR_SUCCESS) {
				// 지정된 위치의 장치 정보를 얻어온다.
				CString strPath;
				DWORD	nIndex = 0;
				WCHAR	szDevName[256];
				WCHAR	szDllName[256];
				Registry	reg;

				memset(szDevName, 0, sizeof(WCHAR)*256);
				memset(szDllName, 0, sizeof(WCHAR)*256);
				
				strPath.Format(_T("Drivers\\BuiltIn\\%s"), achKey);				
				reg.RegGetValue(HKEY_LOCAL_MACHINE, strPath.GetBuffer(0), 
					_T("Prefix"), szDevName);
				reg.RegGetValue(HKEY_LOCAL_MACHINE, strPath.GetBuffer(0), 
					_T("DLL"), szDllName);
				reg.RegGetIntValue(HKEY_LOCAL_MACHINE, strPath.GetBuffer(0), 
					_T("Index"), &nIndex);
				if(_wcsicmp(szDevName, _T("COM")) == 0 && 
						_wcsicmp(szDllName, _T("NateNMEADriver.dll")) != 0 && nIndex <= 8) {
					ComPortInfo_t *pPort = (ComPortInfo_t *)calloc(1, sizeof(ComPortInfo_t));					
					// 얻어온 통신 포트 정보를 리스트에 등록한다.
					CString strComName;
					strComName.Format(_T("Com %d - %s"), nIndex, achKey);
					ComPort[nIndex-1] = 1;
					pPort->nIndex = nIndex;
					wcscpy(pPort->wszPortName, achKey);
					m_comboComList.InsertString(nComboIndex, strComName);
					m_comboComList.SetItemDataPtr(nComboIndex, (void *)pPort);					
					nComboIndex++;
				} else if(_wcsicmp(szDllName, _T("NateNMEADriver.dll")) == 0) {
					ComPort[nIndex-1] = 100;
					wcscpy(wstrConvertPort, achKey);
					reg.RegGetIntValue(HKEY_LOCAL_MACHINE, strPath.GetBuffer(0), 
						_T("NateGPSPort"), &nTargetPort);
					ComPort[nTargetPort-1] = 10;
				}
			} 
		}
	}

	RegCloseKey(hKey);

	return;
}

void CNateNMEAInstallerDlg::OnButtonSet() 
{
	DWORD nRetID = 0;
	CString	msg = _T("");

	int nSel = m_comboComList.GetCurSel();
	ComPortInfo_t *pTargetPort = (ComPortInfo_t *)m_comboComList.GetItemDataPtr(nSel);
	nSel = m_comboConvertPort.GetCurSel();
	ComPortInfo_t *pConvertPort = (ComPortInfo_t *)m_comboConvertPort.GetItemDataPtr(nSel);
	msg.Format(_T("[Com%d - %s] 포트를 [Com%d - %s] 포트로 연결 하시겠습니까?"), pTargetPort->nIndex,
		pTargetPort->wszPortName, pConvertPort->nIndex, pConvertPort->wszPortName);
	nRetID = MessageBox(msg, _T("확인"), MB_YESNO|MB_ICONQUESTION);
	if(nRetID == IDYES) {
		Registry reg;
		// 기존에 설정된 정보를 제거한다.
		if(wstrConvertPort[0] != '\0')
			reg.RemoveRegKey(HKEY_LOCAL_MACHINE, _T("Drivers\\BuiltIn"), wstrConvertPort);
		// 설정된 정보를 레지스트리에 설정한다.
		CString	path;
		path.Format(_T("Drivers\\BuiltIn\\%s"), pConvertPort->wszPortName);
		reg.CreateRegValueSZ(HKEY_LOCAL_MACHINE, path.GetBuffer(0), _T("Dll"), _T("NateNMEADriver.dll"));
		reg.CreateRegValueSZ(HKEY_LOCAL_MACHINE, path.GetBuffer(0), _T("Prefix"), _T("COM"));
		reg.CreateRegValueSZ(HKEY_LOCAL_MACHINE, path.GetBuffer(0), _T("FriendlyName"), _T("Nate GPS Convert Driver"));
		reg.CreateRegValueINT(HKEY_LOCAL_MACHINE, path.GetBuffer(0), _T("Index"), pConvertPort->nIndex);
		reg.CreateRegValueINT(HKEY_LOCAL_MACHINE, path.GetBuffer(0), _T("Order"), 2);
		reg.CreateRegValueINT(HKEY_LOCAL_MACHINE, path.GetBuffer(0), _T("NateGPSPort"), pTargetPort->nIndex);
		if(m_checkDebug.GetCheck() == BST_CHECKED)
			reg.CreateRegValueINT(HKEY_LOCAL_MACHINE, path.GetBuffer(0), _T("Debug"), 1);
		else
			reg.CreateRegValueINT(HKEY_LOCAL_MACHINE, path.GetBuffer(0), _T("Debug"), 0);

		MessageBox(_T("설정이 완료되었습니다. PDA를 리셋합니다."), _T("확인"), MB_OK|MB_ICONINFORMATION);

		// 재부팅을 수행한다.
		KernelIoControl(IOCTL_HAL_REBOOT, NULL, 0, NULL, 0, NULL);

		Sleep(2000);
	}	
}

void CNateNMEAInstallerDlg::OnButtonCancel() 
{
	// TODO: Add your control notification handler code here
	DWORD nRetID = MessageBox(_T("취소하시겠습니까?"), _T("확인"), MB_YESNO|MB_ICONQUESTION);
	if(nRetID == IDYES)
		exit(0);
}

void CNateNMEAInstallerDlg::OnButtonRemove() 
{
	// TODO: Add your control notification handler code here
	// 기존에 설정된 정보를 제거한다.
	if(wstrConvertPort[0] != '\0') {
		DWORD nRetID = MessageBox(_T("현재 설정된 정보를 제거 하시겠습니까?"), 
				_T("확인"), MB_YESNO|MB_ICONQUESTION);
		if(nRetID == IDYES) {
			Registry reg;
			reg.RemoveRegKey(HKEY_LOCAL_MACHINE, _T("Drivers\\BuiltIn"), wstrConvertPort);

			MessageBox(_T("설정이 제거되었습니다. PDA를 리셋합니다."), _T("확인"), MB_OK|MB_ICONINFORMATION);

			// 재부팅을 수행한다.
			KernelIoControl(IOCTL_HAL_REBOOT, NULL, 0, NULL, 0, NULL);

			Sleep(2000);
		}
	} else {
		MessageBox(_T("설정된 정보가 존재하지 않습니다."), _T("확인"), MB_OK|MB_ICONINFORMATION);
	}
}



