#ifndef __MSPADDR_H_
#define __MSPADDR_H_

typedef struct {
  LIST_ENTRY Link;
  MSP_EVENT_INFO MSPEventInfo;
} MSPEVENTITEM,*PMSPEVENTITEM;

MSPEVENTITEM *AllocateEventItem(SIZE_T nExtraBytes = 0);
WINBOOL FreeEventItem(MSPEVENTITEM *pEventItemToFree);

typedef HRESULT (*PFNCREATETERM)(CComPtr<IMoniker> pMoniker,MSP_HANDLE htAddress,ITTerminal **pTerm);

typedef struct {
  DWORD dwMediaType;
  const CLSID *clsidClassManager;
  PFNCREATETERM pfnCreateTerm;
} STATIC_TERMINAL_TYPE;

class ATL_NO_VTABLE CPlugTerminalClassInfo : public IDispatchImpl<ITPluggableTerminalClassInfo,&IID_ITPluggableTerminalClassInfo,&LIBID_TAPI3Lib>,public CComObjectRootEx<CComMultiThreadModel>,public CMSPObjectSafetyImpl
{
public:
  DECLARE_GET_CONTROLLING_UNKNOWN()
  virtual HRESULT FinalConstruct(void);
  BEGIN_COM_MAP(CPlugTerminalClassInfo)
    COM_INTERFACE_ENTRY(ITPluggableTerminalClassInfo)
    COM_INTERFACE_ENTRY(IDispatch)
    COM_INTERFACE_ENTRY(IObjectSafety)
    COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal,m_pFTM)
  END_COM_MAP()
public:
  CPlugTerminalClassInfo() : m_bstrName(NULL),m_bstrCompany(NULL),m_bstrVersion(NULL),m_bstrCLSID(NULL),m_bstrTerminalClass(NULL),m_lMediaType(1),m_Direction(TD_CAPTURE),m_pFTM(NULL)
  {
  }
  ~CPlugTerminalClassInfo() {
    if(m_bstrName) {
      SysFreeString(m_bstrName);
    }
    if(m_bstrCompany) {
      SysFreeString(m_bstrCompany);
    }
    if(m_bstrVersion) {
      SysFreeString(m_bstrVersion);
    }
    if(m_bstrCLSID) {
      SysFreeString(m_bstrCLSID);
    }
    if(m_bstrTerminalClass) {
      SysFreeString(m_bstrTerminalClass);
    }
    if(m_pFTM) {
      m_pFTM->Release();
    }
  }
public:
  STDMETHOD(get_Name)(BSTR *pName);
  STDMETHOD(get_Company)(BSTR *pCompany);
  STDMETHOD(get_Version)(BSTR *pVersion);
  STDMETHOD(get_TerminalClass)(BSTR *pTerminalClass);
  STDMETHOD(get_CLSID)(BSTR *pCLSID);
  STDMETHOD(get_Direction)(TERMINAL_DIRECTION *pDirection);
  STDMETHOD(get_MediaTypes)(long *pMediaTypes);
private:
  CMSPCritSection m_CritSect;
  BSTR m_bstrName;
  BSTR m_bstrCompany;
  BSTR m_bstrVersion;
  BSTR m_bstrTerminalClass;
  BSTR m_bstrCLSID;
  long m_lMediaType;
  TERMINAL_DIRECTION m_Direction;
  IUnknown *m_pFTM;
private:
  STDMETHOD(put_Name)(BSTR bstrName);
  STDMETHOD(put_Company)(BSTR bstrCompany);
  STDMETHOD(put_Version)(BSTR bstrVersion);
  STDMETHOD(put_TerminalClass)(BSTR bstrTerminalClass);
  STDMETHOD(put_CLSID)(BSTR bstrCLSID);
  STDMETHOD(put_Direction)(TERMINAL_DIRECTION nDirection);
  STDMETHOD(put_MediaTypes)(long nMediaTypes);
  friend class CMSPAddress;
};

class ATL_NO_VTABLE CPlugTerminalSuperclassInfo : public IDispatchImpl<ITPluggableTerminalSuperclassInfo,&IID_ITPluggableTerminalSuperclassInfo,&LIBID_TAPI3Lib>,public CComObjectRootEx<CComMultiThreadModel>,public CMSPObjectSafetyImpl
{
public:
  DECLARE_GET_CONTROLLING_UNKNOWN()
  virtual HRESULT FinalConstruct(void);
  BEGIN_COM_MAP(CPlugTerminalSuperclassInfo)
    COM_INTERFACE_ENTRY(ITPluggableTerminalSuperclassInfo)
    COM_INTERFACE_ENTRY(IDispatch)
    COM_INTERFACE_ENTRY(IObjectSafety)
    COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal,m_pFTM)
  END_COM_MAP()
public:
  CPlugTerminalSuperclassInfo() : m_bstrCLSID(NULL),m_bstrName(NULL),m_pFTM(NULL) {
  }
  ~CPlugTerminalSuperclassInfo() {
    if(m_bstrName) {
      SysFreeString(m_bstrName);
    }
    if(m_bstrCLSID) {
      SysFreeString(m_bstrCLSID);
    }
    if(m_pFTM) {
      m_pFTM->Release();
    }
  }
