//
// Copyright: © 2009 Sierra Wireless Inc., all rights reserved
//
// SimpleCMDlg.cpp : implementation file
//

#include "stdafx.h"
#include "SimpleCM.h"
#include "SimpleCMDlg.h"

// global pointer to self
CSimpleCMDlg* g_This = NULL;

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#define TIMER_DeviceSearch				WM_USER + 0x1000
#define WM_USER_NOTIFY_SIGNAL_STRENGTH	WM_USER + 0x1001
#define WM_USER_NOTIFY_SESSION_STATE	WM_USER + 0x1002
#define WM_USER_NOTIFY_DEVICE_REMOVAL	WM_USER + 0x1003

// CSimpleCMDlg dialog


CSimpleCMDlg::CSimpleCMDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CSimpleCMDlg::IDD, pParent)
	, m_hGobiApi(NULL)
	, m_szRssi(_T(""))
	, m_szApn(_T(""))
	, m_szUsername(_T(""))
	, m_szPassword(_T(""))
	, m_szStatus(_T(""))
	, m_nSessionId(0)
	, m_szCarrier(_T(""))
	, m_pFnConnect(NULL)
	, m_pFnDisconnect(NULL)
	, m_pFnSetSessionStateCb(NULL)
	, m_pFnSetSignalStrengthCb(NULL)
	, m_pFnSwiSetDeviceRemovalCb(NULL)
	, m_pFnStartDataSession(NULL)
	, m_pFnStopDataSession(NULL)
	, m_pFnGetServingNetwork(NULL)
	, m_pFnGetSessionState(NULL)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CSimpleCMDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Text(pDX, IDC_Rssi, m_szRssi);
	DDX_Text(pDX, IDC_EDT_Apn, m_szApn);
	DDX_Text(pDX, IDC_EDT_Username, m_szUsername);
	DDX_Text(pDX, IDC_EDT_Password, m_szPassword);
	DDX_Text(pDX, IDC_EDT_Status, m_szStatus);
	DDX_Text(pDX, IDC_EDT_Carrier, m_szCarrier);
	DDX_Control(pDX, IDC_CMB_Tech, m_cmbTech);
}

BEGIN_MESSAGE_MAP(CSimpleCMDlg, CDialog)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
	ON_WM_TIMER()
	ON_WM_DESTROY()
	ON_MESSAGE(WM_USER_NOTIFY_SIGNAL_STRENGTH, OnNotifSignalStrength)
	ON_MESSAGE(WM_USER_NOTIFY_SESSION_STATE, OnNotifSessionState)
	ON_MESSAGE(WM_USER_NOTIFY_DEVICE_REMOVAL, OnNotifDeviceRemoval)
	ON_BN_CLICKED(IDC_BTN_Connect, &CSimpleCMDlg::OnBnClickedBtnConnect)
END_MESSAGE_MAP()


// CSimpleCMDlg message handlers

BOOL CSimpleCMDlg::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

	// cache global pointer to self
	g_This = this;

	// set technology type to first item
	m_cmbTech.SetCurSel(0);

	// Initialize GobiApi
	if (Initialize())
	{
		// start timer to search for available device
		SetTimer(TIMER_DeviceSearch, 1000, NULL);
	}
	else
	{
		m_szStatus = L"Failed to initialize GobiApi";
		UpdateData(FALSE);
	}

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

// 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 CSimpleCMDlg::OnPaint()
{
#ifndef UNDER_CE
	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();
	}
#else
	CDialog::OnPaint();
#endif	// UNDER_CE
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CSimpleCMDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

// WinCE helper function
FARPROC WINAPI GetProcAddress_W(__in HMODULE hModule, __in LPCWSTR lpProcName)
{
#ifndef UNDER_CE
	const int nAsciiLen = 256;
	CHAR szAscii[nAsciiLen] =  "";

	WideCharToMultiByte(CP_ACP, 0, lpProcName, -1, szAscii, nAsciiLen, 0, 0);

	return GetProcAddress(hModule, szAscii);
#else
	return GetProcAddress(hModule, lpProcName);
#endif
}

