/*===================================================================
Microsoft Denali

Microsoft Confidential.
Copyright 1997 Microsoft Corporation. All Rights Reserved.

Component: Component Collection

File: Compcol.h

Owner: DmitryR

This is the Component Collection header file.

Component collection replaces:  (used in:)
COleVar, COleVarList            (HitObj, Session, Application)
CObjectCover                    (HitObj, Server, Session)
VariantLink HasTable            (Session, Application)
===================================================================*/

#ifndef COMPCOL_H
#define COMPCOL_H

/*===================================================================
  Special OLE stuff
===================================================================*/

#include "gip.h"

/*===================================================================
  Misc declarations
===================================================================*/

#include "hashing.h"
#include "idhash.h"
#include "dbllink.h"
#include "util.h"
#include "viperint.h"
#include "memcls.h"

// Forward declarations
class CHitObj;
class CAppln;
class CSession;
class CScriptingContext;

// Component Types
#define CompType    DWORD
#define ctUnknown   0x00000000  // (Used as UnInitialized state)
#define ctTagged    0x00000001  // Created by <OBJECT ...> tag
#define ctProperty  0x00000002  // Created with Session("xxx") =
#define ctUnnamed   0x00000004  // Created with Server.CreateObject()

// Scope levels 
#define CompScope   DWORD
#define csUnknown   0x00000000
#define csAppln     0x00000001
#define csSession   0x00000002
#define csPage      0x00000004

// COM threading models
#define CompModel   DWORD
#define cmUnknown   0x00000000
#define cmSingle    0x00000001
#define cmApartment 0x00000002
#define cmFree      0x00000004
#define cmBoth      0x00000008

/*===================================================================
  Utility Functions Prototypes
===================================================================*/

HRESULT CompModelFromCLSID
    (
    const CLSID &ClsId, 
    CompModel *pcmModel = NULL, 
    BOOL *pfInProc = NULL
    );

BOOL FIsIntrinsic(IDispatch *pdisp);

inline BOOL FIsIntrinsic(VARIANT *pVar)
    {
    if (V_VT(pVar) != VT_DISPATCH)
        return FALSE;
    return FIsIntrinsic(V_DISPATCH(pVar));
    }

/*===================================================================
  OnPageInfo struct used to cache ids of OnStartPage()/OnEndPage()
===================================================================*/

#define ONPAGEINFO_ONSTARTPAGE      0
#define ONPAGEINFO_ONENDPAGE        1
#define ONPAGE_METHODS_MAX          ONPAGEINFO_ONENDPAGE+1

struct COnPageInfo
    {
    DISPID m_rgDispIds[ONPAGE_METHODS_MAX];

    BOOL FHasAnyMethod() const;
    };

inline BOOL COnPageInfo::FHasAnyMethod() const
    {
#if (ONPAGE_METHODS_MAX == 2)
    // fast implementation for the real case
    return
        (
        m_rgDispIds[0] != DISPID_UNKNOWN ||
        m_rgDispIds[1] != DISPID_UNKNOWN
        );
#else
    for (int i = 0; i < ONPAGE_METHODS_MAX; i++)
        {
        if (m_rgDispIds[i] != DISPID_UNKNOWN)
            return TRUE;
        }
    return FALSE;
#endif
    }

