/*==========================================================================
 *
 *  Copyright (C) 1996-1999 Microsoft Corporation.  All Rights Reserved.
 *
 *  File:       modem.c
 *  Content:	Routines for modem I/O
 *  History:
 *   Date	By	Reason
 *   ====	==	======
 *  6/10/96	kipo	created it
 *  6/22/96	kipo	added support for EnumConnectionData(); dim "OK" button
 *					until user types at least one character.
 *	6/25/96	kipo	updated for DPADDRESS
 *	7/08/96 kipo	added support for new dialogs
 *  7/13/96	kipo	added GetModemAddress()
 *	7/16/96	kipo	changed address types to be GUIDs instead of 4CC
 *	8/10/96 kipo	added support for dialing location
 *	8/15/96 kipo	commented out support for dialing location
 *  9/04/96 dereks  fixed focus in dial/answer dialogs
 *  1/06/97 kipo	updated for objects
 *  2/11/97 kipo	pass player flags to GetAddress()
 *  2/18/97 kipo	allow multiple instances of service provider
 *	3/04/97 kipo	close com port handle when deallocating call; use string
 *					table for modem strings; updated debug output.
 *	3/17/97 kipo	added support for Unicode phone numbers
 *	3/24/97 kipo	added support for specifying which modem to use
 *  4/08/97 kipo	added support for separate modem and serial baud rates
 *  5/07/97 kipo	added support for modem choice list
 *  5/23/97 kipo	added support return status codes
 *  5/25/97 kipo	use DPERR_CONNECTING error to return status; set focus
 *					on cancel button in status window
 *  6/03/97 kipo	really make the cancel button work with return
 *  2/01/98 kipo	Display an error string in status dialog if line goes
 *					idle while dialing. Fixes bug #15251
 *  5/08/98 a-peterz #15251 - Better error state detection
 * 10/13/99	johnkan	#413516 - Mismatch between modem dialog selection and TAPI device ID
 ***************************************************************************/

#include <windows.h>
#include <windowsx.h>

#include "dplaysp.h"
#include "dputils.h"
#include "dial.h"
#include "dpf.h"
#include "resource.h"

// constants

enum {
	PHONENUMBERSIZE = 200,				// size of phone number string
	MODEMNAMESIZE = 200,				// size of modem name string
	TEMPSTRINGSIZE = 300,				// size of temporary strings
	MODEMTIMEOUT = 30 * 1000,			// milliseconds to wait for phone to connect
	MODEMSLEEPTIME = 50,				// milliseconds to sleep while waiting for modem
	TIMERINTERVAL = 100,
	MAXPHONENUMBERS = 10
};

// bit masks used to select connection actions
enum {
	DIALCALL		= (0 << 0),			// make a call
	ANSWERCALL		= (1 << 0),			// answer a call

	NOSETTINGS		= (0 << 1),			// no phone settings are set
	HAVESETTINGS	= (1 << 1),			// phone settings are set

	STATUSDIALOG	= (0 << 2),			// show a connection status dialog
	RETURNSTATUS	= (1 << 2)			// return status to app
};

#define MRU_SP_KEY			L"Modem Connection For DirectPlay"
#define MRU_NUMBER_KEY		L"Phone Number"

// structures

// modem object
typedef struct {
	DPCOMPORT	comPort;				// base object globals
	LPDPDIAL	lpDial;					// dialing globals
	BOOL		bHaveSettings;			// set to TRUE if we have settings
	BOOL		bAnswering;				// set to TRUE if we are answering
	DWORD		dwDeviceID;				// device id to use
	DWORD		dwLocation;				// location to use
	TCHAR		szPhoneNumber[PHONENUMBERSIZE];	// phone number to use
} DPMODEM, *LPDPMODEM;

// globals

// this is defined in dllmain.c
extern HINSTANCE		ghInstance;

// this is defined in dpserial.c
extern GUID				DPMODEM_GUID;

// prototypes

static HRESULT			DisposeModem(LPDPCOMPORT baseObject);
static HRESULT			ConnectModem(LPDPCOMPORT baseObject, BOOL bWaitForConnection, BOOL bReturnStatus);
static HRESULT			DisconnectModem(LPDPCOMPORT baseObject);
static HRESULT			GetModemBaudRate(LPDPCOMPORT baseObject, LPDWORD lpdwBaudRate);
static HRESULT			GetModemAddress(LPDPCOMPORT baseObject, DWORD dwPlayerFlags,
										LPVOID lpAddress, LPDWORD lpdwAddressSize);

static BOOL FAR PASCAL	EnumAddressData(REFGUID lpguidDataType, DWORD dwDataSize,
										LPCVOID lpData, LPVOID lpContext);
static HRESULT			GetModemAddressChoices(LPDPCOMPORT baseObject,
								LPVOID lpAddress, LPDWORD lpdwAddressSize);
