/*
    ReactOS Kernel-Mode COM
    IUnknown implementations

    This file is in the public domain.

    AUTHORS
        Andrew Greenwood
*/

#ifndef STDUNK_H
#define STDUNK_H

#include <punknown.h>

/* ===============================================================
    INonDelegatingUnknown interface
*/

DECLARE_INTERFACE(INonDelegatingUnknown)
{
    STDMETHOD_(NTSTATUS, NonDelegatingQueryInterface)( THIS_
        IN  REFIID,
        OUT PVOID*) PURE;

    STDMETHOD_(ULONG, NonDelegatingAddRef)( THIS ) PURE;
    STDMETHOD_(ULONG, NonDelegatingRelease)( THIS ) PURE;
};

typedef INonDelegatingUnknown *PNONDELEGATINGUNKNOWN;


/* ===============================================================
    CUnknown declaration / definition

    There are 2 variants for this, and I'm not sure if the C
    version is correct.
*/

#ifdef __cplusplus

class CUnknown : public INonDelegatingUnknown
{
    private :
        LONG m_ref_count;
        PUNKNOWN m_outer_unknown;

    public :
        /* CUnknown */
        CUnknown(PUNKNOWN pUnknownOuter);
        virtual ~CUnknown();

        PUNKNOWN GetOuterUnknown()
        { return m_outer_unknown; }

        /* INonDelegatingUnknown */
        STDMETHODIMP_(ULONG) NonDelegatingAddRef();
        STDMETHODIMP_(ULONG) NonDelegatingRelease();

        STDMETHODIMP_(NTSTATUS) NonDelegatingQueryInterface(
            REFIID  rIID,
            PVOID* ppVoid);
};

#define DECLARE_STD_UNKNOWN() \
    STDMETHODIMP_(NTSTATUS) NonDelegatingQueryInterface( \
        REFIID iid, \
        PVOID* ppvObject); \
\
    STDMETHODIMP_(NTSTATUS) QueryInterface( \
        REFIID riid, \
        void** ppv) \
    { \
        return GetOuterUnknown()->QueryInterface(riid, ppv); \
    } \
\
    STDMETHODIMP_(ULONG) AddRef() \
    { \
        return GetOuterUnknown()->AddRef(); \
    } \
\
    STDMETHODIMP_(ULONG) Release() \
    { \
        return GetOuterUnknown()->Release(); \
    }

#define DEFINE_STD_CONSTRUCTOR(classname) \
    classname(PUNKNOWN outer_unknown) \
    : CUnknown(outer_unknown) \
    { }

#else   /* Not C++ - this is probably very buggy... */

NTSTATUS
STDMETHODCALLTYPE
Unknown_QueryInterface(
    IUnknown* this,
    IN  REFIID refiid,
    OUT PVOID* output);

ULONG
STDMETHODCALLTYPE
Unknown_AddRef(
    IUnknown* unknown_this);

ULONG
STDMETHODCALLTYPE
Unknown_Release(
    IUnknown* unknown_this);

typedef struct CUnknown
{
    __GNU_EXTENSION union
    {
        IUnknown IUnknown;
        INonDelegatingUnknown INonDelegatingUnknown;
    };

    LONG m_ref_count;
    PUNKNOWN m_outer_unknown;
} CUnknown;

#endif  /* __cplusplus */



#ifdef __cplusplus


/* ===============================================================
    Construction helpers
*/

#define QICAST(typename) \
    PVOID( (typename) (this) )

#define QICASTUNKNOWN(typename) \
    PVOID( PUNKNOWN( (typename) (this) ) )

#define STD_CREATE_BODY_WITH_TAG_(classname, unknown, outer_unknown, pool_type, tag, base) \
    classname *new_ptr = new(pool_type, tag) classname(outer_unknown); \
\
    if ( ! new_ptr ) \
        return STATUS_INSUFFICIENT_RESOURCES; \
\
    *unknown = PUNKNOWN((base)(new_ptr)); \
    (*unknown)->AddRef(); \
    return STATUS_SUCCESS

#define STD_CREATE_BODY_WITH_TAG(classname, unknown, outer_unknown, pool_type, tag, base) \
    STD_CREATE_BODY_WITH_TAG_(classname, unknown, outer_unknown, pool_type, tag, PUNKNOWN)

#define STD_CREATE_BODY_(classname, unknown, outer_unknown, pool_type, base) \
    STD_CREATE_BODY_WITH_TAG_(classname, unknown, outer_unknown, pool_type, 'rCcP', base)

#define STD_CREATE_BODY(classname, unknown, outer_unknown, pool_type) \
    STD_CREATE_BODY_(classname, unknown, outer_unknown, pool_type, PUNKNOWN)


/* ===============================================================
    Custom "new" and "delete" C++ operators
*/

#ifndef _NEW_DELETE_OPERATORS_
#define _NEW_DELETE_OPERATORS_

inline PVOID
KCOM_New(
    size_t size,
    POOL_TYPE pool_type,
    ULONG tag)
{
    PVOID result;

    result = ExAllocatePoolWithTag(pool_type, size, tag);

    if ( result )
        RtlZeroMemory(result, size);

    return result;
}

inline PVOID
operator new (
    size_t  size,
    POOL_TYPE pool_type)
{
    return KCOM_New(size, pool_type, 'wNcP');
}

inline PVOID
operator new (
    size_t size,
    POOL_TYPE pool_type,
    ULONG tag)
{
    return KCOM_New(size, pool_type, tag);
}

inline void __cdecl
operator delete(
    PVOID ptr)
{
    ExFreePool(ptr);
}

#endif  /* ALLOCATION_OPERATORS_DEFINED */


#else   /* Being compiled with C */


#endif  /* __cplusplus */

#endif  /* include guard */