/*===================================================================
  Component object stores information about a single object
  Each component object belongs to a component collection
  Component objects are linked into a list, also
  tagged objects are hashed by name, and
  properties are hashed by name, and
  all instantiated objects are hashed by IUnknown*
===================================================================*/
class CComponentObject : public CLinkElem
    {

friend class CComponentCollection;
friend class CPageComponentManager;
friend class CComponentIterator;

private:
    // properties
	CompScope   m_csScope : 4;	// Scope
    CompType    m_ctType  : 4;  // Component Object Type
	CompModel	m_cmModel : 4;  // Threading behavior (from Registry)

	DWORD       m_fAgile : 1;   // Agile?

	// flag to indicate if OnPageInfo was queried
	DWORD       m_fOnPageInfoCached : 1;
	// flag: on-start-page done, waiting to do on-end-page
	DWORD       m_fOnPageStarted : 1;

	// flag to avoid multiple unsuccessful attempts to instantiate
	DWORD       m_fFailedToInstantiate : 1;
	// flag to mark instantiated (or tried to inst.) tagged objects
	DWORD       m_fInstantiatedTagged : 1;

	// flag to mark the object in pointer cache
	DWORD       m_fInPtrCache : 1;

    // variant filled with value?
	DWORD       m_fVariant : 1;

    // name was allocated (longer than the default buffer)?
	DWORD       m_fNameAllocated : 1;

	// pointers to object and type info
	IDispatch   *m_pDisp;		// Dispatch interface pointer
	IUnknown    *m_pUnknown;	// IUnknown interface pointer

    union
    {
	CLSID		m_ClsId;	// Class id (for tagged and unnamed)
	VARIANT     m_Variant;  // Variant (for properties)
    };
    
	// For objects that use OLE cookie API
	DWORD       m_dwGIPCookie;

	// cached OnPageInfo
	COnPageInfo m_OnPageInfo;

	// pointer to connect objects into link list
	CComponentObject *m_pCompNext;  // Next object in the link list.
    CComponentObject *m_pCompPrev;  // Prev object in the link list.

    // buffer for names that fit in (36 bytes = 17 unicode chars + '\0')
	BYTE        m_rgbNameBuffer[36];

private:
    // constructor is private! (not for outside use)
    CComponentObject
        (
        CompScope csScope, 
        CompType  ctType,
        CompModel cmModel
        );
    ~CComponentObject();

    // Initializes CLinkElem portion
    HRESULT Init(LPWSTR pwszName, DWORD cbName);

    // Releases all interface pointers (used by clear)
    HRESULT ReleaseAll();

    // Clears out data (releases all) leaving link alone
    HRESULT Clear();
    
    // Create instance if not there already
	HRESULT Instantiate(CHitObj *pHitObj);
	HRESULT TryInstantiate(CHitObj *pHitObj);
	
    // Set value from variant
    HRESULT SetPropertyValue(VARIANT *);

    // Convert Object to be GIP cookie
    HRESULT ConvertToGIPCookie();
    
    // Get and cache the ids for OnStart methods
    HRESULT GetOnPageInfo();

public:
    // functions to get the COM object (internally resolve cookies)
    HRESULT GetAddRefdIDispatch(IDispatch **ppdisp);
    HRESULT GetAddRefdIUnknown(IUnknown **ppunk);
    HRESULT GetVariant(VARIANT *pVar);  // not for GIP cookies

    // Check if the unnamed page level object object 
    // can be removed without waiting till the end of request
    inline BOOL FEarlyReleaseAllowed() const;
    
    // public inlines to access the object's properties
    // these are the only methods available from outside
    inline LPWSTR GetName();
    
    inline CompScope GetScope() const;
    inline CompType  GetType()  const;
    inline CompModel GetModel() const;
    inline BOOL      FAgile()   const;

    // Retrieve the cached ids
    inline const COnPageInfo *GetCachedOnPageInfo() const;

public:
#ifdef DBG
	void AssertValid() const;
#else
	void AssertValid() const {}
#endif

    // Cache on per-class basis
    ACACHE_INCLASS_DEFINITIONS()
    };

inline LPWSTR CComponentObject::GetName()
    {
    return (LPWSTR)m_pKey; 
    }

inline CompScope CComponentObject::GetScope() const
    {
    return m_csScope;
    }
    
inline CompType CComponentObject::GetType() const
    {
    return m_ctType; 
    }

inline CompType CComponentObject::GetModel() const
    {
    return m_cmModel; 
    }

inline BOOL CComponentObject::FAgile() const
    {
    return m_fAgile;
    }

inline const COnPageInfo *CComponentObject::GetCachedOnPageInfo() const
    {
    return m_fOnPageInfoCached ? &m_OnPageInfo : NULL;
    }

inline BOOL CComponentObject::FEarlyReleaseAllowed() const
    {
    return (!m_fOnPageStarted   &&  // no need to do on-end-page
            !m_fInPtrCache      &&  // no need to search by pointer
            m_csScope == csPage &&  // page scoped
            m_ctType == ctUnnamed); // created with Server.CreateObject()
    }

