/******************************************************************************
 *                                                                         
 *               (C) COPYRIGHT Pacific MindWorks, Inc. 2001-2005
 *                          All rights reserved.
 *
 *****************************************************************************/

#ifndef __NTLSTATECACHE_INCLUDED
#define __NTLSTATECACHE_INCLUDED

#include <atlstr.h>
#include <atlcoll.h>


// Forward declarations
class CCacheManager;

///////////////////////////////////////////////////////////////////////////////
//
//	Comparison helper functions
//

inline bool CompareDoubles(double val1, double val2, int nPrecision)
{
	HRESULT hr = S_OK;
	double dRoundedVal1 = val1;
	double dRoundedVal2 = val2;
	hr = ::VarR8Round(val1, nPrecision, &dRoundedVal1);
	hr = ::VarR8Round(val2, nPrecision, &dRoundedVal2);
	ATLASSERT(SUCCEEDED(hr));

	return dRoundedVal1 == dRoundedVal2;
}

inline bool CompareBSTRs(BSTR val1, BSTR val2)
{
	return VarBstrCmp(val1, val2, LOCALE_USER_DEFAULT, 0) == VARCMP_EQ;
}

inline bool CompareVariants(VARIANT val1, VARIANT val2, int nPrecision)
{
	bool bCompare = false;
	ATLASSERT(val1.vt == val2.vt);
	if (val1.vt == val2.vt)
	{
		switch (val1.vt)
		{
			case VT_BOOL:
				bCompare = val1.boolVal == val2.boolVal;
				break;
			case VT_BSTR:
				bCompare = CompareBSTRs(val1.bstrVal, val2.bstrVal);
				break;
			case VT_I8:
			case VT_I4:
			case VT_I2:
			case VT_UI1:
			case VT_R4:
				bCompare = CompareDoubles(val1.fltVal, val2.fltVal, nPrecision);
				break;
			case VT_R8:
				bCompare = CompareDoubles(val1.dblVal, val2.dblVal, nPrecision);
				break;
			default:
				ATLASSERT(!"Unexpected data type.");
				break;
		}

	}

	return bCompare;
}

///////////////////////////////////////////////////////////////////////////////
//
//	CComparePolicy
//

template <typename T>
class CComparePolicy
	: public CModelBasedType
{
public:
	CComparePolicy(const CString& strModelOrFamily) 
		: CModelBasedType(strModelOrFamily) 
	{}

	virtual bool CompareValues(T val1, T val2) const = 0;
};

///////////////////////////////////////////////////////////////////////////////
//
//	CSimpleComparePolicyBase
//

template <typename T>
class CSimpleComparePolicyBase
	: public CComparePolicy<T>
{
public:
	CSimpleComparePolicyBase(BYTE nPrecision, const CString& strModelOrFamily = _T(""))
		: CComparePolicy<T>(strModelOrFamily), m_nPrecision(nPrecision)
	{}

	virtual bool CompareValues(T val1, T val2) const = 0;

public:
	BYTE GetPrecision() const { return m_nPrecision; }

protected:
	BYTE m_nPrecision;
};

///////////////////////////////////////////////////////////////////////////////
//
//	CSimpleComparePolicy
//

template <typename T>
class CSimpleComparePolicy
	: public CSimpleComparePolicyBase<T>
{
public:
	CSimpleComparePolicy(BYTE nPrecision, const CString& strModelOrFamily = _T(""))
		: CSimpleComparePolicyBase<T>(nPrecision, strModelOrFamily)
	{}

	virtual bool CompareValues(T val1, T val2) const { return val1 == val2; }
};

// Specializations for supported data types
template <> 
class CSimpleComparePolicy<bool>
	: public CSimpleComparePolicyBase<bool>
{
public:
	CSimpleComparePolicy(BYTE nPrecision, const CString& strModelOrFamily = _T(""))
		: CSimpleComparePolicyBase<bool>(nPrecision, strModelOrFamily)
	{}

	virtual bool CompareValues(bool val1, bool val2) const { return val1 == val2; }
};

template <>
class CSimpleComparePolicy<short>
	: public CSimpleComparePolicyBase<short>
{
public:
	CSimpleComparePolicy(BYTE nPrecision, const CString& strModelOrFamily = _T(""))
		: CSimpleComparePolicyBase<short>(nPrecision, strModelOrFamily)
	{}

	virtual bool CompareValues(short val1, short val2) const { return val1 == val2; }
};

template <>
class CSimpleComparePolicy<int>
	: public CSimpleComparePolicyBase<int>
{
public:
	CSimpleComparePolicy(BYTE nPrecision, const CString& strModelOrFamily = _T(""))
		: CSimpleComparePolicyBase<int>(nPrecision, strModelOrFamily)
	{}

	virtual bool CompareValues(int val1, int val2) const { return val1 == val2; }
};

