/*==========================================================================
 *
 *  Copyright (C) 1995-1997 Microsoft Corporation.  All Rights Reserved.
 *
 *  File:        dvretro.c
 *  Content:	 	Retrofit functions
 *  History:
 *
 *   Date		By		Reason
 *   ====		==		======
 *  08/12/99	rodtoll	created it
 *	08/19/99	rodtoll	Updated lobby launching to support retrofit
 *  08/20/99	rodtoll	Modified to respond to shutdown request and
 *						do proper lobby launch when in client mode.
 *	08/25/99	rodtoll	Updated to use new GUID based compression selection
 *  09/14/99	rodtoll	Updated for new Init call and SoundDeviceInfo usage.
 *  09/27(8)/99 rodtoll	Updated with new interface
 *  09/28/99	rodtoll	Updated for structure changes.
 *  09/29/99	rodtoll	Updated for async enum, fixed bug with lobby launch
 *  10/15/99	rodtoll	Massive code cleanup.  Split into multiple files
 *				rodtoll	Minor fix, event was being released twice
 *  10/20/99	rodtoll	Fixed name info for helper app
 *  11/02/99	rodtoll	Bug #116677 - Can't use lobby clients that don't hang around
 *  11/04/99	pnewson Bug #115297 - launch device test if required
 *  11/12/99	rodtoll	Added support for the new waveIN/waveOut flags and the 
 *					    echo suppression flag. (Can specify them on command-line)
 *  12/01/99	rodtoll	Bug #115783 - Regardless of device specified always adjusts volume
 *						for default.  Modified dpvhelp to allow user to select play/record
 *						devices by using /SS switch.
 *  12/07/99	rodtoll	Bug #122628 Make error messages silent when running in silent mode
 *				rodtoll	Bug #122979 Make invisible to end user
 *						Now app must be run with /A to show an interface.
 *						Also, unless /A or /L or /W are specified, the app will exit immediately.
 *						Command-line help removed.
 *						Auto-registration code removed for public version of app.
 *  02/15/2000	rodtoll	Bug #132715 Voice is not working after rejoining the session
 *  03/03/2000	rodtoll	Updated to handle alternative gamevoice build.   
 *  06/28/2000	rodtoll	Prefix Bug #38033
 *
 ***************************************************************************/

#include "dxvhelppch.h"


#define MAX_LOADSTRING 100

#define DPVHELP_WINDOWTITLE				_T("DirectPlay Voice Chat")
#define DPVHELP_WINDOWCLASS				_T("DPVHELP")

#define DPVHELP_PRIVATE_APPNAME					_T("DirectPlay Voice Chat (Retrofit)")
#define DPVHELP_PRIVATE_DESC					_T("DirectPlay Voice Chat (Retrofit)")
#define DPVHELP_PRIVATE_EXENAME					_T("dpvhelp.exe")
#define DPVHELP_PRIVATE_COMMANDLINE				_T("/L /S /SC03")
#define DPVHELP_PRIVATE_FLAGS					0x80000002

#undef DPF_MODNAME
#define DPF_MODNAME "InitializeRunTime"

void InitializeRunTime( PDXVHELP_RTINFO prtInfo )
{
	prtInfo->hReceiveEvent = NULL;
	prtInfo->hThreadDone = CreateEvent( NULL, TRUE, FALSE, NULL );
	prtInfo->hShutdown = CreateEvent( NULL, TRUE, FALSE, NULL );
	prtInfo->hGo = CreateEvent( NULL, FALSE, FALSE, NULL );
	prtInfo->hMainDialog = NULL;
	prtInfo->dpidLocalPlayer = 0xFFFFFFFF;
	prtInfo->lVolumeHeight = 0;
	prtInfo->hInst = NULL;
	prtInfo->hMainWnd = NULL;
	prtInfo->dwNumClients = 0;
	prtInfo->lpdpLobby = NULL;
	prtInfo->lpdvClient = NULL;
	prtInfo->lpdvServer = NULL;
	prtInfo->lpdpDirectPlay = NULL;
	prtInfo->hLobbyEvent = NULL;
}

