//
// Copyright 1997 - Microsoft
//

//
// QI.H - Handles the query interface functions
//

#ifndef _QI_H_
#define _QI_H_

///////////////////////////////////////
//  
// QueryInterface Definitions
//
typedef struct {
    LPUNKNOWN          pvtbl;   // "punk" - pointer to a specific interface
    const struct _GUID *riid;   // GUID of interfaace

#ifdef DEBUG
    LPCTSTR pszName;    // Text name of interface - used to make sure tables are consistant
    DWORD   cFunctions; // Number of function entries in this interface's vtbl.
#endif // DEBUG

} QITABLE, *LPQITABLE, QIENTRY, *LPQIENTRY;

///////////////////////////////////////
//
// Quick-Lookup table declaration macro
//
#define DECLARE_QITABLE( _Class) QITABLE _QITable[ARRAYSIZE(QIT_##_Class)];

///////////////////////////////////////
//
// Quick-Lookup table construction macros
//
#ifdef DEBUG
#define DEFINE_QI( _iface, _name, _nFunctions ) \
    { NULL, &_iface, TEXT(#_name), _nFunctions },
#else // RETAIL
#define DEFINE_QI( _iface, _name, _nFunctions ) \
    { NULL, &_iface },
#endif // DEBUG

#define BEGIN_QITABLE( _Class ) \
    static const QITABLE QIT_##_Class[] = { DEFINE_QI( IID_IUnknown, IUnknown, 0 )

#define END_QITABLE  { NULL, NULL } };

///////////////////////////////////////
//
// Common Quick-Lookup QueryInterface( )
//
extern HRESULT
QueryInterface( 
    LPVOID    that,
    LPQITABLE pQI,
    REFIID    riid, 
    LPVOID   *ppv );

#ifdef DEBUG
///////////////////////////////////////
//
// BEGIN DEBUG 
//

#ifndef NO_TRACE_INTERFACES
///////////////////////////////////////
//
// BEGIN DEBUG INTERFACE TRACKING
//
#pragma message("BUILD: Interface tracking enabled")

///////////////////////////////////////
//
// Debug Quick-Lookup QI Interface Macros
//

// Begins construction of the runtime Quick-Lookup table.
// Adds IUnknown by default.
#define BEGIN_QITABLE_IMP( _Class, _IUnknownPrimaryInterface ) \
    int _i = 0; \
    CopyMemory( _QITable, &QIT_##_Class, sizeof( QIT_##_Class ) ); \
	_QITable[_i].pvtbl = (_IUnknownPrimaryInterface *) this;

// Checks that the QIENTRY matches the current QITABLE_IMP.
#define QITABLE_IMP( _Interface ) \
    _i++; \
    _QITable[_i].pvtbl = (_Interface *) this; \
{   int ___i = lstrcmp( TEXT(#_Interface), _QITable[_i].pszName ); \
    AssertMsg( ___i == 0, \
        "DEFINE_QIs and QITABLE_IMPs don't match. Incorrect order.\n" ); }

// Verifies that the number of entries in the QITABLE match
// the number of QITABLE_IMP in the runtime section
#define END_QITABLE_IMP( _Class ) \
    AssertMsg( _i == ( ARRAYSIZE( QIT_##_Class ) - 2 ), \
        "The number of DEFINE_QIs and QITABLE_IMPs don't match.\n" ); \
    LPVOID pCITracker; \
    TraceMsgDo( pCITracker = CITracker_CreateInstance( _QITable ), "0x%08x" );

///////////////////////////////////////
//
// CITracker Structures
//
typedef HRESULT (CALLBACK *LPFNQUERYINTERFACE)(
    LPUNKNOWN punk, 
    REFIID    riid, 
    LPVOID*   ppv );

typedef ULONG (CALLBACK *LPFNADDREF)(
    LPUNKNOWN punk );

typedef ULONG (CALLBACK *LPFNRELEASE)( 
    LPUNKNOWN punk );

typedef struct __vtbl {
    LPFNQUERYINTERFACE lpfnQueryInterface;
    LPFNADDREF         lpfnAddRef;
    LPFNRELEASE        lpfnRelease;
} VTBL, *LPVTBL;

typedef struct __vtbl2 {
    LPCTSTR            pszInterface;
    UINT               cRef;
    LPUNKNOWN          punk;
    LPVTBL             pOrginalVtbl;
    LPUNKNOWN          pITracker;
    // These must be last and in this order QI, AddRef, Release.
    LPFNQUERYINTERFACE lpfnQueryInterface;
    LPFNADDREF         lpfnAddRef;
    LPFNRELEASE        lpfnRelease;
} VTBL2, *LPVTBL2;

#define VTBL2OFFSET ( sizeof( VTBL2 ) - ( 3 * sizeof(LPVOID) ) )

///////////////////////////////////////
//
// CITracker Functions
//
LPVOID
CITracker_CreateInstance( 
    LPQITABLE pQITable );   // QI Table of the object
    
///////////////////////////////////////
//
// CCITracker Class
//
//
class
CITracker:
    public IUnknown
{
private: // Members
    VTBL2 _vtbl;

private: // Methods
    CITracker( );
    ~CITracker( );
    STDMETHOD(Init)( LPQITABLE pQITable );

public: // Methods
    friend LPVOID CITracker_CreateInstance( LPQITABLE pQITable );

    // IUnknown (Translates to IUnknown2)
    STDMETHOD(QueryInterface)( REFIID riid, LPVOID *ppv );
    STDMETHOD_(ULONG, AddRef)(void);
    STDMETHOD_(ULONG, Release)(void);

    // IUnknown2 (Real Implementation)
    STDMETHOD(_QueryInterface)( REFIID riid, LPVOID *ppv );
    STDMETHOD_(ULONG, _AddRef)(void);
    STDMETHOD_(ULONG, _Release)(void);
};

typedef CITracker* LPITRACKER;

//
// END DEBUG INTERFACE TRACKING
//
///////////////////////////////////////
#else // !NO_TRACE_INTERFACES
///////////////////////////////////////
//
// BEGIN DEBUG WITHOUT INTERFACE TRACKING
//

// Begins construction of the runtime Quick-Lookup table.
// Adds IUnknown by default.
#define BEGIN_QITABLE_IMP( _Class, _IUnknownPrimaryInterface ) \
    int _i = 0; \
    LPVOID pCITracker; \
    CopyMemory( _QITable, &QIT_##_Class, sizeof( QIT_##_Class ) ); \
	_QITable[_i].pvtbl = (_IUnknownPrimaryInterface *) this;

// Adds a CITracker to interface and checks that the QIENRTY
// matches the current QITABLE_IMP.
#define QITABLE_IMP( _Interface ) \
    _i++; \
    _QITable[_i].pvtbl = (_Interface *) this; \
{   int ___i = lstrcmp( TEXT(#_Interface), _QITable[_i].pszName ); \
    AssertMsg( ___i == 0, \
        "DEFINE_QIs and QITABLE_IMPs don't match. Incorrect order.\n" ); }

// Verifies that the number of entries in the QITABLE match
// the number of QITABLE_IMP in the runtime section
#define END_QITABLE_IMP( _Class )\
    AssertMsg( _i == ( ARRAYSIZE( QIT_##_Class ) - 2 ), \
        "The number of DEFINE_QIs and QITABLE_IMPs don't match.\n" );

//
// END DEBUG INTERFACE TRACKING
//
///////////////////////////////////////
#endif // NO_TRACE_INTERFACES

#else
///////////////////////////////////////
//
// BEGIN RETAIL
//

//
// Debug Macros -> Retail Code
//
#define BEGIN_QITABLE_IMP( _Class, _IUnknownPrimaryInterface ) \
    int _i = 0; \
    CopyMemory( _QITable, &QIT_##_Class, sizeof( QIT_##_Class ) ); \
	_QITable[_i++].pvtbl = (_IUnknownPrimaryInterface *) this;

#define QITABLE_IMP( _Interface ) \
    _QITable[_i++].pvtbl = (_Interface *) this;

#define END_QITABLE_IMP( _Class )


//
// END RETAIL
//
///////////////////////////////////////
#endif // DEBUG

#endif _QI_H_