template <>
class CSimpleComparePolicy<long>
	: public CSimpleComparePolicyBase<long>
{
public:
	CSimpleComparePolicy(BYTE nPrecision, const CString& strModelOrFamily = _T(""))
		: CSimpleComparePolicyBase<long>(nPrecision, strModelOrFamily)
	{}

	virtual bool CompareValues(long val1, long val2) const { return val1 == val2; }
};

template <> 
class CSimpleComparePolicy<float>
	: public CSimpleComparePolicyBase<float>
{
public:
	CSimpleComparePolicy(BYTE nPrecision, const CString& strModelOrFamily = _T(""))
		: CSimpleComparePolicyBase<float>(nPrecision, strModelOrFamily)
	{}

	virtual bool CompareValues(float val1, float val2) const { return CompareDoubles(val1, val2, m_nPrecision); }
};

template <> 
class CSimpleComparePolicy<double>
	: public CSimpleComparePolicyBase<double>
{
public:
	CSimpleComparePolicy(BYTE nPrecision, const CString& strModelOrFamily = _T(""))
		: CSimpleComparePolicyBase<double>(nPrecision, strModelOrFamily)
	{}

	virtual bool CompareValues(double val1, double val2) const { return CompareDoubles(val1, val2, m_nPrecision); }
};

template <> 
class CSimpleComparePolicy<VARIANT>
	: public CSimpleComparePolicyBase<VARIANT>
{
public:
	CSimpleComparePolicy(BYTE nPrecision, const CString& strModelOrFamily = _T(""))
		: CSimpleComparePolicyBase<VARIANT>(nPrecision, strModelOrFamily)
	{}

	virtual bool CompareValues(VARIANT val1, VARIANT val2) const { return CompareVariants(val1, val2, m_nPrecision); }
};

template <> 
class CSimpleComparePolicy<BSTR>
	: public CSimpleComparePolicyBase<BSTR>
{
public:
	CSimpleComparePolicy(BYTE nPrecision, const CString& strModelOrFamily = _T(""))
		: CSimpleComparePolicyBase<BSTR>(nPrecision, strModelOrFamily)
	{}

	virtual bool CompareValues(BSTR val1, BSTR val2) const { return CompareBSTRs(val1, val2); }
};

///////////////////////////////////////////////////////////////////////////////
//
//	CCustomComparePolicy
//

template <typename T>
class CCustomComparePolicy
	: public CComparePolicy<T>
{
public:
	CCustomComparePolicy(const CString& strModelOrFamily = _T(""))
		: CComparePolicy<T>(strModelOrFamily)
	{}

	virtual bool CompareValues(T val1, T val2) { return val1 == val2; }
};


///////////////////////////////////////////////////////////////////////////////
//
//	CComparePolicyManagerBase
//

class CComparePolicyManagerBase
{
};

///////////////////////////////////////////////////////////////////////////////
//
//	CComparePolicyManager
//

template <typename T>
class CComparePolicyManager 
	: public CComparePolicyManagerBase
{
public:
	CComparePolicyManager(CComparePolicy<T>* pFirstComparePolicy, ...);

	const CComparePolicy<T>* LookupComparePolicy(const CString& strModel) const;

	bool IsDefaultPolicyManager() const;

	static const CComparePolicyManager<T>* GetDefaultPolicyManager();			// Implemented in generated code

private:
	typedef CModelBasedMap< CComparePolicy<T> > ComparePolicyMap;				// key = model, value = CComparePolicy
	ComparePolicyMap m_mapComparePolicies;
};

template <typename T>
CComparePolicyManager<T>::CComparePolicyManager(CComparePolicy<T>* pFirstComparePolicy, ...)
{
	va_list argPtr;
	va_start(argPtr, pFirstComparePolicy);

	m_mapComparePolicies.BuildMap(pFirstComparePolicy, argPtr);

	va_end(argPtr);
}

template <typename T>
bool CComparePolicyManager<T>::IsDefaultPolicyManager() const
{
	return (this == GetDefaultPolicyManager());
}

template <typename T>
const CComparePolicy<T>* CComparePolicyManager<T>::LookupComparePolicy(const CString& strModel) const
{
	CComparePolicy<T>* pComparePolicy = NULL;
	m_mapComparePolicies.Lookup(strModel, pComparePolicy);

	return pComparePolicy;
}

///////////////////////////////////////////////////////////////////////////////
//
//	CComparePolicyManagerMapEntry
//

class CComparePolicyManagerMapEntry
{
public:
	CComparePolicyManagerMapEntry(const CString& strPropertyIdentifier, CComparePolicyManagerBase* pComparePolicyManager)
		: m_strPropertyIdentifier(strPropertyIdentifier), m_pComparePolicyManager(pComparePolicyManager)
	{
	}

