/*++

Copyright (c) 2000  Microsoft Corporation

Module Name:

    phone.h

Abstract:

    Declaration of the CPhone class
    
Notes:

Revision History:

--*/

#ifndef __PHONE_H_
#define __PHONE_H_

#include "resource.h"       // main symbols

#include "address.h"
#include "ObjectSafeImpl.h"

class CTAPI;

extern CHashTable             * gpPhoneHashTable;

/////////////////////////////////////////////////////////////////
// Intermediate classes  used for DISPID encoding
template <class T>
class  ITPhoneVtbl : public ITPhone
{
	DECLARE_TRACELOG_CLASS(ITPhoneVtbl)
};

template <class T>
class  ITAutomatedPhoneControlVtbl : public ITAutomatedPhoneControl
{
	DECLARE_TRACELOG_CLASS(ITAutomatedPhoneControlVtbl)
};

/////////////////////////////////////////////////////////////////
// Constants defining default values for certain properties.

const DWORD APC_DEFAULT_AEONT = 3000;
const DWORD APC_DEFAULT_AKTMD = 100;
const DWORD APC_DEFAULT_VCS = 4096;
const DWORD APC_DEFAULT_VCRD = 500;
const DWORD APC_DEFAULT_VCRP = 100;

const DWORD APC_MAX_NUMBERS_GATHERED = 100;

/////////////////////////////////////////////////////////////////
// Types

typedef enum AUTOMATED_PHONE_STATE
{
    APS_ONHOOK_IDLE,
    APS_OFFHOOK_DIALTONE,
    APS_OFFHOOK_WARNING,
    APS_ONHOOK_RINGING_IN,
    APS_OFFHOOK_DIALING,
    APS_OFFHOOK_DEAD_LINE,
    APS_OFFHOOK_CALL_INIT,
    APS_OFFHOOK_CONNECTED,
    APS_ONHOOK_CONNECTED,
    APS_OFFHOOK_BUSY_TONE,
    APS_OFFHOOK_RINGING_OUT,
    APS_ONHOOK_RINGING_OUT
} AUTOMATED_PHONE_STATE;

/////////////////////////////////////////////////////////////////////////////
// CPhone
class CPhone : 
	public CTAPIComObjectRoot<CPhone>,
    public IDispatchImpl<ITPhoneVtbl<CPhone>, &IID_ITPhone, &LIBID_TAPI3Lib>,
    public IDispatchImpl<ITAutomatedPhoneControlVtbl<CPhone>,
                         &IID_ITAutomatedPhoneControl, &LIBID_TAPI3Lib>,
    public CObjectSafeImpl
{
public:

	CPhone() : m_pTAPI(NULL),
               m_hPhoneApp(NULL),
               m_dwAPIVersion(0),
               m_dwDeviceID(0),
               m_hPhone(NULL),
               m_dwPrivilege(0),
               m_pPhoneCaps(NULL),
               m_pdwLineDeviceIDs(NULL),
               m_dwNumLineDeviceIDs(0),
               m_Tone(PT_SILENCE),
               m_fRinger(FALSE),
               m_fPhoneHandlingEnabled(FALSE),
               m_dwAutoEndOfNumberTimeout(APC_DEFAULT_AEONT),
               m_fAutoDialtone(TRUE),
               m_fAutoStopTonesOnOnHook(TRUE),
               m_fAutoStopRingOnOffHook(TRUE),
               m_fAutoKeypadTones(TRUE),
               m_dwAutoKeypadTonesMinimumDuration(APC_DEFAULT_AKTMD),
               m_pCall(NULL),
               m_hTimerQueue(NULL),
               m_hTimerEvent(NULL),
               m_hToneTimer(NULL),
               m_hDTMFTimer(NULL),
               m_hRingTimer(NULL),
               m_hVolumeTimer(NULL),
               m_hAutoEndOfNumberTimer(NULL),
               m_fUseWaveForRinger(FALSE),
               m_wszNumbersGathered(NULL),
               m_AutomatedPhoneState(APS_ONHOOK_IDLE),
               m_fAutoVolumeControl(TRUE),
               m_dwAutoVolumeControlStep(APC_DEFAULT_VCS),
               m_dwAutoVolumeControlRepeatDelay(APC_DEFAULT_VCRD),
               m_dwAutoVolumeControlRepeatPeriod(APC_DEFAULT_VCRP),
               m_dwOffHookCount(0),
               m_fInitialized(FALSE)
    {
        LOG((TL_TRACE, "CPhone[%p] - enter", this ));
        LOG((TL_TRACE, "CPhone - finished" ));
    }

    ~CPhone()
    {
        LOG((TL_TRACE, "~CPhone[%p] - enter", this ));
        LOG((TL_TRACE, "~CPhone - finished" ));
    }


DECLARE_DEBUG_ADDREF_RELEASE(CPhone)
DECLARE_QI()
DECLARE_MARSHALQI(CPhone)
DECLARE_TRACELOG_CLASS(CPhone)

BEGIN_COM_MAP(CPhone)
	COM_INTERFACE_ENTRY2(IDispatch, ITPhone)
    COM_INTERFACE_ENTRY(ITPhone)
    // ITAutomatedPhoneControlQI will fall thru on SUCCESS, so we need to keep COM_INTERFACE_ENTRY(ITAutomatedPhoneControl)
    COM_INTERFACE_ENTRY_FUNC(IID_ITAutomatedPhoneControl, 0, CPhone::ITAutomatedPhoneControlQI)
    COM_INTERFACE_ENTRY(ITAutomatedPhoneControl)
    COM_INTERFACE_ENTRY(IObjectSafety)
    COM_INTERFACE_ENTRY_FUNC(IID_IMarshal, 0, IMarshalQI)
    COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal, m_pFTM)