/*===================================================================
  Component collection is a manager of various types of component
  objects:
    1) Tagged objects (<OBJECT...>) (instantiated or not)
    2) Session("xxx") and Application("xxx") properties
    3) Unnamed objects (Server.CreateObject())
  It hashes added objects as needed (some by name, IUnkn *, etc.)

  The idea is to isolate the above issues from outside as much
  as possible.

  Component collections exist under session, application, hitobj
===================================================================*/
class CComponentCollection
    {
    
friend class CPageComponentManager;
friend class CComponentIterator;
friend class CVariantsIterator;

private:
    CompScope m_csScope : 4;          // scope (page, session, appln)
    DWORD     m_fUseTaggedArray : 1;  // remember tagged objects array?
    DWORD     m_fUsePropArray   : 1;  // remember properties array?
    DWORD     m_fHasComProperties : 1; // any property VARIANTs that could be objects
    
    // hash table (by name) of tagged objects
    CHashTableStr m_htTaggedObjects;     
    
    // hash table (by name) of properties (4)
    CHashTableStr m_htProperties;
    
    // hash table (by IUnknown *) of all instances
    CIdHashTable m_htidIUnknownPtrs;

    // Pointer to the component objects link list
	CComponentObject *m_pCompFirst;  // First object in link list.

	// Array of pointers to static objects to speed lookup by index
	CPtrArray m_rgpvTaggedObjects;

	// Array of pointers to properties to speed lookup by index
	CPtrArray m_rgpvProperties;

    // Various object counts in the collection
    USHORT m_cAllTagged;         // all tagged objects
    USHORT m_cInstTagged;        // instanciated tagged objects
    USHORT m_cProperties;        // all properties
    USHORT m_cUnnamed;           // number of unnamed objects
    
    // Add/remove object to the component objects link list
    HRESULT AddComponentToList(CComponentObject *pObj);
    HRESULT RemoveComponentFromList(CComponentObject *pObj);
    
    // Add named object to the proper hash table by name
    HRESULT AddComponentToNameHash
        (
        CComponentObject *pObj, 
        LPWSTR pwszName,
        DWORD  cbName
        );
    
    // Add named object to the IUnkown * hash table
    HRESULT AddComponentToPtrHash(CComponentObject *pObj);

    // Find by name (for tagged)
    HRESULT FindComponentObjectByName
        (
        LPWSTR pwszName,
        DWORD  cbName,
        CComponentObject **ppObj
        );
        
    // Find by name (for properties)
    HRESULT FindComponentPropertyByName
        (
        LPWSTR pwszName, 
        DWORD  cbName,
        CComponentObject **ppObj
        );

    // Find by IUnknown*
    HRESULT FindComponentByIUnknownPtr
        (
        IUnknown *pUnk,
        CComponentObject **ppObj
        );

    // Fill in the arrays for access by index for the first time
    HRESULT StartUsingTaggedObjectsArray();
    HRESULT StartUsingPropertiesArray();

public:
    // Add various kinds of objects to the collection
    // They are also used by 
    //      CPageComponentManager AddScoped...()
    
    HRESULT AddTagged
        (
        LPWSTR pwszName, 
        const CLSID &clsid, 
        CompModel cmModel
        );
        
    HRESULT AddProperty
        (
        LPWSTR pwszName,
        VARIANT *pVariant,
        CComponentObject **ppObj = NULL
        );

    HRESULT AddUnnamed
        (
        const CLSID &clsid, 
        CompModel cmModel, 
        CComponentObject **ppObj
        );

    HRESULT GetTagged
        (
        LPWSTR pwszName,
        CComponentObject **ppObj
        );

    HRESULT GetProperty
        (
        LPWSTR pwszName,
        CComponentObject **ppObj
        );

    HRESULT GetNameByIndex
        (
        CompType ctType,
        int index,
        LPWSTR *ppwszName
        );

    HRESULT RemoveComponent(CComponentObject *pObj);
    
    HRESULT RemoveProperty(LPWSTR pwszName);
    
    HRESULT RemoveAllProperties();

    CComponentCollection();
    ~CComponentCollection();

    HRESULT Init(CompScope csScope);
    HRESULT UnInit();

    BOOL FHasStateInfo() const;    // TRUE when state-full
    BOOL FHasObjects() const;      // TRUE when contains objects

    DWORD GetPropertyCount() const;
    DWORD GetTaggedObjectCount() const;

public:
#ifdef DBG
	void AssertValid() const;
#else
	void AssertValid() const {}
#endif

    // Cache on per-class basis
    ACACHE_INCLASS_DEFINITIONS()
    };

inline BOOL CComponentCollection::FHasStateInfo() const
    {
    return ((m_cAllTagged + m_cProperties + m_cUnnamed) > 0);
    }