	~CComparePolicyManagerMapEntry()
	{
		if (m_pComparePolicyManager != NULL)
		{
			delete m_pComparePolicyManager;
		}
	}

	const CString& GetPropertyIdentifier() const { return m_strPropertyIdentifier; }
	const CComparePolicyManagerBase* GetComparePolicyManager() const { return m_pComparePolicyManager; }

private:
	const CString& m_strPropertyIdentifier;
	const CComparePolicyManagerBase* m_pComparePolicyManager;
};

///////////////////////////////////////////////////////////////////////////////
//
//	CComparePolicyManagerMap
//

class CComparePolicyManagerMap
{
public:
	CComparePolicyManagerMap(CComparePolicyManagerMapEntry* pComparePolicyManagerMapEntry, ...);
	~CComparePolicyManagerMap();

	const CComparePolicyManagerBase* LookupComparePolicyManager(const CString& strPath) const;

private:
	CAtlMap<CString, CComparePolicyManagerMapEntry*> m_mapComparePolicyManagers;
};

inline CComparePolicyManagerMap::CComparePolicyManagerMap(CComparePolicyManagerMapEntry* pFirstComparePolicyManagerMapEntry, ...)
{
	va_list argPtr;
	va_start(argPtr, pFirstComparePolicyManagerMapEntry);

	CComparePolicyManagerMapEntry* pComparePolicyManagerMapEntry = pFirstComparePolicyManagerMapEntry;
	while (pComparePolicyManagerMapEntry != NULL)
	{
		CComparePolicyManagerMapEntry* pDummy = NULL;
		CString strPropertyIdentifier = pComparePolicyManagerMapEntry->GetPropertyIdentifier();
		if (!m_mapComparePolicyManagers.Lookup(strPropertyIdentifier, pDummy))
		{
			m_mapComparePolicyManagers[strPropertyIdentifier] = pComparePolicyManagerMapEntry;
		}

		pComparePolicyManagerMapEntry = va_arg(argPtr, CComparePolicyManagerMapEntry*);
	}

	va_end(argPtr);
}

inline CComparePolicyManagerMap::~CComparePolicyManagerMap()
{
	POSITION pos = m_mapComparePolicyManagers.GetStartPosition();
	while (pos != NULL)
	{
		delete m_mapComparePolicyManagers.GetNextValue(pos);
	}
}

inline const CComparePolicyManagerBase* CComparePolicyManagerMap::LookupComparePolicyManager(const CString& strPath) const
{
	// Format of cache entry path is: "Channels[0].Measurements[45].IAgilentMeasurement.Frequency"
	CString strPropertyIdentifier;
	CAtlArray<CString> rgTokens;
	int curPos = 0;
	CString strToken = strPath.Tokenize(_T("."), curPos);
	while (strToken != _T(""))
	{
		rgTokens.Add(strToken);
		strToken = strPath.Tokenize(_T("."), curPos);
	}

	size_t len = rgTokens.GetCount();
	if (len >= 2)
	{
		CString strInterface = rgTokens[len-2];
		CString strProperty = rgTokens[len-1];
		strPropertyIdentifier = strInterface + _T(".") + strProperty;
	}

	CComparePolicyManagerMapEntry* pComparePolicyManagerMapEntry = NULL;
	bool bFound = m_mapComparePolicyManagers.Lookup(strPropertyIdentifier, pComparePolicyManagerMapEntry);

	return bFound ? pComparePolicyManagerMapEntry->GetComparePolicyManager() : NULL;
}

///////////////////////////////////////////////////////////////////////////////
//
//	SimpleCacheEntryTraits
//

template <typename T>
class SimpleCacheEntryTraits
{
public:
	static T Copy(T val) { return val; }
	static void Delete(T val)
	{
		NTL_UNUSED(val);
	}
};

///////////////////////////////////////////////////////////////////////////////
//
//	DefaultCacheEntryTraits
//

template <typename T>
class DefaultCacheEntryTraits
{
public:
	static T Copy(T val);
	static void Delete(T val)
	{
		NTL_UNUSED(val);
	}
};

// Specializations for supported cache entry types
template <> class DefaultCacheEntryTraits<bool> 
	: public SimpleCacheEntryTraits<bool>
{
};

template <> class DefaultCacheEntryTraits<BYTE> 
	: public SimpleCacheEntryTraits<BYTE>
{
};

template <> class DefaultCacheEntryTraits<short> 
	: public SimpleCacheEntryTraits<short>
{
};

template <> class DefaultCacheEntryTraits<int> 
	: public SimpleCacheEntryTraits<int>
{
};

template <> class DefaultCacheEntryTraits<long> 
	: public SimpleCacheEntryTraits<long>
{
};

template <> class DefaultCacheEntryTraits<float> 
	: public SimpleCacheEntryTraits<float>
{
};