END_COM_MAP()

    void FinalRelease();    

private:
    ITTAPI      * m_pTAPI;
    HPHONEAPP     m_hPhoneApp;
    DWORD         m_dwAPIVersion;
    DWORD         m_dwDeviceID;
    HPHONE        m_hPhone;
    DWORD         m_dwPrivilege;
    LPPHONECAPS   m_pPhoneCaps;
    DWORD       * m_pdwLineDeviceIDs;
    DWORD         m_dwNumLineDeviceIDs;
    BOOL          m_fInitialized;

    // AutomatedPhoneControl Variables
    CWavePlayer   m_WavePlayer;
    BOOL          m_fUseWaveForRinger;    
    BOOL          m_fRinger;
    BOOL          m_fPhoneHandlingEnabled;
    DWORD         m_dwAutoEndOfNumberTimeout;
    BOOL          m_fAutoDialtone;
    BOOL          m_fAutoStopTonesOnOnHook;
    BOOL          m_fAutoStopRingOnOffHook;
    BOOL          m_fAutoKeypadTones;    
    DWORD         m_dwAutoKeypadTonesMinimumDuration;
    BOOL          m_fAutoVolumeControl;
    DWORD         m_dwAutoVolumeControlStep;
    DWORD         m_dwAutoVolumeControlRepeatDelay;
    DWORD         m_dwAutoVolumeControlRepeatPeriod;
    DWORD         m_dwOffHookCount;
    ITCallInfo  * m_pCall;
    LPWSTR        m_wszNumbersGathered;  
    PHONE_TONE    m_Tone;
    DWORD         m_dwToneDuration;
    DWORD         m_dwTonePeriodOn;
    DWORD         m_dwTonePeriodOff;    
    PHONE_TONE    m_DTMF;
    DWORD         m_dwDTMFStart;
    DWORD         m_dwRingDuration;
    DWORD         m_dwRingPeriod;
    BOOL          m_fVolumeUp;
    AUTOMATED_PHONE_STATE m_AutomatedPhoneState;
    BOOL          m_fDefaultTerminalsSelected;

    // Timer Handles
    HANDLE        m_hTimerQueue;
    HANDLE        m_hTimerEvent;
    HANDLE        m_hToneTimer;
    HANDLE        m_hDTMFTimer;
    HANDLE        m_hRingTimer;
    HANDLE        m_hVolumeTimer;
    HANDLE        m_hAutoEndOfNumberTimer;

    // Critical Sections
    CRITICAL_SECTION m_csAutomatedPhoneState;
    CRITICAL_SECTION m_csToneTimer;
    CRITICAL_SECTION m_csRingTimer;