static BOOL FAR PASCAL	EnumMRUPhoneNumbers(LPCVOID lpData, DWORD dwDataSize, LPVOID lpContext);
static void				UpdateButtons(HWND hWnd);

BOOL					DoDialSetup(HINSTANCE hInstance, HWND hWndParent, LPDPMODEM globals);
BOOL					DoDial(HINSTANCE hInstance, HWND hWndParent, LPDPMODEM globals);
BOOL					DoAnswerSetup(HINSTANCE hInstance, HWND hWndParent, LPDPMODEM globals);
BOOL					DoAnswer(HINSTANCE hInstance, HWND hWndParent, LPDPMODEM globals);
HRESULT					DoDialStatus(LPDPMODEM globals);
HRESULT					DoAnswerStatus(LPDPMODEM globals);

/*
 * NewModem
 *
 * Create new modem object. Open TAPI and verify there are lines available.
 */

HRESULT NewModem(LPVOID lpConnectionData, DWORD dwConnectionDataSize,
				 LPDIRECTPLAYSP lpDPlay, LPREADROUTINE lpReadRoutine,
				 LPDPCOMPORT *storage)
{
	LPDPCOMPORT baseObject;
	LPDPMODEM	globals;
	LINERESULT	lResult;
	HRESULT		hr;

	// create base object with enough space for our globals
	hr = NewComPort(sizeof(DPMODEM), lpDPlay, lpReadRoutine, &baseObject);
	if FAILED(hr)
		return (hr);

	// fill in methods we implement
	baseObject->Dispose = DisposeModem;
	baseObject->Connect = ConnectModem;
	baseObject->Disconnect = DisconnectModem;
	baseObject->GetBaudRate = GetModemBaudRate;
	baseObject->GetAddress = GetModemAddress;
	baseObject->GetAddressChoices = GetModemAddressChoices;

	globals = (LPDPMODEM) baseObject;

	// initialize TAPI
	lResult = dialInitialize(ghInstance, TEXT("TapiSP"), (LPDPCOMPORT) globals, &globals->lpDial);
	if (lResult)
	{
		hr = DPERR_UNAVAILABLE;
		goto Failure;
	}

	// check for valid connection data
	if (lpConnectionData)
	{
		baseObject->lpDPlay->lpVtbl->EnumAddress(baseObject->lpDPlay, EnumAddressData,
									lpConnectionData, dwConnectionDataSize,
									globals);
	}

	// return object pointer
	*storage = baseObject;

	return (DP_OK);

Failure:
	DisposeModem(baseObject);

	return (hr);
}

/*
 * EnumConnectionData
 *
 * Search for valid connection data
 */

static BOOL FAR PASCAL EnumAddressData(REFGUID lpguidDataType, DWORD dwDataSize,
							LPCVOID lpData, LPVOID lpContext)
{
	LPDPMODEM	globals = (LPDPMODEM) lpContext;
	CHAR		szModemName[MODEMNAMESIZE];

	// this is an ANSI phone number
	if ((IsEqualGUID(lpguidDataType, &DPAID_Phone)) &&
		(dwDataSize) )
	{
		// make sure there is room (for terminating null too)
		if (dwDataSize > (PHONENUMBERSIZE - 1))
			dwDataSize = (PHONENUMBERSIZE - 1);
		CopyMemory(globals->szPhoneNumber, lpData, dwDataSize);

		globals->bHaveSettings = TRUE;		// we have a phone number
	}

	// this is an UNICODE phone number
	else if ((IsEqualGUID(lpguidDataType, &DPAID_PhoneW)) &&
			 (dwDataSize) )
	{
		if (WideToAnsi(globals->szPhoneNumber, (LPWSTR) lpData, PHONENUMBERSIZE))
			globals->bHaveSettings = TRUE;	// we have a phone number
	}

	// this is an ANSI modem name
	else if ((IsEqualGUID(lpguidDataType, &DPAID_Modem)) &&
			 (dwDataSize) )
	{
		// search modem list for this name
		if (dialGetDeviceIDFromName(globals->lpDial, lpData, &globals->dwDeviceID) == SUCCESS)
			globals->bHaveSettings = TRUE;	// can answer the phone
	}

	// this is a UNICODE modem name
	else if ((IsEqualGUID(lpguidDataType, &DPAID_ModemW)) &&
			 (dwDataSize) )
	{
		// search modem list for this name
		if (WideToAnsi(szModemName, (LPWSTR) lpData, MODEMNAMESIZE))
		{
			if (dialGetDeviceIDFromName(globals->lpDial, szModemName, &globals->dwDeviceID) == SUCCESS)
				globals->bHaveSettings = TRUE;	// we have a phone number
		}
	}

	return (TRUE);
}

/*
 * DisposeModem
 *
 * Dispose modem object.
 */