template <> class DefaultCacheEntryTraits<double> 
	: public SimpleCacheEntryTraits<double>
{
};

template <> class DefaultCacheEntryTraits<BSTR>
{
public:
	static BSTR Copy(BSTR val) { return ::SysAllocString(val); }
	static void Delete(BSTR val) { ::SysFreeString(val); }
};

template <> class DefaultCacheEntryTraits<VARIANT>
{
public:
	static VARIANT Copy(VARIANT val)
	{
		VARIANT varResult;
		CComVariant varDest = val;
		varDest.Detach(&varResult);

		return varResult;
	}

	static void Delete(VARIANT val) { ::VariantClear(&val); }
};


///////////////////////////////////////////////////////////////////////////////
//
//	CacheComparer
//

template <typename T>
class CacheComparer
{
public:
	CacheComparer(const CComparePolicyManager<T>* pComparePolicyManager = NULL)
		: m_pComparePolicyManager(pComparePolicyManager)
	{
		if (pComparePolicyManager == NULL)
		{
			m_pComparePolicyManager = CComparePolicyManager<T>::GetDefaultPolicyManager();
		}
	}

	~CacheComparer()
	{
		if ((m_pComparePolicyManager != NULL) && (!m_pComparePolicyManager->IsDefaultPolicyManager()))
		{
			delete m_pComparePolicyManager;
		}
	}

	bool CompareValues(const CString& strModel, T val1, T val2) const;

private:
	const CComparePolicyManager<T>* m_pComparePolicyManager;
};

template <typename T>
bool CacheComparer<T>::CompareValues(const CString& strModel, T val1, T val2) const
{
	bool bCompare = false;
	if (m_pComparePolicyManager != NULL)
	{
		const CComparePolicy<T>* pComparePolicy = m_pComparePolicyManager->LookupComparePolicy(strModel);
		if (pComparePolicy == NULL)
		{
			// No model-specific compare policy found, so use default compare policy
			CString strDefaultModel = _T("");
			pComparePolicy = m_pComparePolicyManager->LookupComparePolicy(strDefaultModel);
			ATLASSERT(pComparePolicy != NULL);
		}

		if (pComparePolicy != NULL)
		{
			bCompare = pComparePolicy->CompareValues(val1, val2);
		}
	}

	return bCompare;
}

///////////////////////////////////////////////////////////////////////////////
//
//	CCacheEntryBase
//

class CCacheEntryBase
{
public:
	CCacheEntryBase() { Invalidate(); }
	virtual void Invalidate() { m_bValid = false; }
	virtual void SetValid() { m_bValid = true; }
	virtual bool IsValid() const { return m_bValid; }

private:
	bool m_bValid;
};


///////////////////////////////////////////////////////////////////////////////
//
//	CCacheEntry
//

template <typename T, class CacheEntryTraits = DefaultCacheEntryTraits<T> >
class CCacheEntry : 
	public CCacheEntryBase,
	public CacheComparer<T>,
	public CacheEntryTraits
{
public:
	CCacheEntry(const CComparePolicyManager<T>* pComparePolicyManager = NULL)
		: CacheComparer<T>(pComparePolicyManager) 
	{
		Invalidate();
	}

	virtual void Invalidate();

	bool GetValue(T* pValue) const;
	bool GetValue(T* pValue, CPerfMonManager* pPerfMonManager) const;
	bool ContainsValue(T value, const CString& strModel);
	bool ContainsValue(T value, const CString& strModel, CPerfMonManager* pPerfMonManager);
	void UpdateValue(T value);

private:
	T m_value;
};

template <typename T, class CacheEntryTraits>
void CCacheEntry<T, CacheEntryTraits>::Invalidate()
{
	if (IsValid())
	{
		Delete(m_value);
	}

	CCacheEntryBase::Invalidate();
}

template <typename T, class CacheEntryTraits>
bool CCacheEntry<T, CacheEntryTraits>::GetValue(T* pValue) const
{
	bool bValid = false;
	if (IsValid())
	{
		bValid = true;
		*pValue = Copy(m_value);
	}

	return bValid;
}

template <typename T, class CacheEntryTraits>
bool CCacheEntry<T, CacheEntryTraits>::GetValue(T* pValue, CPerfMonManager* pPerfMonManager) const
{
	// Record cache read
	pPerfMonManager->IncrementCachedPropertyCallCount();

	bool bValid = false;
	if (IsValid())
	{
		bValid = true;
		*pValue = Copy(m_value);

		// Record cache hit
		pPerfMonManager->IncrementCacheHitCount();
	}

	return bValid;
}

template <typename T, class CacheEntryTraits>
bool CCacheEntry<T, CacheEntryTraits>::ContainsValue(T value, const CString& strModel)
{
	bool bContainsValue = false;

	if (IsValid() && CompareValues(strModel, m_value, value))
	{
		bContainsValue = true;
	}

	return bContainsValue;
}