public:

    static HRESULT WINAPI ITAutomatedPhoneControlQI(void* pv, REFIID riid, LPVOID* ppv, DWORD_PTR dw);

    DWORD GetDeviceID(){ return m_dwDeviceID; }
    DWORD GetAPIVersion(){ return m_dwAPIVersion; }
    HPHONEAPP GetHPhoneApp(){ return m_hPhoneApp; }

    HRESULT Initialize(
                       ITTAPI * pTAPI,
                       HPHONEAPP hPhoneApp,
                       DWORD dwAPIVersion,
                       DWORD dwDeviceID
                      );

    void SetPhoneCapBuffer( LPVOID pBuf );

    HRESULT UpdatePhoneCaps();
    HRESULT InvalidatePhoneCaps();

    BOOL IsPhoneOnAddress(ITAddress * pAddress);
    BOOL IsPhoneOnPreferredAddress(ITAddress *pAddress);
    BOOL IsPhoneUsingWaveID(DWORD dwWaveID, TERMINAL_DIRECTION nDir);

    CTAPI * GetTapi();

    void ForceClose();

    void Automation_CallState( ITCallInfo * pCall, CALL_STATE cs, CALL_STATE_EVENT_CAUSE cause );
    void Automation_ButtonDown( DWORD dwButtonId );
    void Automation_ButtonUp( DWORD dwButtonId );
    void Automation_OnHook( PHONE_HOOK_SWITCH_DEVICE phsd );
    void Automation_OffHook( PHONE_HOOK_SWITCH_DEVICE phsd );
    void Automation_EndOfNumberTimeout();

    //
    // IDispatch
    //

    STDMETHOD(GetIDsOfNames)(REFIID riid, 
                             LPOLESTR* rgszNames,
                             UINT cNames, 
                             LCID lcid, 
                             DISPID* rgdispid
                            );

    STDMETHOD(Invoke)(DISPID dispidMember, 
                      REFIID riid, 
                      LCID lcid,
                      WORD wFlags, 
                      DISPPARAMS* pdispparams, 
                      VARIANT* pvarResult,
                      EXCEPINFO* pexcepinfo, 
                      UINT* puArgErr
                      );

    STDMETHOD_(ULONG, InternalAddRef)();

    STDMETHOD_(ULONG, InternalRelease)();

    //
    // ITPhone
    //

    STDMETHOD(Open)(
            IN   PHONE_PRIVILEGE Privilege
            );
            
    STDMETHOD(Close)();
    
    STDMETHOD(get_Addresses)(
            OUT  VARIANT * pAddresses
            );

    STDMETHOD(EnumerateAddresses)(
            OUT  IEnumAddress ** ppEnumAddress
            );

    STDMETHOD(get_PreferredAddresses)(
            OUT  VARIANT * pAddresses
            );

    STDMETHOD(EnumeratePreferredAddresses)(
            OUT  IEnumAddress ** ppEnumAddress
            );

    STDMETHOD(get_PhoneCapsLong)(
            IN   PHONECAPS_LONG   pclCap,
            OUT  long           * plCapability
            );

    STDMETHOD(get_PhoneCapsString)(
            IN   PHONECAPS_STRING   pcsCap,
            OUT  BSTR             * ppCapability
            );

    STDMETHOD(GetPhoneCapsBuffer)(
            IN PHONECAPS_BUFFER pcbCaps,
            OUT DWORD *pdwSize,
            OUT BYTE **ppPhoneCapsBuffer
            );

    STDMETHOD(get_PhoneCapsBuffer)(
            IN PHONECAPS_BUFFER pcbCaps,
            OUT VARIANT *pVarBuffer
            );

    STDMETHOD(get_Terminals)(
            IN   ITAddress * pAddress,
            OUT  VARIANT   * pTerminals
            );

    STDMETHOD(EnumerateTerminals)(
            IN   ITAddress      * pAddress,
            OUT  IEnumTerminal ** ppEnumTerminal
            );

    STDMETHOD(get_ButtonMode)(
            IN   long                lButtonID,
            OUT  PHONE_BUTTON_MODE * pButtonMode
            );

    STDMETHOD(put_ButtonMode)(
            IN   long                lButtonID,
            IN   PHONE_BUTTON_MODE   ButtonMode
            );


    STDMETHOD(get_LampMode)(
            IN long lLampID,
            OUT PHONE_LAMP_MODE* pLampMode
            );

    STDMETHOD(put_LampMode)(
            IN long lLampID,
            IN PHONE_LAMP_MODE pLampMode
            );

    STDMETHOD(get_ButtonFunction)(
            IN   long                    lButtonID,
            OUT  PHONE_BUTTON_FUNCTION * pButtonFunction
            );

    STDMETHOD(put_ButtonFunction)(
            IN long                  lButtonID, 
            IN PHONE_BUTTON_FUNCTION ButtonFunction
            );

    STDMETHOD(get_ButtonText)(
            IN   long   lButtonID, 
            OUT  BSTR * ppButtonText
            );

    STDMETHOD(put_ButtonText)(
            IN   long   lButtonID, 
            IN   BSTR   bstrButtonText
            );

    STDMETHOD(get_ButtonState)(
            IN   long                 lButtonID,
            OUT  PHONE_BUTTON_STATE * pButtonState
            );

    STDMETHOD(get_HookSwitchState)(
            IN   PHONE_HOOK_SWITCH_DEVICE   HookSwitchDevice,
            OUT  PHONE_HOOK_SWITCH_STATE  * pHookSwitchState
            );

    STDMETHOD(put_HookSwitchState)(
            IN   PHONE_HOOK_SWITCH_DEVICE HookSwitchDevice,
            OUT  PHONE_HOOK_SWITCH_STATE  HookSwitchState
            );

    STDMETHOD(put_RingMode)(
            IN   long lRingMode
            );

    STDMETHOD(get_RingMode)(
            OUT  long * plRingMode
            );

    STDMETHOD(put_RingVolume)(
            IN   long lRingVolume
            );

    STDMETHOD(get_RingVolume)(
            OUT  long * plRingVolume
            );

    STDMETHOD(get_Privilege)(
            OUT  PHONE_PRIVILEGE * pPrivilege
            );

    STDMETHOD(get_Display)(
            BSTR *pbstrDisplay
            );

    
    //
    // put display string at a specified location
    //

    STDMETHOD(SetDisplay)(
            IN long lRow,
            IN long lColumn,
            IN BSTR bstrDisplay
            );

    STDMETHOD(DeviceSpecific)(
	         IN BYTE *pParams,
	         IN DWORD dwSize
            );

    STDMETHOD(DeviceSpecificVariant)(
	         IN VARIANT varDevSpecificByteArray
            );

    STDMETHOD(NegotiateExtVersion)(
	         IN long lLowVersion,
	         IN long lHighVersion,
	         IN long *plExtVersion
            );

    //
    // ITAutomatedPhoneControl
    //