inline BOOL CComponentCollection::FHasObjects() const
    {
    return (m_cInstTagged > 0 || m_cUnnamed > 0 ||
            (m_cProperties > 0 && m_fHasComProperties));
    }

inline DWORD CComponentCollection::GetPropertyCount() const
    {
    return m_cProperties;
    }

inline DWORD CComponentCollection::GetTaggedObjectCount() const
    {
    return m_cAllTagged;
    }

inline HRESULT CComponentCollection::AddComponentToList
(
CComponentObject *pObj
)
    {
    pObj->m_pCompNext = m_pCompFirst;
    pObj->m_pCompPrev = NULL;
    if (m_pCompFirst)
        m_pCompFirst->m_pCompPrev = pObj;
    m_pCompFirst = pObj;
    return S_OK;
    }

inline HRESULT CComponentCollection::RemoveComponentFromList
(
CComponentObject *pObj
)
    {
    if (pObj->m_pCompPrev)
        pObj->m_pCompPrev->m_pCompNext = pObj->m_pCompNext;
    if (pObj->m_pCompNext)
        pObj->m_pCompNext->m_pCompPrev = pObj->m_pCompPrev;
    if (m_pCompFirst == pObj)
        m_pCompFirst = pObj->m_pCompNext;
    pObj->m_pCompPrev = pObj->m_pCompNext = NULL;
    return S_OK;
    }

/*===================================================================
  A page object controls calls to OnStartPage(), OnEndPage().
  Page objects are used by CPageComponentManager
  They are hashed using IDispatch * to avoid multiple OnStartPage()
  calls for the same object.
===================================================================*/
class CPageObject
    {

friend class CPageComponentManager;

private:
	IDispatch   *m_pDisp;		       // Dispatch interface pointer
	COnPageInfo  m_OnPageInfo;         // cached OnPageInfo

    DWORD        m_fStartPageCalled : 1;
    DWORD        m_fEndPageCalled : 1;
	
private: // the only access is using CPageComponentManager
    CPageObject();
    ~CPageObject();

    HRESULT	Init(IDispatch *pDisp, const COnPageInfo &OnPageInfo);

    // Invoke OnStartPage or OnEndPage
    HRESULT InvokeMethod
        (
        DWORD iMethod, 
        CScriptingContext *pContext, 
        CHitObj *pHitObj
        );
    HRESULT TryInvokeMethod     // used by InvokeMethod
        (                       // inside TRY CATCH
        DISPID     DispId,
        BOOL       fOnStart, 
        IDispatch *pDispContext,
        CHitObj   *pHitObj
        );

public:
#ifdef DBG
	void AssertValid() const;
#else
	void AssertValid() const {}
#endif

    // Cache on per-class basis
    ACACHE_INCLASS_DEFINITIONS()
    };
    