template <typename T, class CacheEntryTraits>
bool CCacheEntry<T, CacheEntryTraits>::ContainsValue(T value, const CString& strModel, CPerfMonManager* pPerfMonManager)
{
	bool bContainsValue = false;

	// Record cache read
	pPerfMonManager->IncrementCachedPropertyCallCount();

	if (IsValid() && CompareValues(strModel, m_value, value))
	{
		bContainsValue = true;

		// Record cache hit
		pPerfMonManager->IncrementCacheHitCount();
	}

	return bContainsValue;
}

template <typename T, class CacheEntryTraits>
void CCacheEntry<T, CacheEntryTraits>::UpdateValue(T value)
{
	if (IsValid())
	{
		Delete(m_value);
	}

	m_value = Copy(value);

	SetValid();
}

///////////////////////////////////////////////////////////////////////////////
//
//	CNoCacheEntry
//

template <typename T, class CacheEntryTraits = DefaultCacheEntryTraits<T> >
class CNoCacheEntry : 
	public CCacheEntryBase,
	public CacheComparer<T>,
	public CacheEntryTraits
{
public:
	CNoCacheEntry(const CComparePolicyManager<T>* pComparePolicyManager)
	{}

	virtual void Invalidate();

	bool GetValue(T* pValue) const { return false; }
	bool GetValue(T* pValue, CPerfMonManager* pPerfMonManager) const { return false; }
	bool ContainsValue(T value, const CString& strModel, CPerfMonManager* pPerfMonManager) { return false; }
	void UpdateValue(T value) {}
};

///////////////////////////////////////////////////////////////////////////////
//
//	CCacheManager
//

const CComparePolicyManagerBase* GetComparePolicyManager(const CString& strPath);		// Implemented in generated code

typedef CAtlMap<CString, CCacheEntryBase*>	CacheEntryMap;								// key = cache entry key, value = CCacheEntryBase

class CCacheManager
{
public:
	virtual ~CCacheManager();

	void InvalidateCache();
	void InvalidateCacheAll();
	void InvalidateCacheEntry(const CString& strPath);

	template <typename T>
	void InitializeCacheEntry(const CString& strKey, T value);

	template <typename T>
	void UpdateCacheEntry(const CString& strKey, T value);

	template <typename T>
	void GetCacheEntryValue(const CString& strKey, T* value);

	template <typename T>
	CCacheEntry<T>* GetOrCreateCacheEntry(const CString& strKey);

	CCacheEntryBase* GetCacheEntry(const CString& strPath);

	void DestroyAllCacheEntries();

protected:
	virtual CCacheManager* GetRepCapCacheManager(const CString& strRepCapName, long lRepCapIndex) = 0;	// Implemented in CObjectModelNode
	virtual CCacheManager* GetRootCacheManager() = 0;													// Implemented in CObjectModelNode
	virtual void InvalidateRepCapCache() = 0;															// Implemented in CObjectModelNode

private:
	CCacheEntryBase* GetCacheEntry(CAtlArray<CString>& rgRepCapSpecs, const CString& strKey);
	static const CString ParseCacheEntryPath(const CString& strPath, CAtlArray<CString>& rgRepCapSpecs);
	static long ParseRepCapSpec(const CString& strRepCapSpec, CString& strRepCapName);

private:
	CacheEntryMap m_mapCacheEntries;
};

inline CCacheManager::~CCacheManager()
{
	POSITION pos = m_mapCacheEntries.GetStartPosition();
	while (pos != NULL)
	{
		delete m_mapCacheEntries.GetNextValue(pos);
	}
}

inline void CCacheManager::InvalidateCacheAll()
{
	// Invalidates all cache data on all objects in the driver
	CCacheManager* pRootCacheManager = GetRootCacheManager();
	if (pRootCacheManager != NULL)
	{
		pRootCacheManager->InvalidateCache();
	}
}

inline void CCacheManager::InvalidateCache()
{
	// Invalidates all cache data on this object AND all child repcaps
	POSITION pos = m_mapCacheEntries.GetStartPosition();
	while (pos != NULL)
	{
		CCacheEntryBase* pEntry = m_mapCacheEntries.GetNextValue(pos);
		pEntry->Invalidate();
	}

	InvalidateRepCapCache();
}

inline void CCacheManager::InvalidateCacheEntry(const CString& strPath)
{
	// Invalidates a single cache entry
	CCacheEntryBase* pEntry = GetCacheEntry(strPath);
	if (pEntry != NULL)
	{
		pEntry->Invalidate();
	}
}