static HRESULT DisposeModem(LPDPCOMPORT baseObject)
{
	LPDPMODEM	globals = (LPDPMODEM) baseObject;
	LPDPDIAL	lpDial = globals->lpDial;
	LINERESULT	lResult;

	// shut down modem
	if (lpDial)
		lResult = dialShutdown(lpDial);

	// free object
	GlobalFreePtr((HGLOBAL) baseObject);

	return (DP_OK);
}

/*
 * ConnectModem
 *
 * Dial number based on user settings.
 */

static HRESULT ConnectModem(LPDPCOMPORT baseObject,
							BOOL bWaitForConnection, BOOL bReturnStatus)
{
	LPDPMODEM	globals = (LPDPMODEM) baseObject;
	LPDPDIAL	lpDial = globals->lpDial;
	DWORD		dwFeatures;
	BOOL		bResult;
	HRESULT		hr;

	// dial object has not been created?
	if (lpDial == NULL)
		return (DPERR_INVALIDPARAM);

	// are we already connected?
	if (dialIsConnected(lpDial))
		return (DP_OK);

	// remember if we are answering or not
	globals->bAnswering = bWaitForConnection;

	dwFeatures = 0;

	if (globals->bAnswering)
		dwFeatures |= ANSWERCALL;

	if (globals->bHaveSettings)
		dwFeatures |= HAVESETTINGS;

	if (bReturnStatus)
		dwFeatures |= RETURNSTATUS;

	hr = DP_OK;

	switch (dwFeatures)
	{
		case (STATUSDIALOG | NOSETTINGS   | DIALCALL):

			bResult = DoDialSetup(ghInstance, GetForegroundWindow(), globals);
			if (!bResult)
				goto FAILURE;

			globals->bHaveSettings = TRUE;
			break;

		case (STATUSDIALOG | NOSETTINGS   | ANSWERCALL):

			bResult = DoAnswerSetup(ghInstance, GetForegroundWindow(), globals);
			if (!bResult)
				goto FAILURE;

			globals->bHaveSettings = TRUE;
			break;

		case (STATUSDIALOG | HAVESETTINGS | DIALCALL):

			bResult = DoDial(ghInstance, GetForegroundWindow(), globals);
			if (!bResult)
				goto FAILURE;
			break;

		case (STATUSDIALOG | HAVESETTINGS | ANSWERCALL):

			bResult = DoAnswer(ghInstance, GetForegroundWindow(), globals);
			if (!bResult)
				goto FAILURE;
			break;

		case (RETURNSTATUS   | NOSETTINGS   | DIALCALL):
		case (RETURNSTATUS   | NOSETTINGS   | ANSWERCALL):

			DPF(0, "Invalid flags - no phone number or modem specified");
			hr = DPERR_INVALIDPARAM;
			break;

		case (RETURNSTATUS   | HAVESETTINGS | DIALCALL):

			hr = DoDialStatus(globals);
			break;

		case (RETURNSTATUS   | HAVESETTINGS | ANSWERCALL):

			hr = DoAnswerStatus(globals);
			break;
	}

	return (hr);

FAILURE:
	DisconnectModem(baseObject);

	return (DPERR_USERCANCEL);
}

/*
 * DisconnectModem
 *
 * Hang up any call in progress.
 */

static HRESULT DisconnectModem(LPDPCOMPORT baseObject)
{
	LPDPMODEM	globals = (LPDPMODEM) baseObject;
	LPDPDIAL	lpDial = globals->lpDial;

	// dial object has not been created?
	if (lpDial == NULL)
		return (DPERR_INVALIDPARAM);

	// disconnect the call
	dialDropCall(lpDial);
	dialDeallocCall(lpDial);
	dialLineClose(lpDial);

	return (DP_OK);
}

/*
 * GetModemAddress
 *
 * Return current modem address if available.
 */