// Initialize GobiApi
BOOL CSimpleCMDlg::Initialize()
{
	m_hGobiApi = LoadLibrary(L"GobiApi.dll");

	if (m_hGobiApi != 0)
	{
		// Function names
		LPCWSTR QCWWAN_CONNECT = L"QCWWANConnect";
		LPCWSTR QCWWAN_DISCONNECT = L"QCWWANDisconnect";
		LPCWSTR QCWWAN_SET_SIG_STR_CB = L"SetSignalStrengthCallback";
		LPCWSTR QCWWAN_SET_SES_STATE_CB = L"SetSessionStateCallback";
		LPCWSTR QCWWAN_SWI_SET_DEV_REM_CB = L"SwiSetDeviceRemovalCallback";
		LPCWSTR QCWWAN_START_DATA_SESSION = L"StartDataSession";
		LPCWSTR QCWWAN_STOP_DATA_SESSION = L"StopDataSession";
		LPCWSTR QCWWAN_GET_SRV_NETWORK = L"GetServingNetwork";
		LPCWSTR QCWWAN_GET_SIGNAL_STRENGTHS = L"GetSignalStrengths";
		LPCWSTR QCWWAN_UIM_GET_PIN_STATUS = L"DMSUIMGetPINStatus";
		LPCWSTR QCWWAN_GET_SESSION_STATE = L"GetSessionState";

		FARPROC pFN = 0;

		pFN = ::GetProcAddress_W(m_hGobiApi, QCWWAN_CONNECT);
		m_pFnConnect = (tFNConnect)pFN;

		pFN = ::GetProcAddress_W(m_hGobiApi, QCWWAN_DISCONNECT);
		m_pFnDisconnect = (tFNDisconnect)pFN;

		pFN = ::GetProcAddress_W(m_hGobiApi, QCWWAN_SET_SIG_STR_CB);
		m_pFnSetSignalStrengthCb = (tFNSetSignalStrengthCallback)pFN;

		pFN = ::GetProcAddress_W(m_hGobiApi, QCWWAN_SET_SES_STATE_CB);
		m_pFnSetSessionStateCb = (tFNSetSessionStateCallback)pFN;

		pFN = ::GetProcAddress_W(m_hGobiApi, QCWWAN_SWI_SET_DEV_REM_CB);
		m_pFnSwiSetDeviceRemovalCb = (tFNSwiSetDeviceRemovalCallback)pFN;

		pFN = ::GetProcAddress_W(m_hGobiApi, QCWWAN_START_DATA_SESSION);
		m_pFnStartDataSession = (tFNStartDataSession)pFN;

		pFN = ::GetProcAddress_W(m_hGobiApi, QCWWAN_STOP_DATA_SESSION);
		m_pFnStopDataSession = (tFNStopDataSession)pFN;

		pFN = ::GetProcAddress_W(m_hGobiApi, QCWWAN_GET_SRV_NETWORK);
		m_pFnGetServingNetwork = (tFNGetServingNetwork)pFN;

		pFN = ::GetProcAddress_W(m_hGobiApi, QCWWAN_GET_SIGNAL_STRENGTHS);
		m_pFnGetSignalStrengths = (tFNGetSignalStrengths)pFN;

		pFN = ::GetProcAddress_W(m_hGobiApi, QCWWAN_UIM_GET_PIN_STATUS);
		m_pFnUIMGetPINStatus = (tFNUIMGetPINStatus)pFN;

		pFN = ::GetProcAddress_W(m_hGobiApi, QCWWAN_GET_SESSION_STATE);
		m_pFnGetSessionState = (tFNGetSessionState)pFN;
	}
	else
		// problem loading GobiApi.DLL
		return FALSE;

	if (!m_pFnConnect || 
		!m_pFnDisconnect ||
		!m_pFnSetSignalStrengthCb ||
		!m_pFnSetSessionStateCb ||
		!m_pFnSwiSetDeviceRemovalCb ||
		!m_pFnStartDataSession ||
		!m_pFnStopDataSession ||
		!m_pFnGetServingNetwork ||
		!m_pFnGetSignalStrengths ||
		!m_pFnUIMGetPINStatus ||
		!m_pFnGetSessionState)
	{
		// problem obtaining function pointers
		m_pFnConnect = NULL;
		m_pFnDisconnect = NULL;
		m_pFnSetSignalStrengthCb = NULL;
		m_pFnSetSessionStateCb = NULL;
		m_pFnSwiSetDeviceRemovalCb = NULL;
		m_pFnStartDataSession = NULL;
		m_pFnStopDataSession = NULL;
		m_pFnGetServingNetwork  = NULL;
		m_pFnGetSignalStrengths = NULL;
		m_pFnUIMGetPINStatus = NULL;
		m_pFnGetSessionState = NULL;

		return FALSE;
	}

	return TRUE;
}