#undef DPF_MODNAME
#define DPF_MODNAME "FreeRunTime"

void FreeRunTime( PDXVHELP_RTINFO prtInfo )
{
	CloseHandle( prtInfo->hThreadDone );
	CloseHandle( prtInfo->hShutdown );
	CloseHandle( prtInfo->hGo );
}

#undef DPF_MODNAME
#define DPF_MODNAME "SetDefaultParameters"

void SetDefaultParameters( PDXVHELP_PARAMETERS pParameters )
{
	pParameters->fHost = FALSE;
	pParameters->fLobbyLaunched = FALSE;
	pParameters->fSilent = TRUE;
	pParameters->dwSessionType = DVSESSIONTYPE_PEER;
	pParameters->guidCT = DPVCTGUID_DEFAULT;
	pParameters->lpszConnectAddress[0] = 0;
	pParameters->fAGC = TRUE;
	pParameters->fAdvancedUI = FALSE;
	pParameters->fWaitForSettings = FALSE;
	pParameters->lRecordVolume = DSBVOLUME_MAX;
	pParameters->fRegister = FALSE;
	pParameters->fUnRegister = FALSE;
	pParameters->fKill = FALSE;
	pParameters->fIgnoreLobbyDestroy = FALSE;
	pParameters->fAllowWaveOut = FALSE;
	pParameters->fForceWaveOut = FALSE;
	pParameters->fAllowWaveIn = FALSE;
	pParameters->fForceWaveIn = FALSE;
	pParameters->fEchoSuppression = FALSE;
	pParameters->fAutoSelectMic = TRUE;
	pParameters->guidPlaybackDevice = DSDEVID_DefaultVoicePlayback;
	pParameters->guidRecordDevice = DSDEVID_DefaultVoiceCapture;
	pParameters->fSelectCards = FALSE;
	pParameters->fStrictFocus = FALSE;
	pParameters->fDisableFocus = FALSE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "ProcessCommandLine"

BOOL ProcessCommandLine( PSTR pstrCommandLine, PDXVHELP_PARAMETERS pParameters )
{
	PSTR pNextToken = NULL;

	pNextToken = _tcstok( pstrCommandLine, _T(" ") );

	while( pNextToken != NULL )
	{
		if( _tcsicmp( pNextToken, _T("/A") ) == 0 ) 
		{
			pParameters->fAdvancedUI = TRUE;
			pParameters->fSilent = FALSE;			
		}
		else if( _tcsicmp( pNextToken, _T("/ES" ) ) == 0 )
		{
			pParameters->fEchoSuppression = TRUE;
		}
		else if( _tcsicmp( pNextToken, _T("/WOF") ) == 0 )
		{
			pParameters->fForceWaveOut = TRUE;
		}
		else if( _tcsicmp( pNextToken, _T("/NAS") ) == 0 )
		{
			pParameters->fAutoSelectMic = FALSE;
		}
		else if( _tcsicmp( pNextToken, _T("/WOA") ) == 0 )
		{
			pParameters->fAllowWaveOut = TRUE;
		}		
		else if( _tcsicmp( pNextToken, _T("/WIF") ) == 0 )
		{
			pParameters->fForceWaveIn = TRUE;
		}
		else if( _tcsicmp( pNextToken, _T("/WIA") ) == 0 )
		{
			pParameters->fAllowWaveIn = TRUE;
		}				
		else if( _tcsicmp( pNextToken, _T("/ES") ) == 0 )
		{
			pParameters->fEchoSuppression = TRUE;
		}
		else if( _tcsicmp( pNextToken, _T("/W") ) == 0 )
		{
			pParameters->fLobbyLaunched = TRUE;
			pParameters->fWaitForSettings = TRUE;
		}
		else if( _tcsicmp( pNextToken, _T("/L") ) == 0 )
		{
			pParameters->fLobbyLaunched = TRUE;
			pParameters->fWaitForSettings = FALSE;
		}
		else if( _tcsicmp( pNextToken, _T("/R") ) == 0 )
		{
			pParameters->fRegister = TRUE;
			return FALSE;
		}
		else if( _tcsicmp( pNextToken, _T("/U") ) == 0 )
		{
			pParameters->fUnRegister = TRUE;
			return FALSE;
		}
		else if( _tcsicmp( pNextToken, _T("/S") ) == 0 )
		{
			pParameters->fSilent = TRUE;
		}
		else if( _tcsicmp( pNextToken, _T("/K") ) == 0 )
		{
			pParameters->fKill = TRUE;
			return FALSE;
		}
		else if( _tcsicmp( pNextToken, _T("/I") ) == 0 )
		{
			pParameters->fIgnoreLobbyDestroy = TRUE;
		}
		else if( _tcsicmp( pNextToken, _T("/GSM") ) == 0 )
		{
			pParameters->guidCT = DPVCTGUID_GSM;
		}
		else if( _tcsicmp( pNextToken, _T("/SC03" ) ) == 0 )
		{
			pParameters->guidCT = DPVCTGUID_SC03;
		}
		else if( _tcsicmp( pNextToken, _T("/SC06" ) ) == 0 )
		{
			pParameters->guidCT = DPVCTGUID_SC06;		
		}		
		else if( _tcsicmp( pNextToken, _T("/VR12" ) ) == 0 )
		{
			pParameters->guidCT = DPVCTGUID_VR12;				
		}				
		else if( _tcsicmp( pNextToken, _T("/ADPCM" ) ) == 0 )
		{
			pParameters->guidCT = DPVCTGUID_ADPCM;						
		}	
		else if( _tcsicmp( pNextToken, _T("/NONE" ) ) == 0 )
		{
			pParameters->guidCT = DPVCTGUID_NONE;								
		}						
		else if( _tcsicmp( pNextToken, _T("/TRUE" ) ) == 0 )
		{
			pParameters->guidCT = DPVCTGUID_TRUESPEECH;										
		}						
		else if( _tcsicmp( pNextToken, _T("/SS") ) == 0 )
		{
			pParameters->fSelectCards = TRUE;
		}
		else if( _tcsicmp( pNextToken, _T("/FS") ) == 0 )
		{
			pParameters->fStrictFocus = TRUE;
		}
		else if( _tcsicmp( pNextToken, _T( "/FD" ) ) == 0 )
		{
			pParameters->fDisableFocus = TRUE;		
		}
		else
		{
			return FALSE;
		}
		
		pNextToken = _tcstok( NULL, _T(" ") );
	}

	if( !pParameters->fAdvancedUI && !pParameters->fLobbyLaunched )
	{
		DPFX(DPFPREP,  DVF_ERRORLEVEL, "Can only run in test mode and lobby mode" );
		return FALSE;
	}

	return TRUE;
}

#undef DPF_MODNAME
#define DPF_MODNAME "RegisterApplication"

void RegisterApplication()
{
	HRESULT hr;
	BOOL fFailed = FALSE;
	
	hr = DPVDX_DP_RegisterApplication( DPVHELP_PRIVATE_APPNAME, DPVHELP_PRIVATE_APPID, DPVHELP_PRIVATE_EXENAME, 
	                                   DPVHELP_PRIVATE_COMMANDLINE, DPVHELP_PRIVATE_DESC, DPVHELP_PRIVATE_FLAGS );

	if( FAILED( hr ) )
	{
		DPVDX_DPERRDisplay( hr, _T("Unable to register private application"), FALSE );
		MessageBox( NULL, _T("Registered Application"), DPVHELP_WINDOWTITLE, MB_OK );
	}

	if( !fFailed )
	{
		MessageBox( NULL, _T("Registered Application"), DPVHELP_WINDOWTITLE, MB_OK );
	}	
}

#undef DPF_MODNAME
#define DPF_MODNAME "UnRegisterApplication"

void UnRegisterApplication()
{
	HRESULT hr;
	BOOL fFailed = FALSE;

	hr = DPVDX_DP_UnRegisterApplication( DPVHELP_PRIVATE_APPID );

	if( FAILED( hr ) )
	{
		DPVDX_DPERRDisplay( hr, _T("Unable to un-register private application"), FALSE );
		fFailed = TRUE;
	}

	if( !fFailed )
	{
		MessageBox( NULL, _T("Un-Registered Application"), DPVHELP_WINDOWTITLE, MB_OK );
	}	
}

//
//  FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND	- process the application menu
//  WM_PAINT	- Paint the main window
//  WM_DESTROY	- post a quit message and return
//
//
#undef DPF_MODNAME
#define DPF_MODNAME "MainWindowProc"

LRESULT CALLBACK MainWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;
	TCHAR szHello[MAX_LOADSTRING];

	switch (message) 
	{
		case WM_SHOWWINDOW:
			break;
		case WM_COMMAND:
			wmId    = LOWORD(wParam); 
			wmEvent = HIWORD(wParam); 
			// Parse the menu selections:
			switch (wmId)
			{
				case IDM_EXIT:
				   DestroyWindow(hWnd);
				   break;
				default:
				   return DefWindowProc(hWnd, message, wParam, lParam);
			}
			break;
		case WM_PAINT:
			hdc = BeginPaint(hWnd, &ps);
			EndPaint(hWnd, &ps);  
			break;
		case WM_DESTROY:
			PostQuitMessage(0);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
   }
   return 0;
}