static HRESULT GetModemAddress(LPDPCOMPORT baseObject, DWORD dwPlayerFlags,
							   LPVOID lpAddress, LPDWORD lpdwAddressSize)
{
	LPDPMODEM					globals = (LPDPMODEM) baseObject;
	LPDPDIAL					lpDial = globals->lpDial;
	WCHAR						szPhoneNumberW[PHONENUMBERSIZE];
	DPCOMPOUNDADDRESSELEMENT	addressElements[3];
	HRESULT						hr;

	// no settings?
	if (!globals->bHaveSettings)
		return (DPERR_UNAVAILABLE);

	// dial object has not been created?
	if (lpDial == NULL)
		return (DPERR_UNAVAILABLE);

	// not connected?
	if (!dialIsConnected(lpDial))
		return (DPERR_UNAVAILABLE);

	// if we answered there is no way for us to know a phone number
	if (globals->bAnswering)
		return (DPERR_UNAVAILABLE);

	// we can't know the phone number of local players, only remote players
	if (dwPlayerFlags & DPLAYI_PLAYER_PLAYERLOCAL)
		return (DPERR_UNAVAILABLE);

	// get UNICODE version of phone number
	if (!AnsiToWide(szPhoneNumberW, globals->szPhoneNumber, PHONENUMBERSIZE))
		return (DPERR_GENERIC);

	// service provider chunk
	addressElements[0].guidDataType = DPAID_ServiceProvider;
	addressElements[0].dwDataSize = sizeof(GUID);
	addressElements[0].lpData = &DPMODEM_GUID;

	// ANSI phone number
	addressElements[1].guidDataType = DPAID_Phone;
	addressElements[1].dwDataSize = lstrlen(globals->szPhoneNumber) + 1;
	addressElements[1].lpData = globals->szPhoneNumber;

	// UNICODE phone number
	addressElements[2].guidDataType = DPAID_PhoneW;
	addressElements[2].dwDataSize = (lstrlen(globals->szPhoneNumber) + 1) * sizeof(WCHAR);
	addressElements[2].lpData = szPhoneNumberW;

	// create the address
	hr = baseObject->lpDPlay->lpVtbl->CreateCompoundAddress(baseObject->lpDPlay,
						addressElements, 3,
						lpAddress, lpdwAddressSize);
	return (hr);
}

/*
 * GetModemAddressChoices
 *
 * Return modem address choices
 */

static HRESULT GetModemAddressChoices(LPDPCOMPORT baseObject,
					LPVOID lpAddress, LPDWORD lpdwAddressSize)
{
	LPDPMODEM					globals = (LPDPMODEM) baseObject;
	LPDPDIAL					lpDial = globals->lpDial;
	DPCOMPOUNDADDRESSELEMENT	addressElements[3];
	LINERESULT					lResult;
	HRESULT						hr;

	// dial object has not been created?
	if (lpDial == NULL)
		return (DPERR_UNAVAILABLE);

	ZeroMemory(addressElements, sizeof(addressElements));

	// service provider chunk
	addressElements[0].guidDataType = DPAID_ServiceProvider;
	addressElements[0].dwDataSize = sizeof(GUID);
	addressElements[0].lpData = &DPMODEM_GUID;

	// get ANSI modem name list
	addressElements[1].guidDataType = DPAID_Modem;
	lResult = dialGetModemList(lpDial, TRUE,
					&addressElements[1].lpData,
					&addressElements[1].dwDataSize);
	if (lResult)
	{
		hr = DPERR_OUTOFMEMORY;
		goto Failure;
	}

	// Unicode modem name list
	addressElements[2].guidDataType = DPAID_ModemW;
	lResult = dialGetModemList(lpDial, FALSE,
					&addressElements[2].lpData,
					&addressElements[2].dwDataSize);
	if (lResult)
	{
		hr = DPERR_OUTOFMEMORY;
		goto Failure;
	}

	// create the address
	hr = baseObject->lpDPlay->lpVtbl->CreateCompoundAddress(baseObject->lpDPlay,
						addressElements, 3,
						lpAddress, lpdwAddressSize);

Failure:
	if (addressElements[1].lpData)
		GlobalFreePtr(addressElements[1].lpData);
	if (addressElements[2].lpData)
		GlobalFreePtr(addressElements[2].lpData);

	return (hr);

}

/*
 * GetModemBaudRate
 *
 * Get baud rate of modem connnection.
 */

static HRESULT GetModemBaudRate(LPDPCOMPORT baseObject, LPDWORD lpdwBaudRate)
{
	LPDPMODEM	globals = (LPDPMODEM) baseObject;
	LPDPDIAL	lpDial = globals->lpDial;
	LINERESULT	lResult;

	lResult = dialGetBaudRate(lpDial, lpdwBaudRate);

	if (lResult == SUCCESS)
		return (DP_OK);
	else
		return (DPERR_UNAVAILABLE);
}

// Local prototypes
INT_PTR CALLBACK DialSetupWndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK AnswerSetupWndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK ModemStatusWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

void ChangeDialingProperties(HWND hWnd, LPDPDIAL lpDial);
void ConfigureModem(HWND hWnd);
void CenterWindow(HWND, HWND);


// ---------------------------------------------------------------------------
// DoDialSetup
// ---------------------------------------------------------------------------
// Description:             Gets modem setup information from the user.
// Arguments:
//  HINSTANCE               [in] Instance handle to load resources from.
//  HWND                    [in] Parent window handle.
//  LPDPMODEM				[in] modem globals
// Returns:
//  BOOL                    TRUE on success.
BOOL DoDialSetup(HINSTANCE hInstance, HWND hWndParent, LPDPMODEM globals)
{
	INT_PTR	iResult;

    iResult = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MODEM_DIAL), hWndParent, DialSetupWndProc, (LPARAM) globals);
	return (iResult > 0);
}