// Search for available device
BOOL CSimpleCMDlg::DeviceSearch()
{
	// Attempt to connect to first available device
	return (0 == m_pFnConnect())?TRUE:FALSE;
}

// Enable/Disable user editable controls
void CSimpleCMDlg::EnableControls(BOOL bEnable /*=TRUE*/)
{
	GetDlgItem(IDC_EDT_Apn)->EnableWindow(bEnable);
	GetDlgItem(IDC_EDT_Password)->EnableWindow(bEnable);
	GetDlgItem(IDC_EDT_Username)->EnableWindow(bEnable);
	GetDlgItem(IDC_CMB_Tech)->EnableWindow(bEnable);
}

void CSimpleCMDlg::OnDestroy()
{
	CDialog::OnDestroy();

	if (m_pFnDisconnect && m_pFnSetSignalStrengthCb && m_pFnSetSessionStateCb && m_pFnSwiSetDeviceRemovalCb)
	{
		// clear callback handlers
		m_pFnSetSignalStrengthCb(NULL, 0, 0);
		m_pFnSetSessionStateCb(NULL);
		m_pFnSwiSetDeviceRemovalCb(NULL);

		// disconnect from device
		m_pFnDisconnect();
	}

	FreeLibrary(m_hGobiApi);

	g_This = NULL;
}

// Signal strength callback handler
static void GOBICALLBACK SignalStrengthCallback(INT8 nSignalStrength, ULONG nRadioInterface)
{
	g_This->PostMessage(WM_USER_NOTIFY_SIGNAL_STRENGTH, (WPARAM)nSignalStrength, (LPARAM)nRadioInterface);
}

// Session state callback handler
static void GOBICALLBACK SessionStateCallback(ULONG nConnectionStatus, ULONG nReason)
{
	g_This->PostMessage(WM_USER_NOTIFY_SESSION_STATE, (WPARAM)nConnectionStatus, (LPARAM)nReason);
}

// Session state callback handler
static void GOBICALLBACK DeviceRemovalCallback(ULONG nStatus)
{
	g_This->PostMessage(WM_USER_NOTIFY_DEVICE_REMOVAL, (WPARAM)nStatus, NULL);
}

void CSimpleCMDlg::OnTimer(UINT_PTR nIDEvent)
{
	switch (nIDEvent)
	{
		// search for available device
		case TIMER_DeviceSearch:
			if (DeviceSearch())
			{
				// device found

				KillTimer(nIDEvent);

				// Set signal strength callback thresholds
				INT8 Thresholds[] = { -106, -100, -93, -86, -80 }; 
				m_pFnSetSignalStrengthCb(SignalStrengthCallback, 5, Thresholds);

				// Assign session state callback handler
				m_pFnSetSessionStateCb(SessionStateCallback);

				// Assign device removal callback handler
				m_pFnSwiSetDeviceRemovalCb(DeviceRemovalCallback);

				// Get initial network information
				const BYTE MAX_SS = 5;
				ULONG	nArraySize = MAX_SS;
				INT8	nSignalStrength[MAX_SS];
				ULONG	nRadioInterface[MAX_SS];

				if (!m_pFnGetSignalStrengths(&nArraySize, &nSignalStrength[0], &nRadioInterface[0]))
				{
					g_This->PostMessage(WM_USER_NOTIFY_SIGNAL_STRENGTH, (WPARAM)nSignalStrength[0], (LPARAM)nRadioInterface[0]);
				}

				// Get initial device state information
				GetDeviceState();

				// Enable user editable controls
				EnableControls();

			}
			else
			{
				// Device not found
				GetDlgItem(IDC_BTN_Connect)->EnableWindow(FALSE);
				EnableControls(FALSE);
				m_szStatus = L"Searching for device";
				UpdateData(FALSE);
			}
			break;
		default:
			break;
	}

	CDialog::OnTimer(nIDEvent);
}