//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
//  COMMENTS:
//
//    This function and its usage is only necessary if you want this code
//    to be compatible with Win32 systems prior to the 'RegisterClassEx'
//    function that was added to Windows 95. It is important to call this function
//    so that the application will get 'well formed' small icons associated
//    with it.
//
#undef DPF_MODNAME
#define DPF_MODNAME "MyRegisterClass"

ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX); 

	wcex.style			= 0; 						// CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= (WNDPROC)MainWindowProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, (LPCTSTR)IDI_DIRECTX);
	wcex.hCursor		= NULL; 					// LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= NULL; 					//(LPCSTR)IDC_TESTWIN;
	wcex.lpszClassName	= DPVHELP_WINDOWCLASS;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, (LPCTSTR)IDI_DIRECTX);

	return RegisterClassEx(&wcex);
}

//
//   FUNCTION: InitInstance(HANDLE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
#undef DPF_MODNAME
#define DPF_MODNAME "CreateHiddenMainWindow"

HWND CreateHiddenMainWindow(HINSTANCE hInstance, int nCmdShow )
{
   HRESULT hr;
   HWND hWnd;

   hWnd = CreateWindow(DPVHELP_WINDOWCLASS, DPVHELP_WINDOWTITLE, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
		hr = GetLastError();
		return NULL;
   }

   ShowWindow(hWnd, SW_HIDE);
   UpdateWindow(hWnd);  

   return hWnd;
}