// ---------------------------------------------------------------------------
// DialSetupWndProc
// ---------------------------------------------------------------------------
// Description:             Message callback function for dial setup dialog.
// Arguments:
//  HWND                    [in] Dialog window handle.
//  UINT                    [in] Window message identifier.
//  WPARAM                  [in] Depends on message.
//  LPARAM                  [in] Depends on message.
// Returns:
//  BOOL                    TRUE if message was processed internally.
INT_PTR CALLBACK DialSetupWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	LPDPMODEM	globals = (LPDPMODEM) GetWindowLongPtr(hWnd, DWLP_USER);

    switch(uMsg)
    {
        case WM_INITDIALOG:
			// modem info pointer passed in lParam
			globals = (LPDPMODEM) lParam;

             // save the globals with the window
			SetWindowLongPtr(hWnd, DWLP_USER, (LONG_PTR)globals);

			// Center over the parent window
            CenterWindow(hWnd, GetParent(hWnd));

/*			gDPlay->lpVtbl->EnumMRUEntries(gDPlay,
								MRU_SP_KEY, MRU_NUMBER_KEY,
								EnumMRUPhoneNumbers, (LPVOID) hWnd);
*/
			if (lstrlen(globals->szPhoneNumber))
				SetDlgItemText(hWnd, IDC_NUMBER, globals->szPhoneNumber);
/*			else
				SendDlgItemMessage(hWnd,
									IDC_NUMBER,
									CB_SETCURSEL,
									(WPARAM) 0,
									(LPARAM) 0);
*/
/*			SendDlgItemMessage(hWnd,
							   IDC_NUMBER,
							   CB_SETCURSEL,
							   (WPARAM) 0,
							   (LPARAM) 0);
*/
            // initialize the modem selection combo box
			dialFillModemComboBox(globals->lpDial, hWnd, IDC_MODEM, globals->dwDeviceID);

			// initialize location combo box
//			dialFillLocationComboBox(lpModemInfo->lpDial, hWnd, IDC_DIALINGFROM, gModemSettings.dwLocation);
			UpdateButtons(hWnd);

            // Set focus so Derek won't have a cow
            SetFocus(GetDlgItem(hWnd, IDC_NUMBER));

            break;

        case WM_DESTROY:
            // Return failure
            EndDialog(hWnd, FALSE);

            break;

        case WM_COMMAND:
            switch(LOWORD(wParam))
            {
                case IDC_NUMBER:
					switch (HIWORD(wParam))
					{
					case EN_CHANGE:
//					case CBN_EDITCHANGE:
						UpdateButtons(hWnd);
						break;
					}
                    break;
/*
                case IDC_DIALPROPERTIES:

					ChangeDialingProperties(hWnd, lpModemInfo->lpDial);
					dialFillLocationComboBox(lpModemInfo->lpDial, hWnd, IDC_DIALINGFROM, gModemSettings.dwLocation);

                    break;
*/
                case IDC_CONFIGUREMODEM:

					ConfigureModem(hWnd);

                    break;

				case IDOK:
				{
					DWORD	dwModemSelection;


                    // Gather dialing info

					// Get phone number
					GetDlgItemText(hWnd, IDC_NUMBER, globals->szPhoneNumber, PHONENUMBERSIZE);

					//
					// get current modem selection and then get the assoicated
					// TAPI modem ID
					//
					dwModemSelection = (DWORD)SendDlgItemMessage(hWnd,
													IDC_MODEM,
													CB_GETCURSEL,
													(WPARAM) 0,
													(LPARAM) 0);
					DDASSERT( dwModemSelection != CB_ERR );

					globals->dwDeviceID = (DWORD)SendDlgItemMessage(hWnd,
													IDC_MODEM,
													CB_GETITEMDATA,
													(WPARAM) dwModemSelection,
													(LPARAM) 0);
					DDASSERT( globals->dwDeviceID != CB_ERR );

/*					if (lstrlen(gModemSettings.szPhoneNumber))
					{
						gDPlay->lpVtbl->AddMRUEntry(gDPlay,
											MRU_SP_KEY, MRU_NUMBER_KEY,
											gModemSettings.szPhoneNumber, lstrlen(gModemSettings.szPhoneNumber),
											MAXPHONENUMBERS);
					}
*/
                    // Dial...
					if (DoDial(ghInstance, hWnd, globals))
	                    EndDialog(hWnd, TRUE);

					break;
				}

                case IDCANCEL:
                    // Return failure
                    EndDialog(hWnd, FALSE);

                    break;
            }

            break;
    }

    // Allow for default processing
    return FALSE;
}