public:
  STDMETHOD(get_Name)(BSTR *pName);
  STDMETHOD(get_CLSID)(BSTR *pCLSID);
private:
  CMSPCritSection m_CritSect;
  BSTR m_bstrCLSID;
  BSTR m_bstrName;
  IUnknown *m_pFTM;
private:
  STDMETHOD(put_Name)(BSTR bstrName);
  STDMETHOD(put_CLSID)(BSTR bstrCLSID);
  friend class CMSPAddress;
};

class ATL_NO_VTABLE CMSPAddress : public CComObjectRootEx<CComMultiThreadModelNoCS>,public ITMSPAddress,public IDispatchImpl<ITTerminalSupport2,&IID_ITTerminalSupport2,&LIBID_TAPI3Lib>
{
public:
  BEGIN_COM_MAP(CMSPAddress)
    COM_INTERFACE_ENTRY(ITMSPAddress)
    COM_INTERFACE_ENTRY(IDispatch)
    COM_INTERFACE_ENTRY(ITTerminalSupport)
    COM_INTERFACE_ENTRY(ITTerminalSupport2)
  END_COM_MAP()
  DECLARE_GET_CONTROLLING_UNKNOWN()
  DECLARE_VQI()
  CMSPAddress();
  virtual ~CMSPAddress();
  virtual ULONG MSPAddressAddRef(void) = 0;
  virtual ULONG MSPAddressRelease(void) = 0;
  STDMETHOD (Initialize) (MSP_HANDLE htEvent);
  STDMETHOD (Shutdown) ();
  STDMETHOD (CreateMSPCall) (MSP_HANDLE htCall,DWORD dwReserved,DWORD dwMediaType,IUnknown *pOuterUnknown,IUnknown **ppMSPCall) = 0;
  STDMETHOD (ShutdownMSPCall) (IUnknown *pMSPCall) = 0;
  STDMETHOD (ReceiveTSPData) (IUnknown *pMSPCall,LPBYTE pBuffer,DWORD dwBufferSize);
  STDMETHOD (GetEvent) (DWORD *pdwSize,BYTE *pBuffer);
  STDMETHOD (get_StaticTerminals) (VARIANT *pVariant);
  STDMETHOD (EnumerateStaticTerminals) (IEnumTerminal **ppTerminalEnumerator);
  STDMETHOD (get_DynamicTerminalClasses) (VARIANT *pVariant);
  STDMETHOD (EnumerateDynamicTerminalClasses) (IEnumTerminalClass **ppTerminalClassEnumerator);
  STDMETHOD (CreateTerminal) (BSTR pTerminalClass,long lMediaType,TERMINAL_DIRECTION Direction,ITTerminal **ppTerminal);
  STDMETHOD (GetDefaultStaticTerminal) (long lMediaType,TERMINAL_DIRECTION Direction,ITTerminal **ppTerminal);
  STDMETHOD (get_PluggableSuperclasses)(VARIANT *pVariant);
  STDMETHOD (EnumeratePluggableSuperclasses)(IEnumPluggableSuperclassInfo **ppSuperclassEnumerator);
  STDMETHOD (get_PluggableTerminalClasses)(BSTR bstrTerminalSuperclass,long lMediaType,VARIANT *pVariant);
  STDMETHOD (EnumeratePluggableTerminalClasses)(CLSID iidTerminalSuperclass,long lMediaType,IEnumPluggableTerminalClassInfo **ppClassEnumerator);
protected:
  virtual HRESULT GetStaticTerminals (DWORD *pdwNumTerminals,ITTerminal **ppTerminals);
  virtual HRESULT GetDynamicTerminalClasses (DWORD *pdwNumClasses,IID *pTerminalClasses);
public:
  virtual WINBOOL IsValidSetOfMediaTypes(DWORD dwMediaType,DWORD dwMask);
  virtual HRESULT PostEvent(MSPEVENTITEM *EventItem);
  virtual DWORD GetCallMediaTypes(void) = 0;
protected:
  virtual HRESULT IsMonikerInTerminalList(IMoniker *pMoniker);
  virtual HRESULT UpdateTerminalListForPnp(WINBOOL bDeviceArrival);
  virtual HRESULT UpdateTerminalList(void);
  virtual HRESULT ReceiveTSPAddressData(PBYTE pBuffer,DWORD dwSize);
public:
  virtual HRESULT PnpNotifHandler(WINBOOL bDeviceArrival);
protected:
  HANDLE m_htEvent;
  LIST_ENTRY m_EventList;
  CMSPCritSection m_EventDataLock;
  ITTerminalManager *m_pITTerminalManager;
  CMSPArray <ITTerminal *> m_Terminals;
  WINBOOL m_fTerminalsUpToDate;
  CMSPCritSection m_TerminalDataLock;
private:
  static const STATIC_TERMINAL_TYPE m_saTerminalTypes[];
  static const DWORD m_sdwTerminalTypesCount;
};