template <typename T>
void CCacheManager::InitializeCacheEntry(const CString& strKey, T value)
{
	CCacheEntryBase* pEntryBase = GetCacheEntry(strKey);
	ATLASSERT(pEntryBase == NULL);
	if (pEntryBase == NULL)
	{
		CCacheEntry<T>* pEntry = GetOrCreateCacheEntry<T>(strKey);
		ATLASSERT(pEntry != NULL);
		pEntry->UpdateValue(value);
	}
}

template <typename T>
void CCacheManager::UpdateCacheEntry(const CString& strKey, T value)
{
	CCacheEntryBase* pEntryBase = GetCacheEntry(strKey);
	ATLASSERT(pEntryBase != NULL);
	if (pEntryBase != NULL)
	{
		CCacheEntry<T>* pEntry = (CCacheEntry<T>*)(pEntryBase);
		pEntry->UpdateValue(value);
	}
}

template <typename T>
void CCacheManager::GetCacheEntryValue(const CString& strKey, T* value)
{
	CCacheEntryBase* pEntryBase = GetCacheEntry(strKey);
	ATLASSERT(pEntryBase != NULL);
	if (pEntryBase != NULL)
	{
		CCacheEntry<T>* pEntry = (CCacheEntry<T>*)(pEntryBase);
		pEntry->GetValue(value);
	}
}

template <typename T>
CCacheEntry<T>* CCacheManager::GetOrCreateCacheEntry(const CString& strKey)
{
	// This function only looks in THIS object's cache
	CCacheEntry<T>* pEntry = NULL;
	CCacheEntryBase* pEntryBase = NULL;
	bool bFound = m_mapCacheEntries.Lookup(strKey, pEntryBase);
	if (!bFound)
	{	
		// Entry has not been allocated, so allocate one
		const CComparePolicyManager<T>* pComparePolicyManager = static_cast<const CComparePolicyManager<T>*>(GetComparePolicyManager(strKey));
		pEntry = new CCacheEntry<T>(pComparePolicyManager);

		m_mapCacheEntries[strKey] = pEntry;
	}
	else
	{
		pEntry = static_cast<CCacheEntry<T>*>(pEntryBase);
	}

	return pEntry;
}

inline CCacheEntryBase* CCacheManager::GetCacheEntry(const CString& strPath)
{
	CAtlArray<CString> rgRepCapSpecs;
	CString strKey = ParseCacheEntryPath(strPath, rgRepCapSpecs);

	CCacheEntryBase* pCacheEntry = NULL;
	if (rgRepCapSpecs.GetCount() > 0)
	{
		// Get cache entry from another node in the object model
		if (rgRepCapSpecs[0].CompareNoCase(_T("Driver")) == 0)
		{
			// This is an absolute path => starts from the ROOT object
			CCacheManager* pRootCacheManager = GetRootCacheManager();
			if (pRootCacheManager != NULL)
			{
				CString strNewPath = strPath;
				strNewPath.Delete(0, 7);	// remove "Driver."
				pCacheEntry = pRootCacheManager->GetCacheEntry(strNewPath);
			}
		}
		else
		{
			// This is a relative path => starts from THIS object
			pCacheEntry = GetCacheEntry(rgRepCapSpecs, strKey);

			// NOTE: Can't reference rgRepCapSpecs after this point, because previous call has emptied it
		}
	}
	else
	{
		// Get cache entry from this object's cache map
		m_mapCacheEntries.Lookup(strKey, pCacheEntry);
	}

	return pCacheEntry;
}

inline CCacheEntryBase* CCacheManager::GetCacheEntry(CAtlArray<CString>& rgRepCapSpecs, const CString& strKey)
{
	// NOTE: This function will remove elements from the input array rgRepCapSpecs
	CCacheEntryBase* pCacheEntry = NULL;
	if (rgRepCapSpecs.GetCount() > 0)
	{
		// We're referencing a cache entry on a child repcap, so delegate to our immediate child's CCacheManager
		CString strRepCapSpec = rgRepCapSpecs[0];
		rgRepCapSpecs.RemoveAt(0);

		CString strRepCapName;
		long lRepCapIndex = ParseRepCapSpec(strRepCapSpec, strRepCapName);
		CCacheManager* pRepCapCacheManager = GetRepCapCacheManager(strRepCapName, lRepCapIndex);
		if (pRepCapCacheManager == NULL)
		{
			ATLASSERT(!"Invalid repeated capability name.");
		}
		else
		{
			pCacheEntry = pRepCapCacheManager->GetCacheEntry(rgRepCapSpecs, strKey);
		}
	}
	else
	{
		// We're referencing a cache entry on THIS repcap
		m_mapCacheEntries.Lookup(strKey, pCacheEntry);
	}

	return pCacheEntry;
}