// ---------------------------------------------------------------------------
// DoDial
// ---------------------------------------------------------------------------
// Description:             Dials the modem
// Arguments:
//  HINSTANCE               [in] Instance handle to load resources from.
//  HWND                    [in] Parent window handle.
//  LPDPMODEM				[in] modem globals
// Returns:
//  BOOL                    TRUE on success.
BOOL DoDial(HINSTANCE hInstance, HWND hWndParent, LPDPMODEM globals)
{
	INT_PTR	iResult;

    iResult = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MODEM_STATUS), hWndParent, ModemStatusWndProc, (LPARAM) globals);
	return (iResult > 0);
}

// ---------------------------------------------------------------------------
// DoAnswerSetup
// ---------------------------------------------------------------------------
// Description:             Gets modem setup information from the user.
// Arguments:
//  HINSTANCE               [in] Instance handle to load resources from.
//  HWND                    [in] Parent window handle.
//  LPDPMODEM				[in] modem globals
// Returns:
//  BOOL                    TRUE on success.
BOOL DoAnswerSetup(HINSTANCE hInstance, HWND hWndParent, LPDPMODEM globals)
{
	INT_PTR	iResult;

	iResult = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MODEM_ANSWER), hWndParent, AnswerSetupWndProc, (LPARAM) globals);
	return (iResult > 0);
}

// ---------------------------------------------------------------------------
// AnswerSetupWndProc
// ---------------------------------------------------------------------------
// Description:             Message callback function for modem setup dialog.
// Arguments:
//  HWND                    [in] Dialog window handle.
//  UINT                    [in] Window message identifier.
//  WPARAM                  [in] Depends on message.
//  LPARAM                  [in] Depends on message.
// Returns:
//  BOOL                    TRUE if message was processed internally.
INT_PTR CALLBACK AnswerSetupWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	LPDPMODEM	globals = (LPDPMODEM) GetWindowLongPtr(hWnd, DWLP_USER);

    switch(uMsg)
    {
		case WM_INITDIALOG:
			// modem info pointer passed in lParam
			globals = (LPDPMODEM) lParam;

             // save the globals with the window
			SetWindowLongPtr(hWnd, DWLP_USER, (LONG_PTR) globals);

            // Center over the parent window
            CenterWindow(hWnd, GetParent(hWnd));

            // Initialize the modem selection combo box
			dialFillModemComboBox(globals->lpDial, hWnd, IDC_MODEM, globals->dwDeviceID);

            // Set focus so Derek won't have a cow
            SetFocus(GetDlgItem(hWnd, IDC_MODEM));

            break;

        case WM_DESTROY:
            // Return failure
            EndDialog(hWnd, FALSE);

            break;

        case WM_COMMAND:
            switch(LOWORD(wParam))
            {
                case IDC_CONFIGUREMODEM:
					ConfigureModem(hWnd);

                    break;

				case IDOK:
				{
					DWORD	dwModemSelection;


					//
					// Get the current selection and then the associated TAPI
					// modem ID.
					//
					dwModemSelection = (DWORD)SendDlgItemMessage(hWnd,
													IDC_MODEM,
													CB_GETCURSEL,
													(WPARAM) 0,
													(LPARAM) 0);

					globals->dwDeviceID = (DWORD)SendDlgItemMessage(hWnd,
													IDC_MODEM,
													CB_GETITEMDATA,
													(WPARAM) dwModemSelection,
													(LPARAM) 0);

                    // Answer...
					if (DoAnswer(ghInstance, hWnd, globals))
	                    EndDialog(hWnd, TRUE);

                    break;
				}

                case IDCANCEL:
                    // Return failure
                    EndDialog(hWnd, FALSE);

                    break;
            }

            break;
    }

    // Allow for default processing
    return FALSE;
}


// ---------------------------------------------------------------------------
// DoAnswer
// ---------------------------------------------------------------------------
// Description:             Answers the modem
// Arguments:
//  HINSTANCE               [in] Instance handle to load resources from.
//  HWND                    [in] Parent window handle.
//  LPDPMODEM				[in] modem globals
// Returns:
//  BOOL                    TRUE on success.
BOOL DoAnswer(HINSTANCE hInstance, HWND hWndParent, LPDPMODEM globals)
{
	INT_PTR	iResult;

    iResult = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MODEM_STATUS), hWndParent, ModemStatusWndProc, (LPARAM) globals);
	return (iResult > 0);
}