template <class T> HRESULT CreateMSPCallHelper(CMSPAddress *pCMSPAddress,MSP_HANDLE htCall,DWORD dwReserved,DWORD dwMediaType,IUnknown *pOuterUnknown,IUnknown **ppMSPCall,T **ppCMSPCall)
{
  LOG((MSP_TRACE,"CreateMSPCallHelper - enter"));
  HRESULT hr;
  T *pMSPCall;
  IUnknown *pUnknown = NULL;
  if(IsBadReadPtr(pCMSPAddress,sizeof(CMSPAddress))) {
    LOG((MSP_ERROR,"CreateMSPCallHelper - bad address pointer - exit E_POINTER"));
    return E_POINTER;
  }
  if(IsBadReadPtr(pOuterUnknown,sizeof(IUnknown))) {
    LOG((MSP_ERROR,"CreateMSPCallHelper - bad outer unknown - we require aggregation - exit E_POINTER"));
    return E_POINTER;
  }
  if(IsBadReadPtr(ppMSPCall,sizeof(IUnknown *))) {
    LOG((MSP_ERROR,"CreateMSPCallHelper - bad iunknown return ptr - exit E_POINTER"));
    return E_POINTER;
  }
  if(IsBadReadPtr(ppCMSPCall,sizeof(T *))) {
    LOG((MSP_ERROR,"CreateMSPCallHelper - bad class return ptr - exit E_POINTER"));
    return E_POINTER;
  }
  if(! pCMSPAddress->IsValidSetOfMediaTypes(dwMediaType,pCMSPAddress->GetCallMediaTypes())) {
    LOG((MSP_ERROR,"CreateMSPCallHelper - unsupported media types - exit TAPI_E_INVALIDMEDIATYPE"));
    return TAPI_E_INVALIDMEDIATYPE;
  }
  CComAggObject<T> *pCall;
  pCall = new CComAggObject<T>(pOuterUnknown);
  if(!pCall) {
    LOG((MSP_ERROR,"CreateMSPCallHelper - could not create agg call instance - exit E_OUTOFMEMORY"));
    return E_OUTOFMEMORY;
  }
  hr = pCall->QueryInterface(IID_IUnknown,(void **)&pUnknown);
  if(FAILED(hr)) {
    LOG((MSP_ERROR,"CreateMSPCallHelper - QueryInterface failed: %x",hr));
    delete pCall;
    return hr;
  }
  hr = pCall->FinalConstruct();
  if(FAILED(hr)) {
    LOG((MSP_ERROR,"CreateMSPCallHelper - FinalConstruct failed: %x.",hr));
    pUnknown->Release();
    return hr;
  }
  pMSPCall = dynamic_cast<T *>(&(pCall->m_contained));
  if(!pMSPCall) {
    LOG((MSP_ERROR,"CreateMSPCallHelper - can not cast to agg object to class pointer - exit E_UNEXPECTED"));
    pUnknown->Release();
    return E_UNEXPECTED;
  }
  hr = pMSPCall->Init(pCMSPAddress,htCall,dwReserved,dwMediaType);
  if(FAILED(hr)) {
    LOG((MSP_ERROR,"CreateMSPCallHelper - call init failed: %x",hr));
    pUnknown->Release();
    return hr;
  }
  *ppMSPCall = pUnknown;
  *ppCMSPCall = pMSPCall;
  LOG((MSP_TRACE,"CreateMSPCallHelper - exit S_OK"));
  return hr;
}

template <class T> HRESULT ShutdownMSPCallHelper(IUnknown *pUnknown,T **ppCMSPCall)
{
  LOG((MSP_TRACE,"ShutdownMSPCallHelper - enter"));
  if(IsBadReadPtr(pUnknown,sizeof(IUnknown))) {
    LOG((MSP_ERROR,"ShutdownMSPCallHelper - bad IUnknown pointer - exit E_POINTER"));
    return E_POINTER;
  }
  if(IsBadWritePtr(ppCMSPCall,sizeof(T *))) {
    LOG((MSP_ERROR,"ShutdownMSPCallHelper - bad return pointer - exit E_POINTER"));
    return E_POINTER;
  }
  T *pMSPCall;
  CComAggObject<T> *pCall = dynamic_cast<CComAggObject<T> *> (pUnknown);
  if(!pCall) {
    LOG((MSP_ERROR,"ShutdownMSPCallHelper - can't cast unknown to agg object pointer - exit E_UNEXPECTED"));
    return E_UNEXPECTED;
  }
  pMSPCall = dynamic_cast<T *> (&(pCall->m_contained));
  if(!pMSPCall) {
    LOG((MSP_ERROR,"ShutdownMSPCallHelper - can't cast contained unknown to class pointer - exit E_UNEXPECTED"));
    return E_UNEXPECTED;
  }
  HRESULT hr = pMSPCall->ShutDown();
  if(FAILED(hr)) {
    LOG((MSP_ERROR,"ShutdownMSPCallHelper - ShutDownMSPCall failed: %x",hr));
    return hr;
  }
  *ppCMSPCall = pMSPCall;
  LOG((MSP_TRACE,"ShutdownMSPCallHelper - exit S_OK"));
  return S_OK;
}
#endif