inline const CString CCacheManager::ParseCacheEntryPath(const CString& strPathIn, CAtlArray<CString>& rgRepCapSpecs)
{
	// Format of cache entry path is: "Channels[0].Measurements[45].IAgilentMeasurement.Frequency"
	rgRepCapSpecs.RemoveAll();

	CString strPath = strPathIn;
	CString strKey;

	// Parse out the parts of the path
	CAtlArray<CString> rgParts;
	int nPos = 0;
	CString strToken = strPath.Tokenize(_T("."), nPos);
	while (strToken != _T(""))
	{
		rgParts.Add(strToken);
		strToken = strPath.Tokenize(_T("."), nPos);
	}

	if (rgParts.GetCount() < 2)
	{
		ATLASSERT(!"Invalid format for coupling specification.");
		return _T("");
	}
	else if (rgParts.GetCount() == 2)
	{
		// This is a simple path that references a cache entry on this object
		strKey = rgParts[0] + "." + rgParts[1];
	}
	else
	{
		// This is a complex path that references a cache entry on a child repcap
		
		// Strip off the cache entry key
		strKey = rgParts[rgParts.GetCount() - 2] + "." + rgParts[rgParts.GetCount() - 1];
		rgParts.RemoveAt(rgParts.GetCount() - 1);
		rgParts.RemoveAt(rgParts.GetCount() - 1);

		rgRepCapSpecs.InsertArrayAt(0, &rgParts);
	}

	return strKey;
}

inline long CCacheManager::ParseRepCapSpec(const CString& strRepCapSpecIn, CString& strRepCapName)
{
	// Format of strRepCapSpec is: "RepCapName[Index]"
	CString strRepCapSpec = strRepCapSpecIn;
	long lRepCapIndex = -1;
	strRepCapSpec.Trim();
	int lLeftBracketIndex = strRepCapSpec.Find('[');
	if (lLeftBracketIndex > 0)
	{
		strRepCapName = strRepCapSpec.Left(lLeftBracketIndex);
		CString strRepCapIndex = strRepCapSpec.Right(strRepCapSpec.GetLength() - lLeftBracketIndex - 1);
		strRepCapIndex.TrimRight(']');
		lRepCapIndex = _ttol(strRepCapIndex);
	}

	return lRepCapIndex;
}

inline void CCacheManager::DestroyAllCacheEntries()
{
	m_mapCacheEntries.RemoveAll();
}

///////////////////////////////////////////////////////////////////////////////
//
//	CCacheCoupling
//

class CCacheCoupling
	: public CModelBasedType
{
public:
	CCacheCoupling(const CString& strCacheEntryPath, const CString& strModelOrFamily = _T(""))	
		: CModelBasedType(strModelOrFamily), m_strCacheEntryPath(strCacheEntryPath), m_pCacheEntry(NULL)
	{}

	virtual void Invalidate(CCacheManager* pCacheManager)
	{
		ATLASSERT(pCacheManager != NULL);
		if (pCacheManager != NULL)
		{
			if (m_pCacheEntry == NULL)
			{
				// Cache entry hadn't yet been allocated last time we tried to invalidate it, so try again
				m_pCacheEntry = pCacheManager->GetCacheEntry(m_strCacheEntryPath);
			}

			if (m_pCacheEntry != NULL)
			{
				m_pCacheEntry->Invalidate();
			}
		}
	}

	virtual const CString& GetCacheEntryPath() const { return m_strCacheEntryPath; }

	virtual CCacheEntryBase* GetCacheEntry() const { return m_pCacheEntry; }
	virtual void SetCacheEntry(CCacheEntryBase* pCacheEntry) { m_pCacheEntry = pCacheEntry; }

	virtual bool ModelOverridesFamily(CModelBasedType* pFamilySpecificEntry) const 
	{ 
		bool modelOverridesFamily = true;
		CCacheCoupling* pFamilySpecificCoupling = dynamic_cast<CCacheCoupling*>(pFamilySpecificEntry);
		if (pFamilySpecificCoupling != NULL)
		{
			modelOverridesFamily = (m_strCacheEntryPath == pFamilySpecificCoupling->GetCacheEntryPath());
		}

		return modelOverridesFamily;
	}


protected:
	CString m_strCacheEntryPath;
	CCacheEntryBase* m_pCacheEntry;
};

///////////////////////////////////////////////////////////////////////////////
//
//	CSimpleCacheCoupling
//

class CSimpleCacheCoupling
	: public CCacheCoupling
{
public:
	CSimpleCacheCoupling(const CString& strCacheEntryPath, const CString& strModelOrFamily = _T(""))	
		: CCacheCoupling(strCacheEntryPath, strModelOrFamily)
	{}
};

///////////////////////////////////////////////////////////////////////////////
//
//	CNullCacheCoupling
//

class CNullCacheCoupling
	: public CCacheCoupling
{
public:
	CNullCacheCoupling(const CString& strCacheEntryPath, const CString& strModelOrFamily)	
		: CCacheCoupling(strCacheEntryPath, strModelOrFamily)
	{}

	virtual void Invalidate(CCacheManager* pCacheManager)
	{
		NTL_UNUSED(pCacheManager);

		// Do nothing
	}
};