// ---------------------------------------------------------------------------
// ModemStatusWndProc
// ---------------------------------------------------------------------------
// Description:             Message callback function for dial setup dialog.
// Arguments:
//  HWND                    [in] Dialog window handle.
//  UINT                    [in] Window message identifier.
//  WPARAM                  [in] Depends on message.
//  LPARAM                  [in] Depends on message.
// Returns:
//  BOOL                    TRUE if message was processed internally.
INT_PTR CALLBACK ModemStatusWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	LPDPMODEM		globals = (LPDPMODEM) GetWindowLongPtr(hWnd, DWLP_USER);
	static UINT_PTR	uTimer = 0; /* timer identifier */
	LINERESULT		lResult;
	TCHAR			szStr[TEMPSTRINGSIZE];	// temp string
	TCHAR			szTableStr[TEMPSTRINGSIZE];	// temp string

    switch(uMsg)
    {
        case WM_INITDIALOG:
			// modem info pointer passed in lParam
			globals = (LPDPMODEM) lParam;

             // save the globals with the window
			SetWindowLongPtr(hWnd, DWLP_USER, (LONG_PTR) globals);

            // Center over the parent window
            CenterWindow(hWnd, GetParent(hWnd));

			// Set focus so Allen won't have a cow
            SetFocus(GetDlgItem(hWnd, IDCANCEL));

			// make sure line is closed
			if (globals->lpDial->hLine)
				dialLineClose(globals->lpDial);

			// open a line
			lResult = dialLineOpen(globals->lpDial, globals->dwDeviceID);
			if (lResult)
			{
				// line would not open, so show an error
				if (LoadString(ghInstance, IDS_COULDNOTOPENLINE, szStr, sizeof(szStr)))
					SetDlgItemText(hWnd, IDC_STATUS, szStr);
				break;
			}

			if (globals->bAnswering)
			{
				// already have settings, so just exit
				if (globals->bHaveSettings)
					EndDialog(hWnd, TRUE);

				// display "please wait" string
				if (LoadString(ghInstance, IDS_WAITINGFORCONNECTION, szStr, sizeof(szStr)))
					SetDlgItemText(hWnd, IDC_STATUS, szStr);
			}
			else
			{
				if (LoadString(ghInstance, IDS_DIALING, szTableStr, sizeof(szTableStr)))
				{
					wsprintf(szStr, szTableStr, globals->szPhoneNumber);
					SetDlgItemText(hWnd, IDC_STATUS, szStr);
				}

				// dial phone number
				lResult = dialMakeCall(globals->lpDial, globals->szPhoneNumber);
				if (lResult < 0)
				{
					// could not dial call, so show an error
					if (LoadString(ghInstance, IDS_COULDNOTDIAL, szStr, sizeof(szStr)))
						SetDlgItemText(hWnd, IDC_STATUS, szStr);
					break;
				}

				// reset to zero so that we don't get a false no connection below
				globals->lpDial->dwCallState = 0;
			}

			uTimer = SetTimer(hWnd, 1, TIMERINTERVAL, NULL);
			break;

		case WM_TIMER:

			if (dialIsConnected(globals->lpDial))
			{
				if (uTimer)
				{
					KillTimer(hWnd, uTimer);
					uTimer = 0;
				}

				// give the other side some time to set up
				Sleep(500);

	            EndDialog(hWnd, TRUE);
			}

			// see if line has failed
			else if (globals->lpDial->dwCallError != CALL_OK)
			{
				// show an error
				if (LoadString(ghInstance,
							   globals->bAnswering ? IDS_COULDNOTOPENLINE : IDS_COULDNOTDIAL,
							   szStr, sizeof(szStr)))
					SetDlgItemText(hWnd, IDC_STATUS, szStr);
			}
			break;

        case WM_DESTROY:
			if (uTimer)
			{
				KillTimer(hWnd, uTimer);
				uTimer = 0;
			}
            break;

        case WM_COMMAND:
            switch(LOWORD(wParam))
            {
                case IDOK:
                case IDCANCEL:
					// disconnect the call
					dialDropCall(globals->lpDial);
					dialDeallocCall(globals->lpDial);
					dialLineClose(globals->lpDial);

					// Return failure
					EndDialog(hWnd, FALSE);
                    break;
            }
            break;
    }

    // Allow for default processing
    return FALSE;
}

HRESULT DoDialStatus(LPDPMODEM globals)
{
	LINERESULT		lResult;


	// see if line had an error or went idle
	if ((globals->lpDial->dwCallError != CALL_OK) ||
		((globals->lpDial->hLine) &&
		 (globals->lpDial->dwCallState == LINECALLSTATE_IDLE)))
	{
		DPF(3, "DoDialStatus error recovery");
		// some errors don't close the line so we will
		if (globals->lpDial->hLine)
			dialLineClose(globals->lpDial);
		// reset the error state
		globals->lpDial->dwCallError = CALL_OK;
		return (DPERR_NOCONNECTION);
	}

	// line is not open
	if (!globals->lpDial->hLine)
	{
		lResult = dialLineOpen(globals->lpDial, globals->dwDeviceID);
		if (lResult)
			return (DPERR_NOCONNECTION);

		lResult = dialMakeCall(globals->lpDial, globals->szPhoneNumber);
		if (lResult < 0)
		{
			dialLineClose(globals->lpDial);
			return (DPERR_NOCONNECTION);
		}

		// reset to zero so that we don't get a false "no connection" before we dial
		globals->lpDial->dwCallState = 0;
	}

	// if we got here then call is in progress
	return (DPERR_CONNECTING);
}

