/*
    Copyright (C) 1998 by Jorrit Tyberghein
    portions Copyright 1997, Don Box
  
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.
  
    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.
  
    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#ifndef _IMPUNK_H
#define _IMPUNK_H

#include <assert.h>
#include <stddef.h>
#include "cscom/comdefs.h"

// AUTO_LONG is just a long that constructs to zero
struct AUTO_LONG
{
  long value;
  AUTO_LONG () : value (0) {}
  inline long *operator & () { return &value; }
  inline long operator ++ () { return ++value; }
  inline long operator -- () { return --value; }
  inline operator long () { return value; }
};

// the AddRef/Release methods defined in this file
// all call functions called ModuleAddRef and ModuleRelease
// unconditionally. If you do not want the object's 
// outstanding references to hold the object running, 
// then add the following macro to your class definition

#define NO_MODULE_LOCK(ClassName) \
inline static void STDMETHODCALLTYPE ModuleAddRef () {} \
inline static void STDMETHODCALLTYPE ModuleRelease () {}

// Declaration for standard IUnknown implementation.
#define DECLARE_IUNKNOWN() \
protected: \
  AUTO_LONG m_cRef; \
public: \
  STDMETHOD (QueryInterface) (REFIID riid, void** ppv); \
  STDMETHOD_(ULong, AddRef) (); \
  STDMETHOD_(ULong, Release) ();

// non-thread safe version.

// de-facto IUnknown implementation.
#define IMPLEMENT_UNKNOWN(ClassName) \
STDMETHODIMP ClassName::QueryInterface (REFIID riid, void **ppv) \
{ \
  return InterfaceTableQueryInterface (this, GetInterfaceTable (), riid, ppv); \
} \
STDMETHODIMP_(ULong) ClassName::AddRef () \
{ \
  extern void STDAPICALLTYPE ModuleAddRef (); \
  if (m_cRef == 0) \
    ModuleAddRef (); \
  return ++m_cRef; \
} \
STDMETHODIMP_(ULong) ClassName::Release () \
{ \
  extern void STDAPICALLTYPE ModuleRelease (); \
  int result = --m_cRef; \
  if (m_cRef == 0) \
  { \
      CHK (delete this); \
      ModuleRelease (); \
  } \
  return result; \
}

#define IMPLEMENT_UNKNOWN_NODELETE(ClassName) \
STDMETHODIMP ClassName::QueryInterface (REFIID riid, void** ppv) \
{ \
  return InterfaceTableQueryInterface (this, GetInterfaceTable (), riid, ppv); \
} \
STDMETHODIMP_(ULong) ClassName::AddRef () \
{ \
  return ++m_cRef; \
} \
STDMETHODIMP_(ULong) ClassName::Release () \
{ \
  return --m_cRef; \
}

#define DECLARE_AGGREGATABLE_IUNKNOWN(ClassName) \
public: \
  AUTO_LONG m_cRef; \
  struct NonDelegatingUnknown : public IUnknown \
  { \
    ClassName *This() \
    { return (ClassName*)((unsigned char*)this - offsetof (ClassName, m_innerUnknown)); } \
    STDMETHOD (QueryInterface) (REFIID riid, void **ppv) \
    { return This()->InternalQueryInterface (riid, ppv); } \
    STDMETHOD_(ULong, AddRef) () \
    { return This()->InternalAddRef (); } \
    STDMETHOD_(ULong, Release) () \
    { return This()->InternalRelease (); } \
    NonDelegatingUnknown() \
    { This()->m_pUnkOuter = this; } \
  }; \
  NonDelegatingUnknown m_innerUnknown; \
  IUnknown *m_pUnkOuter; \
  IUnknown *GetControllingUnknown () { return m_pUnkOuter; } \
  IUnknown *GetNonDelegatingUnknown () { return &m_innerUnknown; } \
  STDMETHOD (InternalQueryInterface) (REFIID riid, void** ppv); \
  STDMETHOD_(ULong, InternalAddRef) (); \
  STDMETHOD_(ULong, InternalRelease) (); \
  STDMETHOD (QueryInterface) (REFIID riid, void** ppv); \
  STDMETHOD_(ULong, AddRef) (); \
  STDMETHOD_(ULong, Release) (); \
  STDMETHOD (ExternalQueryInterface) (REFIID riid, void** ppv); \
  STDMETHOD_(ULong, ExternalAddRef) (); \
  STDMETHOD_(ULong, ExternalRelease) ();

// Aggregatable version.
#define IMPLEMENT_AGGREGATABLE_UNKNOWN(ClassName) \
STDMETHODIMP ClassName::InternalQueryInterface (REFIID riid, void **ppv) \
{ \
  if (riid == IID_IUnknown)\
    return ((IUnknown*)(*ppv = STATIC_CAST(IUnknown*)(&m_innerUnknown)))->AddRef (), S_OK; \
  return InterfaceTableQueryInterface (this, GetInterfaceTable (), riid, ppv); \
} \
STDMETHODIMP_(ULong) ClassName::InternalAddRef () \
{ \
  extern void STDAPICALLTYPE ModuleAddRef (); \
  if (m_cRef == 0)\
    ModuleAddRef (); \
  return ++m_cRef; \
} \
STDMETHODIMP_(ULong) ClassName::InternalRelease () \
{ \
  extern void STDAPICALLTYPE ModuleRelease (); \
  ULong res = --m_cRef; \
  if (res == 0) \
  { \
    CHK (delete this); \
    ModuleRelease (); \
  } \
  return res; \
} \
STDMETHODIMP ClassName::QueryInterface (REFIID riid, void **ppv) \
{ return m_pUnkOuter->QueryInterface (riid, ppv); } \
STDMETHODIMP_(ULong) ClassName::AddRef () \
{ return m_pUnkOuter->AddRef (); } \
STDMETHODIMP_(ULong) ClassName::Release () \
{ return m_pUnkOuter->Release (); } \
STDMETHODIMP ClassName::ExternalQueryInterface (REFIID riid, void **ppv) \
{ return m_pUnkOuter->QueryInterface (riid, ppv); } \
STDMETHODIMP_(ULong) ClassName::ExternalAddRef () \
{ return m_pUnkOuter->AddRef (); } \
STDMETHODIMP_(ULong) ClassName::ExternalRelease () \
{ return m_pUnkOuter->Release (); }

// the standard version of CreateInstance () -- non aggregatable.
#define IMPLEMENT_CREATE_INSTANCE(ClassName) \
static HRESULT STDAPICALLTYPE \
CreateInstance (IUnknown *pUnkOuter, REFIID riid, void **ppv) \
{ \
  *ppv = 0; \
  if (pUnkOuter) \
    return CLASS_E_NOAGGREGATION; \
  CHK (ClassName *p = new ClassName); \
  if (!p)\
    return E_OUTOFMEMORY; \
  p->AddRef (); \
  HRESULT hr = p->QueryInterface (riid, ppv); \
  p->Release (); \
  return hr; \
}

#endif

#define IMPLEMENT_COMPOSITE_UNKNOWN(Outer, Inner) \
STDMETHODIMP I##Inner::QueryInterface (REFIID riid, void** ppv) \
{ METHOD_PROLOGUE(Outer, Inner) return pThis->QueryInterface (riid, ppv); } \
STDMETHODIMP_(ULong) I##Inner::AddRef () \
{ METHOD_PROLOGUE(Outer, Inner) return pThis->AddRef (); } \
STDMETHODIMP_(ULong) I##Inner::Release () \
{ METHOD_PROLOGUE(Outer, Inner) return pThis->Release (); } 

#define IMPLEMENT_COMPOSITE_UNKNOWN_AS_EMBEDDED(Outer, Inner) \
STDMETHODIMP Outer::X##Inner::QueryInterface (REFIID riid, void** ppv) \
{ METHOD_PROLOGUE(Outer, Inner) return pThis->QueryInterface (riid, ppv); } \
STDMETHODIMP_(ULong) Outer::X##Inner::AddRef () \
{ METHOD_PROLOGUE(Outer, Inner) return pThis->AddRef (); } \
STDMETHODIMP_(ULong) Outer::X##Inner::Release () \
{ METHOD_PROLOGUE(Outer, Inner) return pThis->Release (); } 