// Update signal strength information
LRESULT CSimpleCMDlg::OnNotifSignalStrength(WPARAM wParam, LPARAM lParam)
{
	CString szRadio = L"Unknown";
	szRadio.Format(L"Unknown %d", lParam);
	switch (lParam)
	{
		case 0:
			szRadio = L"No service";
			break;
		case 1:
			szRadio = L"CDMA 1xRTT";
			break;
		case 2:
			szRadio = L"CDMA 1xEV-DO";
			break;
		case 3:
			szRadio = L"AMPS";
			break;
		case 4:
			szRadio = L"GSM";
			break;
		case 5:
			szRadio = L"UMTS";
			break;
		case 8:
			szRadio = L"LTE";
			break;
		default:
			break;
	}

	m_szRssi.Format(L"%d, %s", wParam, szRadio);

	GetNetworkInfo();

	return 0;
}

// Get network information
void CSimpleCMDlg::GetNetworkInfo()
{
	// Update network info
	const BYTE MAX_RInt = 0x10;
	const BYTE MAX_Name = 0xFF;

	ULONG   RegistrationState; 
	ULONG   CSDomain ; 
	ULONG   PSDomain; 
	ULONG   RAN; 
	BYTE    RadioIfacesSize = MAX_RInt; 
	BYTE    RadioIfaces[MAX_RInt]; 
	ULONG   Roaming; 
	WORD    MCC; 
	WORD    MNC;
	BYTE	NameSize = MAX_Name;
	CHAR    Name[MAX_Name] = "";

	if (!m_pFnGetServingNetwork(
			&RegistrationState, 
			&CSDomain, 
			&PSDomain, 
			&RAN, 
			&RadioIfacesSize, 
			RadioIfaces, 
			&Roaming, 
			&MCC, 
			&MNC, 
			NameSize, 
			Name)
		)
	{
		switch (RegistrationState)
		{
		case 1:
			// registered
			m_szCarrier = CA2W(Name);
			break;
		case 3:
			m_szCarrier = L"Registration denied";
			break;
		default:
			m_szCarrier = L"Not registered";
			break; 
		}
	}

	UpdateData(FALSE);
}

// Get device state information
void CSimpleCMDlg::GetDeviceState()
{
	ULONG nRetry, nBlockingRetry;
	ULONG nPinStatus = 0;	// 0 = not initialized
							// 1 = PIN enabled, not verified
							// 2 = PIN enabled, verified
							// 3 = PIN disabled,
							// 4 = PIN blocked
							// 5 = PIN permanently blocked
	ULONG nState = 0;		// 1 = Disconnected
							// 2 = Connected
							// 3 = Suspended (not supported)
							// 4 = Authenticating

	// Check PIN status
	if (!m_pFnUIMGetPINStatus(1, &nPinStatus, &nRetry, &nBlockingRetry))
	{
		// display error cases to the user
		switch (nPinStatus)
		{
		case 1:
			m_szStatus.Format(L"PIN enabled, not verified");
			UpdateData(FALSE);
			return;
		case 4:
		case 5:
			m_szStatus.Format(L"PIN blocked");
			UpdateData(FALSE);
			return;
		default:
			break;
		}
	}

	// Check data session state
	if (!m_pFnGetSessionState(&nState))
	{
		// Update screen
		OnNotifSessionState(nState, NULL);
	}
	else
	{
		m_szStatus.Format(L"Failed to get initial session state");
		UpdateData(FALSE);
	}
}

