// MyCom.h

#ifndef __MYCOM_H
#define __MYCOM_H

#include "MyWindows.h"

#define RINOK(x) do { HRESULT __result_ = (x); if(__result_ != S_OK) return __result_; } while (false)

template <class T>
class CMyComPtr final
{
  T* _p;
public:
  // typedef T _PtrClass;
  CMyComPtr() { _p = NULL;}
  CMyComPtr(T* p) {if ((_p = p) != NULL) p->AddRef(); }
  CMyComPtr(const CMyComPtr<T>& lp)
  {
    if ((_p = lp._p) != NULL)
      _p->AddRef();
  }
  ~CMyComPtr() { if (_p) _p->Release(); }
  void Release() { if (_p) { _p->Release(); _p = NULL; } }
  operator T*() const {  return (T*)_p;  }
  // T& operator*() const {  return *_p; }
  T** operator&() { return &_p; }
  T* operator->() const { return _p; }
  T* operator=(T* p)
  {
    if (p == _p) return p; // self-assignment
    if (p != NULL)
      p->AddRef();
    if (_p)
      _p->Release();
    _p = p;
    return p;
  }
  T* operator=(const CMyComPtr<T>& lp) { if (this != &lp) *this = lp._p; return *this; }
  bool operator!() const { return (_p == NULL); }
  // bool operator==(T* pT) const {  return _p == pT; }
  // Compare two objects for equivalence
  void Attach(T* p2)
  {
    Release();
    _p = p2;
  }
  T* Detach()
  {
    T* pt = _p;
    _p = NULL;
    return pt;
  }
  #ifdef _WIN32
  HRESULT CoCreateInstance(REFCLSID rclsid, REFIID iid, LPUNKNOWN pUnkOuter = NULL, DWORD dwClsContext = CLSCTX_ALL)
  {
    return ::CoCreateInstance(rclsid, pUnkOuter, dwClsContext, iid, (void**)&_p);
  }
  #endif
  /*
  HRESULT CoCreateInstance(LPCOLESTR szProgID, LPUNKNOWN pUnkOuter = NULL, DWORD dwClsContext = CLSCTX_ALL)
  {
    CLSID clsid;
    HRESULT hr = CLSIDFromProgID(szProgID, &clsid);
    ATLASSERT(_p == NULL);
    if (SUCCEEDED(hr))
      hr = ::CoCreateInstance(clsid, pUnkOuter, dwClsContext, __uuidof(T), (void**)&_p);
    return hr;
  }
  */
  template <class Q>
  HRESULT QueryInterface(REFGUID iid, Q** pp) const
  {
    return _p->QueryInterface(iid, (void**)pp);
  }
};

//////////////////////////////////////////////////////////

class CMyComBSTR final
{
public:
  BSTR m_str;
  CMyComBSTR() { m_str = NULL; }
  CMyComBSTR(LPCOLESTR pSrc) {  m_str = ::SysAllocString(pSrc);  }
  // CMyComBSTR(int nSize) { m_str = ::SysAllocStringLen(NULL, nSize); }
  // CMyComBSTR(int nSize, LPCOLESTR sz) { m_str = ::SysAllocStringLen(sz, nSize);  }
  CMyComBSTR(const CMyComBSTR& src) { m_str = src.MyCopy(); }
  /*
  CMyComBSTR(REFGUID src)
  {
    LPOLESTR szGuid;
    StringFromCLSID(src, &szGuid);
    m_str = ::SysAllocString(szGuid);
    CoTaskMemFree(szGuid);
  }
  */
  ~CMyComBSTR() { ::SysFreeString(m_str); }
  CMyComBSTR& operator=(const CMyComBSTR& src)
  {
    if (m_str != src.m_str)
    {
      if (m_str)
        ::SysFreeString(m_str);
      m_str = src.MyCopy();
    }
    return *this;
  }
  CMyComBSTR& operator=(LPCOLESTR pSrc)
  {
    ::SysFreeString(m_str);
    m_str = ::SysAllocString(pSrc);
    return *this;
  }
  unsigned int Length() const { return ::SysStringLen(m_str); }
  operator BSTR() const { return m_str; }
  BSTR* operator&() { return &m_str; }
  BSTR MyCopy() const
  {
    int byteLen = ::SysStringByteLen(m_str);
    BSTR res = ::SysAllocStringByteLen(NULL, byteLen);
    memmove(res, m_str, byteLen);
    return res;
  }
  void Attach(BSTR src) {  m_str = src; }
  BSTR Detach()
  {
    BSTR s = m_str;
    m_str = NULL;
    return s;
  }
  void Empty()
  {
    ::SysFreeString(m_str);
    m_str = NULL;
  }
  bool operator!() const {  return (m_str == NULL); }
};


//////////////////////////////////////////////////////////

class CMyUnknownImp /*not_final*/
{
public:
  ULONG __m_RefCount;
  CMyUnknownImp(): __m_RefCount(0) {}
};

#define MY_QUERYINTERFACE_BEGIN STDMETHOD(QueryInterface) \
    (REFGUID iid, void **outObject) override {

#define MY_QUERYINTERFACE_ENTRY(i) if (iid == IID_ ## i) \
    { *outObject = (void *)(i *)this; AddRef(); return S_OK; }

#define MY_QUERYINTERFACE_END return E_NOINTERFACE; }

#define MY_ADDREF_RELEASE \
STDMETHOD_(ULONG, AddRef)() override { return ++__m_RefCount; } \
STDMETHOD_(ULONG, Release)() override { \
  if (--__m_RefCount != 0) return __m_RefCount; \
  delete this; return 0; }

#define MY_UNKNOWN_IMP_SPEC(i) \
  MY_QUERYINTERFACE_BEGIN \
  i \
  MY_QUERYINTERFACE_END \
  MY_ADDREF_RELEASE


#define MY_UNKNOWN_IMP STDMETHOD(QueryInterface)(REFGUID, void **) override { \
  MY_QUERYINTERFACE_END \
  MY_ADDREF_RELEASE

#define MY_UNKNOWN_IMP1(i) MY_UNKNOWN_IMP_SPEC( \
  MY_QUERYINTERFACE_ENTRY(i) \
  )

#define MY_UNKNOWN_IMP2(i1, i2) MY_UNKNOWN_IMP_SPEC( \
  MY_QUERYINTERFACE_ENTRY(i1) \
  MY_QUERYINTERFACE_ENTRY(i2) \
  )

#define MY_UNKNOWN_IMP3(i1, i2, i3) MY_UNKNOWN_IMP_SPEC( \
  MY_QUERYINTERFACE_ENTRY(i1) \
  MY_QUERYINTERFACE_ENTRY(i2) \
  MY_QUERYINTERFACE_ENTRY(i3) \
  )

#define MY_UNKNOWN_IMP4(i1, i2, i3, i4) MY_UNKNOWN_IMP_SPEC( \
  MY_QUERYINTERFACE_ENTRY(i1) \
  MY_QUERYINTERFACE_ENTRY(i2) \
  MY_QUERYINTERFACE_ENTRY(i3) \
  MY_QUERYINTERFACE_ENTRY(i4) \
  )

#define MY_UNKNOWN_IMP5(i1, i2, i3, i4, i5) MY_UNKNOWN_IMP_SPEC( \
  MY_QUERYINTERFACE_ENTRY(i1) \
  MY_QUERYINTERFACE_ENTRY(i2) \
  MY_QUERYINTERFACE_ENTRY(i3) \
  MY_QUERYINTERFACE_ENTRY(i4) \
  MY_QUERYINTERFACE_ENTRY(i5) \
  )

#endif