public:
    STDMETHOD (StartTone)(
            IN   PHONE_TONE Tone,
            IN   long       lDuration
            );

    STDMETHOD (StopTone)();

    STDMETHOD (get_Tone)(
            OUT   PHONE_TONE * pTone
            );

    STDMETHOD (StartRinger)(
            IN    long lRingMode,
            IN    long lDuration
            );

    STDMETHOD (StopRinger)();

    STDMETHOD (get_Ringer)(
            OUT   VARIANT_BOOL * pfRinging
            );

    STDMETHOD (put_PhoneHandlingEnabled)(
            IN    VARIANT_BOOL fEnabled
            );

    STDMETHOD (get_PhoneHandlingEnabled)(
            OUT   VARIANT_BOOL * pfEnabled
            );

    STDMETHOD (put_AutoEndOfNumberTimeout)(
            IN    long lTimeout
            );

    STDMETHOD (get_AutoEndOfNumberTimeout)(
            OUT   long * plTimeout
            );

    STDMETHOD (put_AutoDialtone)(
            IN    VARIANT_BOOL fEnabled
            );

    STDMETHOD (get_AutoDialtone)(
            OUT   VARIANT_BOOL * pfEnabled
            );

    STDMETHOD (put_AutoStopTonesOnOnHook)(
            IN    VARIANT_BOOL fEnabled
            );

    STDMETHOD (get_AutoStopTonesOnOnHook)(
            OUT   VARIANT_BOOL * pfEnabled
            );

    STDMETHOD (put_AutoStopRingOnOffHook)(
            IN    VARIANT_BOOL fEnabled
            );

    STDMETHOD (get_AutoStopRingOnOffHook)(
            OUT   VARIANT_BOOL * pfEnabled
            );

    STDMETHOD (put_AutoKeypadTones)(
            IN    VARIANT_BOOL fEnabled
            );

    STDMETHOD (get_AutoKeypadTones)(
            OUT   VARIANT_BOOL * pfEnabled
            );

    STDMETHOD (put_AutoKeypadTonesMinimumDuration)(
            IN    long lDuration
            );

    STDMETHOD (get_AutoKeypadTonesMinimumDuration)(
            OUT   long * plDuration
            );

    STDMETHOD (put_AutoVolumeControl)(
            IN    VARIANT_BOOL fEnabled
            );

    STDMETHOD (get_AutoVolumeControl)(
            OUT   VARIANT_BOOL * pfEnabled
            );

    STDMETHOD (put_AutoVolumeControlStep)(
            IN    long lStepSize
            );

    STDMETHOD (get_AutoVolumeControlStep)(
            OUT   long * plStepSize
            );

    STDMETHOD (put_AutoVolumeControlRepeatDelay)(
            IN    long lDelay
            );

    STDMETHOD (get_AutoVolumeControlRepeatDelay)(
            OUT   long * plDelay
            );

    STDMETHOD (put_AutoVolumeControlRepeatPeriod)(
            IN    long lPeriod
            );

    STDMETHOD (get_AutoVolumeControlRepeatPeriod)(
            OUT   long * plPeriod
            );

    STDMETHOD (SelectCall)(
            IN    ITCallInfo   * pCall,
            IN    VARIANT_BOOL   fSelectDefaultTerminals
            );

    STDMETHOD (UnselectCall)(
            IN    ITCallInfo * pCall
            );

    STDMETHOD (EnumerateSelectedCalls)(
            OUT   IEnumCall ** ppCallEnum
            );

    STDMETHOD (get_SelectedCalls)(
            OUT   VARIANT * pVariant
            );

    // Private helper methods for ITAutomatedPhoneControl implementation