// Update session state information
LRESULT CSimpleCMDlg::OnNotifSessionState(WPARAM wParam, LPARAM lParam)
{
	switch (wParam)
	{
		case 1:	// Disconnected
			m_szStatus.Format(L"Disconnected from network: %d", lParam);
			if(lParam == 0x01)//Resume from Suspend
			{
				// Regard Suspend/Resume as device unplug/plug
				// Update screen
				m_szStatus.Format(L"Device unavailable: %d", wParam);
				m_szCarrier = L"";
				m_szRssi = L"";

				// Manual data sessions can not survive a device removal cycle, clear session ID info.
				m_nSessionId = 0;

				// Shutdown API
				m_pFnDisconnect();

				// disable user editable controls
				EnableControls(FALSE);

				// disable connect/disconnect button
				GetDlgItem(IDC_BTN_Connect)->EnableWindow(FALSE);

				// start timer to search for available device
				SetTimer(TIMER_DeviceSearch, 1000, NULL);
			}
			else 
			{
				GetDlgItem(IDC_BTN_Connect)->EnableWindow(TRUE);
				GetDlgItem(IDC_BTN_Connect)->SetWindowText(L"Connect");
				EnableControls();
			}
			break;
		case 2: // Connected
			if (m_nSessionId !=0)
			{
				// connection initiated by this application
				m_szStatus = L"Connected to network";
				GetDlgItem(IDC_BTN_Connect)->EnableWindow(TRUE);
			}
			else
			{
				// connection initiated by another application
				m_szStatus = L"Externally connected to network";
				GetDlgItem(IDC_BTN_Connect)->EnableWindow(FALSE);
			}
			GetDlgItem(IDC_BTN_Connect)->SetWindowText(L"Disconnect");
			break;
		case 4:	// Authenticating
			m_szStatus = L"Authenticating";
			GetDlgItem(IDC_BTN_Connect)->EnableWindow(FALSE);
			break;
		default:
			break;
	}

	UpdateData(FALSE);
	return 0;
}

LRESULT CSimpleCMDlg::OnNotifDeviceRemoval(WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);

	// Update screen
	m_szStatus.Format(L"Device unavailable: %d", wParam);
	m_szCarrier = L"";
	m_szRssi = L"";

	// Manual data sessions can not survive a device removal cycle, clear session ID info.
	m_nSessionId = 0;

	// Shutdown API
	m_pFnDisconnect();

	// disable user editable controls
	EnableControls(FALSE);

	// disable connect/disconnect button
	GetDlgItem(IDC_BTN_Connect)->EnableWindow(FALSE);

	// start timer to search for available device
	SetTimer(TIMER_DeviceSearch, 1000, NULL);

	UpdateData(FALSE);	// update screen

	return 0;
}

void CSimpleCMDlg::OnBnClickedBtnConnect()
{
	// Was a connection previously created?
	if (!m_nSessionId)
	{
		// connection was not previously created; create connection
		UpdateData();		// save user input

		ULONG nTech = 0x00000001;	// UMTS
		ULONG nAuth = 0x00000000;	// PAP and CHAP never performed

		// get specified technology type
		CString szTech = L"UMTS";

		m_cmbTech.GetLBText(m_cmbTech.GetCurSel(), szTech);

		if (0 == szTech.Compare(L"UMTS"))
			nTech = 0x00000001;
		else
			nTech = 0x00000002;

		// initiate data session
		ULONG nRet = m_pFnStartDataSession(
						&nTech, 
						NULL, 
						NULL, 
						NULL, 
						NULL, 
						CT2A(m_szApn), 
						NULL, 
						&nAuth, 
						CT2A(m_szUsername), 
						CT2A(m_szPassword), 
						&m_nSessionId);

		if (!nRet)
		{
			// request accepted, wait for session status indication
			m_szStatus.Format(L"Connecting");
			GetDlgItem(IDC_BTN_Connect)->EnableWindow(FALSE);
			EnableControls(FALSE);
		}
		else
		{
			// request not accepted
			m_szStatus.Format(L"Connection attempt failed: %d", nRet);
		}
	}
	else
	{
		// connection previously created; terminate connection
		ULONG nRet = m_pFnStopDataSession(m_nSessionId);

		if (!nRet)
		{
			// request accepted, wait for session status indication
			m_szStatus.Format(L"Disconnecting");
			GetDlgItem(IDC_BTN_Connect)->EnableWindow(FALSE);
			m_nSessionId = 0;	// clear session ID info
		}
		else
		{
			// request not accepted
			m_szStatus.Format(L"Disconnection attempt failed: %d", nRet);
		}
	}

	UpdateData(FALSE);	// update screen
}