#undef DPF_MODNAME
#define DPF_MODNAME "CheckFullDuplex"

BOOL CheckFullDuplex( PDXVHELP_PARAMETERS pParameters )
{
	PDIRECTPLAYVOICETEST lpdvSetup;	
	HRESULT hr;

	hr = CoCreateInstance( DPVOICE_CLSID_DPVOICE, NULL, CLSCTX_INPROC_SERVER, IID_IDirectPlayVoiceTest, (void **) &lpdvSetup );

	if( FAILED( hr ) )
	{
		DPVDX_DVERRDisplay( hr, _T("Create of setup interface failed"), pParameters->fSilent );
		return FALSE;
	}	

	hr = lpdvSetup->CheckAudioSetup( &pParameters->guidPlaybackDevice, &pParameters->guidRecordDevice, NULL, DVFLAGS_QUERYONLY );

	if( FAILED( hr )  )
	{
		if( pParameters->fSilent )
		{
			lpdvSetup->Release();
			return FALSE;
		}
		
		hr = lpdvSetup->CheckAudioSetup( &pParameters->guidPlaybackDevice, &pParameters->guidRecordDevice, NULL, 0 );

		lpdvSetup->Release();		

		if( FAILED( hr ) )
		{
			return FALSE;			
		}
	}
	else
	{
		lpdvSetup->Release();
	}

	return TRUE;

}

