/*==========================================================================
 *
 *  Copyright (C) 1998-2000 Microsoft Corporation.  All Rights Reserved.
 *
 *  File:		Endpoint.h
 *  Content:	DNSerial communications endpoint
 *
 *
 *  History:
 *   Date		By		Reason
 *   ====		==		======
 *	01/20/99	jtk		Created
 *	05/11/99	jtk		Split out to make a base class
 ***************************************************************************/

#ifndef __ENDPOINT_H__
#define __ENDPOINT_H__

//**********************************************************************
// Constant definitions
//**********************************************************************

#define	MAX_PHONE_NUMBER_SIZE	200

//
// enumeration of endpoint types
//
typedef	enum	_ENDPOINT_TYPE
{
	ENDPOINT_TYPE_UNKNOWN = 0,			// unknown endpoint type
	ENDPOINT_TYPE_LISTEN,				// 'Listen' endpoint
	ENDPOINT_TYPE_CONNECT,				// 'Conenct' endpoint
	ENDPOINT_TYPE_ENUM,					// 'Enum' endpoint
	ENDPOINT_TYPE_CONNECT_ON_LISTEN		// endpoint connected from a 'listen'
} ENDPOINT_TYPE;

//
// enumeration of the states an endpoint can be in
//
typedef	enum
{
	ENDPOINT_STATE_UNINITIALIZED = 0,		// uninitialized state
	ENDPOINT_STATE_ATTEMPTING_ENUM,			// attempting to enum
	ENDPOINT_STATE_ENUM,					// endpoint is supposed to enum connections
	ENDPOINT_STATE_ATTEMPTING_CONNECT,		// attempting to connect
	ENDPOINT_STATE_CONNECT_CONNECTED,		// endpoint is supposed to connect and is connected
	ENDPOINT_STATE_ATTEMPTING_LISTEN,		// attempting to listen
	ENDPOINT_STATE_LISTENING,				// endpoint is supposed to listen and is listening
	ENDPOINT_STATE_DISCONNECTING,			// endpoint is disconnecting

	ENDPOINT_STATE_MAX
} ENDPOINT_STATE;

//**********************************************************************
// Macro definitions
//**********************************************************************

//**********************************************************************
// Structure definitions
//**********************************************************************

//
// forward structure references
//
typedef	struct	_JOB_HEADER			JOB_HEADER;
typedef	struct	_THREAD_POOL_JOB	THREAD_POOL_JOB;
class	CModemCommandData;
class	CDataPort;
class	CModemReadIOData;
class	CModemThreadPool;
class	CModemWriteIOData;

//
// structure used to get data from the endpoint port pool
//
typedef	struct	_ENDPOINT_POOL_CONTEXT
{
	CModemSPData *pSPData;
	BOOL	fModem;
} ENDPOINT_POOL_CONTEXT;

//
// structure to bind extra information to an enum query to be used on enum reponse
//
typedef	struct	_ENDPOINT_ENUM_QUERY_CONTEXT
{
	SPIE_QUERY	EnumQueryData;
	HANDLE		hEndpoint;
	UINT_PTR	uEnumRTTIndex;
} ENDPOINT_ENUM_QUERY_CONTEXT;

//**********************************************************************
// Variable definitions
//**********************************************************************

//**********************************************************************
// Function prototypes
//**********************************************************************

//**********************************************************************
// Class definition
//**********************************************************************

class	CModemEndpoint
{
	public:

		#undef DPF_MODNAME
		#define DPF_MODNAME "CModemEndpoint::Lock"
		void	Lock( void )
		{
			DNASSERT( m_Flags.fInitialized != FALSE );
			DNEnterCriticalSection( &m_Lock );
		}

		#undef DPF_MODNAME
		#define DPF_MODNAME "CModemEndpoint::Unlock"
		void	Unlock( void )
		{
			DNASSERT( m_Flags.fInitialized != FALSE );
			DNLeaveCriticalSection( &m_Lock );
		}

		void	ReturnSelfToPool( void );
		
		#undef DPF_MODNAME
		#define DPF_MODNAME "CModemEndpoint::AddRef"
		void	AddRef( void ) 
		{ 
			DNASSERT( m_iRefCount != 0 );
			DNInterlockedIncrement( &m_iRefCount ); 
		}

		#undef DPF_MODNAME
		#define DPF_MODNAME "CModemEndpoint::DecRef"
		void	DecRef( void )
		{
			DNASSERT( m_iRefCount != 0 );
			if ( DNInterlockedDecrement( &m_iRefCount ) == 0 )
			{
				ReturnSelfToPool();
			}
		}