private:
    void UnselectAllPreviouslySelectedTerminals(
            IN   ITBasicCallControl2 * pCall,
            IN   ITTerminal          * pTerminalThatFailed,
            IN   IEnumTerminal       * pEnum
            );

    HRESULT InternalUnselectCall(
            IN    ITCallInfo * pCall
            );

    HRESULT SelectDefaultTerminalsOnCall(
            IN   ITCallInfo * pCall
            );

    HRESULT UnselectDefaultTerminalsOnCall(
            IN   ITCallInfo * pCall
            );

    HRESULT GetPhoneWaveRenderID(
            IN   DWORD * pdwWaveID
            );

    void OpenWaveDevice(
            );

    void CloseWaveDevice(
            );   

    static VOID CALLBACK ToneTimerCallback(
      PVOID lpParameter,    
      BOOLEAN TimerOrWaitFired
    );

    static VOID CALLBACK DTMFTimerCallback(
      PVOID lpParameter,    
      BOOLEAN TimerOrWaitFired
    );

    static VOID CALLBACK RingTimerCallback(
      PVOID lpParameter,    
      BOOLEAN TimerOrWaitFired
    );

    static VOID CALLBACK VolumeTimerCallback(
      PVOID lpParameter,    
      BOOLEAN TimerOrWaitFired
    );

    static VOID CALLBACK AutoEndOfNumberTimerCallback(
      PVOID lpParameter,    
      BOOLEAN TimerOrWaitFired
    );
};