///////////////////////////////////////////////////////////////////////////////
//
//	CCacheCouplingManager
//

class CCacheCouplingManager
{
public:
	CCacheCouplingManager(CCacheCoupling* pFirstCoupling, ...);

	void InvalidateCoupledEntries(const CString& strModel, CCacheManager* pCacheManager);

private:
	typedef CModelBasedMultiMap<CCacheCoupling> CouplingMap;		// key = model, value = CCacheCoupling
	CouplingMap m_mapCouplings;
};

inline CCacheCouplingManager::CCacheCouplingManager(CCacheCoupling* pFirstCoupling, ...)
{
	va_list argPtr;
	va_start(argPtr, pFirstCoupling);

	m_mapCouplings.BuildMap(pFirstCoupling, argPtr);

	va_end(argPtr);
}

inline void CCacheCouplingManager::InvalidateCoupledEntries(const CString& strModel, CCacheManager* pCacheManager)
{
	// Invalidate all model-specific coupled entries that match the current model
	CAtlMap<CString, CString> mapModelSpecificCouplings;	// key = cache entry path, value = cache entry path
	POSITION pos = m_mapCouplings.GetHeadPosition();
	while (pos != NULL)
	{
		CString strCurrentModel;
		CCacheCoupling* pCoupling = NULL;
		m_mapCouplings.GetNextAssoc(pos, strCurrentModel, pCoupling);
		CString strCacheEntryPath = pCoupling->GetCacheEntryPath();

		if (strCurrentModel == strModel)
		{
			pCoupling->Invalidate(pCacheManager);
			mapModelSpecificCouplings[strCacheEntryPath] = strCacheEntryPath;
		}
	}

	// Invalidate all coupled entries that didn't specify a model AND that don't have a model-specific coupling
	pos = m_mapCouplings.GetHeadPosition();
	while (pos != NULL)
	{
		CString strCurrentModel;
		CCacheCoupling* pCoupling = NULL;
		m_mapCouplings.GetNextAssoc(pos, strCurrentModel, pCoupling);
		CString strCacheEntryPath = pCoupling->GetCacheEntryPath();

		if (strCurrentModel.IsEmpty())
		{
			// Only invalidate according to the default coupling IF there is no other coupling for this same property for this model.
			// This allows a CNullCacheEntry to effectively "override" a coupling in the default policy.
			CString strDummy;
			if (!mapModelSpecificCouplings.Lookup(strCacheEntryPath, strDummy))
			{
				pCoupling->Invalidate(pCacheManager);
			}
		}
	}
}


///////////////////////////////////////////////////////////////////////////////
//
//	CCacheControlInfo
//

class CCacheControlInfo
	: public CModelBasedType
{
public:
	CCacheControlInfo(bool bCacheEnabled, const CString& strModelOrFamily = _T(""))	
		: CModelBasedType(strModelOrFamily), m_bCacheEnabled(bCacheEnabled)
	{}

	bool GetCacheEnabled() const { return m_bCacheEnabled; }

private:
	bool m_bCacheEnabled;
};

///////////////////////////////////////////////////////////////////////////////
//
//	CCacheControlInfoManager
//

class CCacheControlInfoManager
{
public:
	CCacheControlInfoManager(CCacheControlInfo* pFirstInfo, ...);

	bool GetCacheEnabled(const CString& strModelOrFamily) const;

private:
	typedef CModelBasedMap<CCacheControlInfo> CacheControlInfoMap;		// key = model, value = CCacheControlInfo
	CacheControlInfoMap m_mapCacheControlInfo;
};

inline CCacheControlInfoManager::CCacheControlInfoManager(CCacheControlInfo* pFirstInfo, ...)
{
	va_list argPtr;
	va_start(argPtr, pFirstInfo);

	m_mapCacheControlInfo.BuildMap(pFirstInfo, argPtr);

	va_end(argPtr);
}

inline bool CCacheControlInfoManager::GetCacheEnabled(const CString& strModel) const
{
	bool bCacheEnabled = false;
	CCacheControlInfo* pCacheControlInfo = NULL;
	m_mapCacheControlInfo.Lookup(strModel, pCacheControlInfo);
	if (pCacheControlInfo == NULL)
	{
		// No model-specific control info found, so use default control info
		m_mapCacheControlInfo.Lookup(_T(""), pCacheControlInfo);
		ATLASSERT(pCacheControlInfo != NULL);
	}

	if (pCacheControlInfo != NULL)
	{
		bCacheEnabled = pCacheControlInfo->GetCacheEnabled();
	}

	return bCacheEnabled;
}

#endif // __NTLSTATECACHE_INCLUDED