		void	AddCommandRef( void )
		{
			DNInterlockedIncrement( &m_lCommandRefCount );
			AddRef();
		}

		void	DecCommandRef( void )
		{
			if ( DNInterlockedDecrement( &m_lCommandRefCount ) == 0 )
			{
				CleanUpCommand();
			}
			DecRef();
		}
		
		DPNHANDLE	GetHandle( void ) const { return m_Handle; }

		#undef DPF_MODNAME
		#define DPF_MODNAME "CModemEndpoint::SetHandle"
		void	SetHandle( const DPNHANDLE Handle )
		{
			DNASSERT( ( m_Handle == 0 ) || ( Handle == 0 ) );
			m_Handle = Handle;
		}

		const CComPortData	*ComPortData( void ) const { return &m_ComPortData; }
		const SP_BAUD_RATE	GetBaudRate( void ) const { return m_ComPortData.GetBaudRate(); }
		HRESULT	SetBaudRate( const SP_BAUD_RATE BaudRate ) { return m_ComPortData.SetBaudRate( BaudRate ); }

		const SP_STOP_BITS	GetStopBits( void ) const { return m_ComPortData.GetStopBits(); }
		HRESULT	SetStopBits( const SP_STOP_BITS StopBits ) { return m_ComPortData.SetStopBits( StopBits ); }

		const SP_PARITY_TYPE	GetParity( void ) const  { return m_ComPortData.GetParity(); }
		HRESULT	SetParity( const SP_PARITY_TYPE Parity ) { return m_ComPortData.SetParity( Parity ); }

		const SP_FLOW_CONTROL	GetFlowControl( void ) const { return m_ComPortData.GetFlowControl(); }
		HRESULT	SetFlowControl( const SP_FLOW_CONTROL FlowControl ) { return m_ComPortData.SetFlowControl( FlowControl ); }

		const CComPortData	*GetComPortData( void ) const { return &m_ComPortData; }

		const GUID	*GetEncryptionGuid( void ) const 
		{ 
			if (m_fModem)
			{
				return &g_ModemSPEncryptionGuid;
			}
			else
			{
				return &g_SerialSPEncryptionGuid; 
			}
		}

		const TCHAR	*GetPhoneNumber( void ) const { return m_PhoneNumber; }

		#undef DPF_MODNAME
		#define DPF_MODNAME "CModemEndpoint::SetPhoneNumber"
		HRESULT	SetPhoneNumber( const TCHAR *const pPhoneNumber )
		{
			DNASSERT( pPhoneNumber != NULL );
			DNASSERT( lstrlen( pPhoneNumber ) < ( sizeof( m_PhoneNumber ) / sizeof( TCHAR ) ) );
			lstrcpyn( m_PhoneNumber, pPhoneNumber, MAX_PHONE_NUMBER_SIZE );
			return	DPN_OK;
		}

		void	*DeviceBindContext( void ) { return &m_dwDeviceID; }

		CDataPort	*GetDataPort( void ) const { return m_pDataPort; }

		#undef DPF_MODNAME
		#define DPF_MODNAME "CModemEndpoint::SetDataPort"
		void	SetDataPort( CDataPort *const pDataPort )
		{
			DNASSERT( ( m_pDataPort == NULL ) || ( pDataPort == NULL ) );
			m_pDataPort = pDataPort;
		}

		ENDPOINT_TYPE	GetType( void ) const { return m_EndpointType; }

		void	*GetUserEndpointContext( void ) const { return m_pUserEndpointContext; }

		#undef DPF_MODNAME
		#define DPF_MODNAME "CModemEndpoint::SetUserEndpointContext"
		void	SetUserEndpointContext( void *const pContext )
		{
			DNASSERT( ( m_pUserEndpointContext == NULL ) || ( pContext == NULL ) );
			m_pUserEndpointContext = pContext;
		}

		ENDPOINT_STATE	GetState( void ) const { return m_State; }
		void	SetState( const ENDPOINT_STATE EndpointState );
		CModemSPData	*GetSPData( void ) const { return m_pSPData; }

		CModemCommandData	*GetCommandData( void ) const { return m_pCommandHandle; }
		DPNHANDLE	GetDisconnectIndicationHandle( void ) const { return this->m_hDisconnectIndicationHandle; }
		void	SetDisconnectIndicationHandle( const DPNHANDLE hDisconnectIndicationHandle )
		{
			DNASSERT( ( GetDisconnectIndicationHandle() == 0 ) ||
					  ( hDisconnectIndicationHandle == 0 ) );
			m_hDisconnectIndicationHandle = hDisconnectIndicationHandle;
		}