/*===================================================================
  Page component manager provides access to component collections
  for page, session, application level.
  It is associated with a HitObj.

  It also takes care of covering (OnStartPage(), OnEndPage()).
===================================================================*/
class CPageComponentManager
    {
private:
    // hashtable of page objects hashed by IDispatch *
    CIdHashTable m_htidPageObjects;

    // hit object (this page)
    CHitObj *m_pHitObj;

    // hash table iterator callbacks
    static IteratorCallbackCode DeletePageObjectCB(void *pvObj, void *, void *);
    static IteratorCallbackCode OnEndPageObjectCB(void *pvObj, void *pvHitObj, void *pvhr);

private:
    // collections related to page, session and application
    HRESULT GetPageCollection(CComponentCollection **ppCollection);
    HRESULT GetSessionCollection(CComponentCollection **ppCollection);
    HRESULT GetApplnCollection(CComponentCollection **ppCollection);
    
    HRESULT GetCollectionByScope
        (
        CompScope csScope, 
        CComponentCollection **ppCollection
        );

    // find objectc in any of the related collections 
    // (internal private method)
    HRESULT FindScopedComponentByName
        (
        CompScope csScope, 
        LPWSTR pwszName,
        DWORD  cbName,
        BOOL fProperty,
        CComponentObject **ppObj, 
        CComponentCollection **ppCollection = NULL
        );

    static HRESULT __stdcall InstantiateObjectFromMTA
        (
        void *pvObj,
        void *pvHitObj
        );

public:
    CPageComponentManager();
    ~CPageComponentManager();

    HRESULT Init(CHitObj *pHitObj);
    
    // OnStartPage processing for an object that need it
    // (OnEndPage is done for all objects at the end of page)
    HRESULT OnStartPage
        (
        CComponentObject  *pCompObj,
        CScriptingContext *pContext,
        const COnPageInfo *pOnPageInfo,
        BOOL *pfStarted
        );

    // request OnEndPage() for all objects that need it
    // (OnStartPage() is done on demand on per-object basis)
    HRESULT OnEndPageAllObjects();

    // Add various kinds of objects. Objects get added to the
    // right collection depending on scope argument
    
    HRESULT AddScopedTagged
        (
        CompScope csScope, 
        LPWSTR pwszName, 
        const CLSID &clsid,
        CompModel cmModel
        );
        
    HRESULT AddScopedProperty
        (
        CompScope csScope, 
        LPWSTR pwszName, 
        VARIANT *pVariant,
        CComponentObject **ppObj = NULL
        );

    // Server.CreateObject
    HRESULT AddScopedUnnamedInstantiated
        (
        CompScope csScope, 
        const CLSID &clsid, 
        CompModel cmModel,
        COnPageInfo *pOnPageInfo,
        CComponentObject **ppObj
        );

    // Get component object (tagged) by name. 
    // Scope could be csUnknown
    HRESULT GetScopedObjectInstantiated
        (
        CompScope csScope, 
        LPWSTR pwszName, 
        DWORD  cbName,
        CComponentObject **ppObj,
        BOOL *pfNewInstance
        );

    // Get component property by name. Scope could be csUnknown
    HRESULT GetScopedProperty
        (
        CompScope csScope, 
        LPWSTR pwszName, 
        CComponentObject **ppObj
        );

    // Find component by IUnknown * (or IDispatch *).
    HRESULT FindAnyScopeComponentByIUnknown
        (
        IUnknown *pUnk, 
        CComponentObject **ppObj
        );
    HRESULT FindAnyScopeComponentByIDispatch
        (
        IDispatch *pDisp, 
        CComponentObject **ppObj
        );
    // The same - but static - gets context from Viper
    static HRESULT FindComponentWithoutContext
        (
        IDispatch *pDisp, 
        CComponentObject **ppObj
        );

    // Remove component -- the early release logic
    HRESULT RemoveComponent(CComponentObject *pObj);

public:
#ifdef DBG
	void AssertValid() const;
#else
	void AssertValid() const {}
#endif

    // Cache on per-class basis
    ACACHE_INCLASS_DEFINITIONS()
    };

// Component iterator is used to go through component names
// all the HitObj - reletated object across collections
// Needed for scripting

class CComponentIterator
    {
private:
    CHitObj *m_pHitObj;
    
    DWORD     m_fInited : 1;
    DWORD     m_fFinished : 1;
    
    CompScope m_csLastScope : 4;
    
    CComponentObject *m_pLastObj;

public:    
    CComponentIterator(CHitObj *pHitObj = NULL);
    ~CComponentIterator();

    HRESULT Init(CHitObj *pHitObj);
    LPWSTR  WStrNextComponentName();
    };

 // Variant Iterator is used to go through Property or Tagged object
 // names in a component collection. Needed for scripting

class CVariantsIterator : public IEnumVARIANT
	{
public:
	CVariantsIterator(CAppln *, DWORD);
	CVariantsIterator(CSession *, DWORD);
	~CVariantsIterator();

	HRESULT Init();

	// The Big Three

	STDMETHODIMP			QueryInterface(const GUID &, void **);
	STDMETHODIMP_(ULONG)	AddRef();
	STDMETHODIMP_(ULONG)	Release();

	// standard methods for iterators

	STDMETHODIMP	Clone(IEnumVARIANT **ppEnumReturn);
	STDMETHODIMP	Next(unsigned long cElements, VARIANT *rgVariant, unsigned long *pcElementsFetched);
	STDMETHODIMP	Skip(unsigned long cElements);
	STDMETHODIMP	Reset();

private:
	ULONG m_cRefs;							// reference count
	CComponentCollection 	*m_pCompColl;	// collection we are iterating over
	DWORD					m_dwIndex;		// current position for iteration
	CAppln					*m_pAppln;		// application (to clone iterator and Lock())
	CSession				*m_pSession;	// session (to clone iterator)
	DWORD					m_ctColType;	// type of collection
	
    // Cache on per-class basis
    ACACHE_INCLASS_DEFINITIONS()
	};

#endif // COMPCOL_H