#undef DPF_MODNAME
#define DPF_MODNAME "WinMain"

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
	DWORD                   dwThreadID;
	DXVHELP_RTINFO          rtInfo;
	BOOL                    bGotMsg = FALSE;	
	MSG                     msg;
	HRESULT                 hr;
    INITCOMMONCONTROLSEX    InitCC = {0};
    BOOL                    bOK = FALSE;
    BOOL					fRunTimeInit = FALSE;


	// Initialize COM
	hr = CoInitializeEx( NULL, COINIT_MULTITHREADED );

	if( FAILED( hr ) )
	{
		return FALSE;
	}

	if( !DNOSIndirectionInit() )
	{
		CoUninitialize();
		return FALSE;
	}

	// Setup default parameters 
	SetDefaultParameters( &rtInfo.dxvParameters );

	// Process the command-line
	//
	// If this function returns false, we are to exit
	if( !ProcessCommandLine( lpCmdLine, &rtInfo.dxvParameters ) )
	{
		if( rtInfo.dxvParameters.fRegister ) 
		{
			RegisterApplication();
		}
		else if( rtInfo.dxvParameters.fUnRegister )
		{
			UnRegisterApplication();
		}

		goto EXIT;
	}

	// Load common-controls
    InitCC.dwSize = sizeof InitCC;
	bOK = InitCommonControlsEx(&InitCC);	

	// Register Window Class
	MyRegisterClass(hInstance);	

	// Initialize Events 
	InitializeRunTime( &rtInfo );	

	fRunTimeInit = TRUE;

	if( rtInfo.dxvParameters.fLobbyLaunched )
	{
		SetEvent( rtInfo.hGo );
	}

	// Perform application initialization:
	rtInfo.hMainWnd = CreateHiddenMainWindow(hInstance, nCmdShow);

	if( rtInfo.hMainWnd == NULL )
	{
		goto EXIT;
	}

	rtInfo.hInst = hInstance;

	// If user wants to select the soundcard
	if( rtInfo.dxvParameters.fSelectCards  )
	{
		GetCardSettings( rtInfo.hInst, NULL, &rtInfo.dxvParameters.guidPlaybackDevice, &rtInfo.dxvParameters.guidRecordDevice );
	}

	if( !CheckFullDuplex( &rtInfo.dxvParameters ) )
	{
		goto EXIT;
	}

	// Startup thread which handles connects etc.
	VoiceManager_Start( &rtInfo );

	// Display Dialog if Required
	if( !rtInfo.dxvParameters.fSilent )
	{
		MainDialog_Create( &rtInfo );
	}

	while( MsgWaitForMultipleObjects( 1, &rtInfo.hThreadDone, FALSE, INFINITE, QS_ALLINPUT) != WAIT_OBJECT_0 )
	{
		bGotMsg = TRUE;
		
		while( bGotMsg )
		{
			bGotMsg = PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE );

			if( bGotMsg )
			{
				if( rtInfo.hMainDialog == NULL || !IsDialogMessage(rtInfo.hMainDialog,&msg) )
				{
					TranslateMessage( &msg );
					DispatchMessage( &msg );
				}
			}
		}
	}

	VoiceManager_Stop( &rtInfo );

EXIT:

	if( fRunTimeInit )
	{
		FreeRunTime( &rtInfo );		
	}

	DNOSIndirectionDeinit();

	CoUninitialize();
	
	return 0;

}