		void	CopyConnectData( const SPCONNECTDATA *const pConnectData );
		static	void	ConnectJobCallback( THREAD_POOL_JOB *const pJobHeader );
		static	void	CancelConnectJobCallback( THREAD_POOL_JOB *const pJobHeader );
		HRESULT	CompleteConnect( void );

		void	CopyListenData( const SPLISTENDATA *const pListenData );
		static	void	ListenJobCallback( THREAD_POOL_JOB *const pJobHeader );
		static	void	CancelListenJobCallback( THREAD_POOL_JOB *const pJobHeader );
		HRESULT	CompleteListen( void );

		void	CopyEnumQueryData( const SPENUMQUERYDATA *const pEnumQueryData );
		static	void	EnumQueryJobCallback( THREAD_POOL_JOB *const pJobHeader );
		static	void	CancelEnumQueryJobCallback( THREAD_POOL_JOB *const pJobHeader );
		HRESULT	CompleteEnumQuery( void );
		void	OutgoingConnectionEstablished( const HRESULT hCommandResult );

		HRESULT	Open( IDirectPlay8Address *const pHostAddress,
					  IDirectPlay8Address *const pAdapterAddress,
					  const LINK_DIRECTION LinkDirection,
					  const ENDPOINT_TYPE EndpointType );
		HRESULT	OpenOnListen( const CModemEndpoint *const pEndpoint );
		void	Close( const HRESULT hActiveCommandResult );
		DWORD	GetLinkSpeed( void ) const;

		HRESULT	Disconnect( const DPNHANDLE hOldEndpointHandle );
		void	SignalDisconnect( const DPNHANDLE hOldEndpointHandle );

		//
		// send functions
		//
		#undef DPF_MODNAME
		#define DPF_MODNAME "CModemEndpoint::SendUserData"
		void	SendUserData( CModemWriteIOData *const pWriteBuffer )
		{
			DNASSERT( pWriteBuffer != NULL );
			DNASSERT( m_pDataPort != NULL );
			m_pDataPort->SendUserData( pWriteBuffer );
		}

		#undef DPF_MODNAME
		#define DPF_MODNAME "CModemEndpoint::SendEnumResponseData"
		void	SendEnumResponseData( CModemWriteIOData *const pWriteBuffer, const UINT_PTR uRTTIndex )
		{
			DNASSERT( pWriteBuffer != NULL );
			DNASSERT( m_pDataPort != NULL );
			m_pDataPort->SendEnumResponseData( pWriteBuffer, uRTTIndex );
		}

		void	StopEnumCommand( const HRESULT hCommandResult );

		LINK_DIRECTION	GetLinkDirection( void ) const;

		//
		// dialog settings
		//
		IDirectPlay8Address	*GetRemoteHostDP8Address( void ) const;
		IDirectPlay8Address	*GetLocalAdapterDP8Address( const ADDRESS_TYPE AddressType ) const;

		//
		// data processing functions
		//
		void	ProcessEnumData( SPRECEIVEDBUFFER *const pReceivedBuffer, const UINT_PTR uRTTIndex );
		void	ProcessEnumResponseData( SPRECEIVEDBUFFER *const pReceivedBuffer, const UINT_PTR uRTTIndex );
		void	ProcessUserData( CModemReadIOData *const pReadData );
		void	ProcessUserDataOnListen( CModemReadIOData *const pReadData );

#ifndef DPNBUILD_NOSPUI
		//
		// UI functions
		//
		HRESULT	ShowOutgoingSettingsDialog( CModemThreadPool *const pThreadPool );
		HRESULT	ShowIncomingSettingsDialog( CModemThreadPool *const pThreadPool );
		void	StopSettingsDialog( const HWND hDialog );

		void	SettingsDialogComplete( const HRESULT hDialogReturnCode );

		HWND	ActiveDialogHandle( void ) const { return m_hActiveDialogHandle; }

		#undef DPF_MODNAME
		#define DPF_MODNAME "CModemEndpoint::SetActiveDialogHandle"
		void	SetActiveDialogHandle( const HWND hDialog )
		{
			DNASSERT( ( ActiveDialogHandle() == NULL ) || ( hDialog == NULL ) );
			m_hActiveDialogHandle = hDialog;
		}
#endif // !DPNBUILD_NOSPUI

		//
		// port settings
		//
		DWORD	GetDeviceID( void ) const 
		{ 
			if (m_fModem)
			{
				return m_dwDeviceID;
			}
			else
			{
				return m_ComPortData.GetDeviceID(); 
			}
		}
		HRESULT	SetDeviceID( const DWORD dwDeviceID ) 
		{ 
			if (m_fModem)
			{
				DNASSERT( ( m_dwDeviceID == INVALID_DEVICE_ID ) || ( dwDeviceID == INVALID_DEVICE_ID ) );
				m_dwDeviceID = dwDeviceID;
				return	DPN_OK;
			}
			else
			{
				return m_ComPortData.SetDeviceID( dwDeviceID ); 
			}
		}