HRESULT DoAnswerStatus(LPDPMODEM globals)
{
	LINERESULT		lResult;

	// see if line had an error
	if (globals->lpDial->dwCallError != CALL_OK)
	{
		// some errors don't close the line so we will
		if (globals->lpDial->hLine)
			dialLineClose(globals->lpDial);
		// reset the error state
		globals->lpDial->dwCallError = CALL_OK;
		return (DPERR_NOCONNECTION);
	}

	// open a line
	if (!globals->lpDial->hLine)
	{
		lResult = dialLineOpen(globals->lpDial, globals->dwDeviceID);
		if (lResult)
			return (DPERR_NOCONNECTION);
	}

	// if we got here then we are ready to answer a call
	return (DP_OK);
}

static BOOL FAR PASCAL EnumMRUPhoneNumbers(LPCVOID lpData, DWORD dwDataSize, LPVOID lpContext)
{
	HWND	hWnd = (HWND) lpContext;

	SendDlgItemMessage(hWnd,
						IDC_NUMBER,
						CB_ADDSTRING,
						(WPARAM) 0,
						(LPARAM) lpData);
	return (TRUE);
}

static void UpdateButtons(HWND hWnd)
{
	LONG_PTR	len;

	// see how much text has been typed into number edit
    len = SendDlgItemMessage(hWnd,
							IDC_NUMBER,
							WM_GETTEXTLENGTH,
							(WPARAM) 0,
							(LPARAM) 0);

	// only enable "Connect" button if text has been entered
	EnableWindow(GetDlgItem(hWnd, IDOK), (len == 0) ? FALSE : TRUE);
}

void ChangeDialingProperties(HWND hWnd, LPDPDIAL lpDial)
{
	TCHAR		szPhoneNumber[PHONENUMBERSIZE];
	DWORD		dwModemSelection;
	DWORD		dwDeviceID;
	LINERESULT	lResult;



	dwModemSelection = (DWORD)SendDlgItemMessage(hWnd,
								IDC_MODEM,
								CB_GETCURSEL,
								(WPARAM) 0,
								(LPARAM) 0);
	DDASSERT( dwModemSelection != CB_ERR );

	dwDeviceID = (DWORD)SendDlgItemMessage(hWnd,
								IDC_MODEM,
								CB_GETITEMDATA,
								(WPARAM) dwModemSelection,
								(LPARAM) 0);
	DDASSERT( dwDeviceID != CB_ERR );
	if (dwDeviceID == CB_ERR)
		return;

	GetDlgItemText(hWnd, IDC_NUMBER, szPhoneNumber, PHONENUMBERSIZE);

	lResult = dialTranslateDialog(lpDial, hWnd, dwDeviceID, szPhoneNumber);
}

void ConfigureModem(HWND hWnd)
{
	DWORD		dwDeviceID;
	DWORD		dwModemSelection;
	LINERESULT	lResult;


	//
	// get the current modem selection and then get the associated TAPI modem ID
	//
	dwModemSelection = (DWORD)SendDlgItemMessage(hWnd,
								IDC_MODEM,
								CB_GETCURSEL,
								(WPARAM) 0,
								(LPARAM) 0);
	DDASSERT( dwModemSelection != CB_ERR );

	dwDeviceID = (DWORD)SendDlgItemMessage(hWnd,
								IDC_MODEM,
								CB_GETITEMDATA,
								(WPARAM) dwModemSelection,
								(LPARAM) 0);
	DDASSERT( dwDeviceID != CB_ERR );
	if (dwDeviceID != CB_ERR)
		lResult = lineConfigDialog(dwDeviceID, hWnd, "comm/datamodem");
}

// ---------------------------------------------------------------------------
// CenterWidow
// ---------------------------------------------------------------------------
// Description:             Centers one window over another.
// Arguments:
//  HWND                    [in] Window handle.
//  HWND                    [in] Parent window handle.  NULL centers the
//                               window over the desktop.
// Returns:
//  void
void CenterWindow(HWND hWnd, HWND hWndParent)
{
    RECT                    rcWindow, rcParent;
    int                     x, y;

    // Get child window rect
    GetWindowRect(hWnd,  &rcWindow);

    // Get parent window rect
//    if(!hWndParent || !IsWindow(hWndParent))
    {
        hWndParent = GetDesktopWindow();
    }

    GetWindowRect(hWndParent, &rcParent);

    // Calculate XY coordinates
    x = ((rcParent.right - rcParent.left) - (rcWindow.right - rcWindow.left)) / 2;
    y = ((rcParent.bottom - rcParent.top) - (rcWindow.bottom - rcWindow.top)) / 2;

    // Center the window
    SetWindowPos(hWnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}