class CPhoneEvent : 
    public CTAPIComObjectRoot<CPhoneEvent, CComMultiThreadModelNoCS>, // no need to have a cs
    public CComDualImpl<ITPhoneEvent, &IID_ITPhoneEvent, &LIBID_TAPI3Lib>,
    public CObjectSafeImpl
{
public:

DECLARE_MARSHALQI(CPhoneEvent)
DECLARE_TRACELOG_CLASS(CPhoneEvent)

BEGIN_COM_MAP(CPhoneEvent)
    COM_INTERFACE_ENTRY(IDispatch)
    COM_INTERFACE_ENTRY(ITPhoneEvent)
    COM_INTERFACE_ENTRY(IObjectSafety)
    COM_INTERFACE_ENTRY_FUNC(IID_IMarshal, 0, IMarshalQI)
    COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal, m_pFTM)
END_COM_MAP()

    static HRESULT FireEvent(
                             CPhone * pCPhone,
                             PHONE_EVENT Event,
                             PHONE_BUTTON_STATE ButtonState,
                             PHONE_HOOK_SWITCH_STATE HookSwitchState,
                             PHONE_HOOK_SWITCH_DEVICE HookSwitchDevice,
                             DWORD dwRingMode,
                             DWORD dwButtonLampId,
                             PWSTR pNumber,
                             ITCallInfo * pCallInfo
                            );
    void FinalRelease();
                      
protected:
    ITPhone                 * m_pPhone;
    PHONE_EVENT               m_Event;
    PHONE_BUTTON_STATE        m_ButtonState;
    PHONE_HOOK_SWITCH_STATE   m_HookSwitchState;
    PHONE_HOOK_SWITCH_DEVICE  m_HookSwitchDevice;
    DWORD                     m_dwRingMode;
    DWORD                     m_dwButtonLampId;
    BSTR                      m_pNumber;
    ITCallInfo              * m_pCallInfo;

#if DBG
    PWSTR                     m_pDebug;
#endif

    
public:
    STDMETHOD(get_Phone)( ITPhone ** ppPhone );
    STDMETHOD(get_Event)( PHONE_EVENT * pEvent );
    STDMETHOD(get_ButtonState)( PHONE_BUTTON_STATE * pState );
    STDMETHOD(get_HookSwitchState)( PHONE_HOOK_SWITCH_STATE * pState );
    STDMETHOD(get_HookSwitchDevice)( PHONE_HOOK_SWITCH_DEVICE * pDevice );
    STDMETHOD(get_RingMode)( long * plRingMode );
    STDMETHOD(get_ButtonLampId)( LONG * plButtonLampId );
    STDMETHOD(get_NumberGathered)( BSTR * ppNumber );
    STDMETHOD(get_Call)( ITCallInfo ** ppCallInfo );
};
            
class CPhoneDevSpecificEvent : 
    public CTAPIComObjectRoot<CPhoneDevSpecificEvent, CComMultiThreadModelNoCS>, // no need to have a cs
    public CComDualImpl<ITPhoneDeviceSpecificEvent, &IID_ITPhoneDeviceSpecificEvent, &LIBID_TAPI3Lib>,
    public CObjectSafeImpl
{
public:

DECLARE_MARSHALQI(CPhoneDevSpecificEvent)
DECLARE_TRACELOG_CLASS(CPhoneDevSpecificEvent)

BEGIN_COM_MAP(CPhoneDevSpecificEvent)
    COM_INTERFACE_ENTRY(IDispatch)
    COM_INTERFACE_ENTRY(ITPhoneDeviceSpecificEvent)
    COM_INTERFACE_ENTRY(IObjectSafety)
    COM_INTERFACE_ENTRY_FUNC(IID_IMarshal, 0, IMarshalQI)
    COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal, m_pFTM)
END_COM_MAP()

    static HRESULT FireEvent(
                             CPhone *pPhone,
                             long lParam1,
                             long lParam2,
                             long lParam3
                            );
    void FinalRelease();
                      
    CPhoneDevSpecificEvent();

protected:




    //
    // phone for which the event was fired
    //

    ITPhone *m_pPhone;

    
    //
    // data received from the TSP
    //

    long m_l1;
    long m_l2;
    long m_l3;


#if DBG
    PWSTR           m_pDebug;
#endif

    
public:

    STDMETHOD(get_Phone)( ITPhone **ppPhone );
    STDMETHOD(get_lParam1)( long *plParam1 );
    STDMETHOD(get_lParam2)( long *plParam2 );
    STDMETHOD(get_lParam3)( long *plParam3 );
    
};


#endif //__PHONE_H_