		//
		// pool functions
		//
		static BOOL	PoolAllocFunction( void* pvItem, void* pvContext );
		static void	PoolInitFunction( void* pvItem, void* pvContext );
		static void	PoolReleaseFunction( void* pvItem );
		static void	PoolDeallocFunction( void* pvItem );


	protected:
		BYTE			m_Sig[4];	// debugging signature ('THPL')

		CModemSPData			*m_pSPData;					// pointer to SP data

		BOOL			m_fModem;

		CComPortData	m_ComPortData;

		DWORD	m_dwDeviceID;
		TCHAR	m_PhoneNumber[ MAX_PHONE_NUMBER_SIZE ];

		struct
		{
			BOOL	fInitialized : 1;
			BOOL	fConnectIndicated : 1;
			BOOL	fCommandPending : 1;
			BOOL	fListenStatusNeedsToBeIndicated : 1;
		} m_Flags;
		
		DWORD		m_dwEnumSendIndex;			// enum send index
		DWORD		m_dwEnumSendTimes[ 4 ];		// enum send times

		union									// Local copy of the pending command paramters.
		{										// This data contains the pointers to the active
			SPCONNECTDATA	ConnectData;		// command, and the user context.
			SPLISTENDATA	ListenData;			//
			SPENUMQUERYDATA	EnumQueryData;		//
		} m_CurrentCommandParameters;			//

		CModemCommandData	*m_pCommandHandle;		// pointer to active command (this is kept to
												// avoid a switch to go through the m_ActveCommandData
												// to find the command handle)

		HWND			m_hActiveDialogHandle;	// handle of active dialog

		#undef DPF_MODNAME
		#define DPF_MODNAME "CModemEndpoint::SetType"
		void	SetType( const ENDPOINT_TYPE EndpointType )
		{
			DNASSERT( ( m_EndpointType == ENDPOINT_TYPE_UNKNOWN ) || ( EndpointType == ENDPOINT_TYPE_UNKNOWN ) );
			m_EndpointType = EndpointType;
		}

		CModemCommandData	*CommandHandle( void ) const;

		#undef DPF_MODNAME
		#define DPF_MODNAME "CModemEndpoint::SetCommandHandle"
		void	SetCommandHandle( CModemCommandData *const pCommandHandle )
		{
			DNASSERT( ( m_pCommandHandle == NULL ) || ( pCommandHandle == NULL ) );
			m_pCommandHandle = pCommandHandle;
		}

		HRESULT	CommandResult( void ) const { return m_hPendingCommandResult; }
		void	SetCommandResult( const HRESULT hCommandResult ) { m_hPendingCommandResult = hCommandResult; }

		void	CompletePendingCommand( const HRESULT hResult );
		HRESULT	PendingCommandResult( void ) const { return m_hPendingCommandResult; }

		static void		EnumCompleteWrapper( const HRESULT hCompletionCode, void *const pContext );	
		static void		EnumTimerCallback( void *const pContext );
		void	EnumComplete( const HRESULT hCompletionCode );
		
		HRESULT	SignalConnect( SPIE_CONNECT *const pConnectData );
		const void	*GetDeviceContext( void ) const;
		void	CleanUpCommand( void );

#ifndef DPNBUILD_ONLYONETHREAD
		DNCRITICAL_SECTION	m_Lock;	   					// critical section
#endif // !DPNBUILD_ONLYONETHREAD
		DPNHANDLE				m_Handle;					// active handle for this endpoint
		volatile ENDPOINT_STATE	m_State;				// endpoint state
		
		volatile LONG			m_lCommandRefCount;		// Command ref count.  When this
														// goes to zero, the endpoint unbinds
														// from the network
		volatile LONG		m_iRefCount;

		ENDPOINT_TYPE	m_EndpointType;					// type of endpoint
		CDataPort		*m_pDataPort;					// pointer to associated data port

		HRESULT			m_hPendingCommandResult;		// command result returned when endpoint RefCount == 0
		DPNHANDLE			m_hDisconnectIndicationHandle;	// handle to be indicated with disconnect notification.

		void			*m_pUserEndpointContext;		// user context associated with this endpoint

		//
		// make copy constructor and assignment operator private and unimplemented
		// to prevent illegal copies from being made
		//
		CModemEndpoint( const CModemEndpoint & );
		CModemEndpoint& operator=( const CModemEndpoint & );
};

#undef DPF_MODNAME

#endif	// __ENDPOINT_H__