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

#ifndef __NTLINSTRCOMMAND_INCLUDED
#define __NTLINSTRCOMMAND_INCLUDED

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

///////////////////////////////////////////////////////////////////////////////
//
//	CInstrumentCommand
//

class CInstrumentCommand
	: public CModelBasedType
{
public:
	CInstrumentCommand(const CString& strCommand, const CString& strModelOrFamily = _T(""))
		: m_strCommand(strCommand), CModelBasedType(strModelOrFamily)
	{}

		const CString& GetCommand() const { return m_strCommand; }

private:
	CString m_strCommand;
};

///////////////////////////////////////////////////////////////////////////////
//
//	CEnumCommand
//

class CEnumCommand
	: public CModelBasedType
{
public:
	CEnumCommand(int nValue, const CString& strCommand, const CString& strModelOrFamily = _T(""))
		: m_nValue(nValue), m_strCommand(strCommand), CModelBasedType(strModelOrFamily)
	{}

public:
	int GetValue() const { return m_nValue; }
	const CString& GetCommand() const { return m_strCommand; }

private:
	int m_nValue;
	CString m_strCommand;
};

///////////////////////////////////////////////////////////////////////////////
//
//	CEnumCommandMap
//

typedef CAtlMap<int, CString> EnumValueMap;						// key = enum value, value = enum command
typedef CAtlMap<CString, int> EnumCommandMap;					// key = enum command, value = enum value
typedef CAtlMap<CString, EnumValueMap*>	EnumModelValueMap;		// key = model, value = enum value map
typedef CAtlMap<CString, EnumCommandMap*> EnumModelCommandMap;	// key = model, value = enum command map

template <typename EnumType>
class CEnumCommandMap
{
public:
	CEnumCommandMap(const CEnumCommand* pFirstCmd, ...);
	~CEnumCommandMap();

public:

	bool LookupEnumCommand(EnumType enumVal, const CString& strModel, CString& strCommand) const;
	bool LookupEnumValue(const CString& strCommand, const CString& strModel, EnumType& enumVal) const;

protected:	
	const EnumModelValueMap* GetModelValueMap() const; 
	const EnumModelCommandMap* GetModelCommandMap() const; 
	const EnumValueMap* GetValueMap(const CString& strModel) const;
	const EnumCommandMap* GetCommandMap(const CString& strModel) const;

private:
	void CreateOrAppendEnumValueMap(const CString& strModel, const CEnumCommand* pCmd);
	void CreateOrAppendEnumCommandMap(const CString& strModel, const CEnumCommand* pCmd);

private:
	EnumModelValueMap m_mapModelToValueMap;
	EnumModelCommandMap m_mapModelToCommandMap;
};

template <typename EnumType>
CEnumCommandMap<EnumType>::CEnumCommandMap(const CEnumCommand* pFirstCmd, ...)
{
	va_list argPtr;
	va_start(argPtr, pFirstCmd);

	const CEnumCommand* pCmd = pFirstCmd;
	while (pCmd)
	{
		CString strModelOrFamily = pCmd->GetModelOrFamily();
		InstrModelMap* pModelMap;
		if (strModelOrFamily.IsEmpty() || CInstrumentModelManager::LookupModel(strModelOrFamily))
		{
			// This is a model, so simply add the command to the list
			CreateOrAppendEnumValueMap(strModelOrFamily, pCmd);
			CreateOrAppendEnumCommandMap(strModelOrFamily, pCmd);
		}
		else if (CInstrumentModelManager::LookupFamily(strModelOrFamily, pModelMap))
		{
			// This is a family, so extract all of the models in the family
			POSITION pos = pModelMap->GetStartPosition();
			while (pos != NULL)
			{
				// We call GetNextKey here since the model map is aranged as (key=model, value=family)
				CString strModel = pModelMap->GetNextKey(pos);

				// We MAY want any model-specific entry to override the family entry, so check first if a model-specific entry exists
				EnumCommandMap* pCommandMap = NULL;
				if (!m_mapModelToCommandMap.Lookup(strModel, pCommandMap))
				{
					CreateOrAppendEnumValueMap(strModel, pCmd);
					CreateOrAppendEnumCommandMap(strModel, pCmd);
				}
			}
		}
		else
		{
			ATLASSERT(!"Unrecognized model.");
		}

		delete pCmd;
		pCmd = va_arg(argPtr, CEnumCommand*);
	}

	va_end(argPtr);
}

template <typename EnumType>
CEnumCommandMap<EnumType>::~CEnumCommandMap()
{
	POSITION pos = m_mapModelToValueMap.GetStartPosition();
	while (pos != NULL)
	{
		delete m_mapModelToValueMap.GetNextValue(pos);
	}

	pos = m_mapModelToCommandMap.GetStartPosition();
	while (pos != NULL)
	{
		delete m_mapModelToCommandMap.GetNextValue(pos);
	}
}

template <typename EnumType>
bool CEnumCommandMap<EnumType>::LookupEnumCommand(EnumType enumVal, const CString& strModel, CString& strCommand) const
{
	bool bFound = false;
	const EnumValueMap* pValueMap = GetValueMap(strModel);
	if (pValueMap != NULL)
	{
		bFound = pValueMap->Lookup(enumVal, strCommand);
	}

	return bFound;
}

template <typename EnumType>
bool CEnumCommandMap<EnumType>::LookupEnumValue(const CString& strCommand, const CString& strModel, EnumType& enumVal) const
{
	bool bFound = false;
	const EnumCommandMap* pCommandMap = GetCommandMap(strModel);
	if (pCommandMap != NULL)
	{
		int val = 0;
		bFound = pCommandMap->Lookup(strCommand, val);
		if (bFound)
		{
			enumVal = (EnumType)val;
		}
	}

	return bFound;
}

template <typename EnumType>
const EnumModelValueMap* CEnumCommandMap<EnumType>::GetModelValueMap() const
{
	return &m_mapModelToValueMap;
}

template <typename EnumType>
const EnumModelCommandMap* CEnumCommandMap<EnumType>::GetModelCommandMap() const
{
	return &m_mapModelToCommandMap;
}

template <typename EnumType>
const EnumValueMap* CEnumCommandMap<EnumType>::GetValueMap(const CString& strModel) const
{
	EnumValueMap* pValueMap = NULL;
	bool bFound = m_mapModelToValueMap.Lookup(strModel, pValueMap);

	return bFound ? pValueMap : NULL;
}

template <typename EnumType>
const EnumCommandMap* CEnumCommandMap<EnumType>::GetCommandMap(const CString& strModel) const
{
	EnumCommandMap* pCommandMap = NULL;
	bool bFound = m_mapModelToCommandMap.Lookup(strModel, pCommandMap);

	return bFound ? pCommandMap : NULL;
}

template <typename EnumType>
void CEnumCommandMap<EnumType>::CreateOrAppendEnumValueMap(const CString& strModel, const CEnumCommand* pCmd)
{
	ATLASSERT(pCmd != NULL);
	if (pCmd != NULL)
	{
		// Create a new EnumValueMap for this model OR append an existing one
		EnumValueMap* pEnumValueMap;
		if (!m_mapModelToValueMap.Lookup(strModel, pEnumValueMap))
		{
			// No EnumValueMap exists for this model, so create one and add it to the model map
			pEnumValueMap = new EnumValueMap;
			m_mapModelToValueMap[strModel] = pEnumValueMap;
		}

		pEnumValueMap->SetAt(pCmd->GetValue(), pCmd->GetCommand());
	}
}


template <typename EnumType>
void CEnumCommandMap<EnumType>::CreateOrAppendEnumCommandMap(const CString& strModel, const CEnumCommand* pCmd)
{
	ATLASSERT(pCmd != NULL);
	if (pCmd != NULL)
	{
		// Create a new EnumCommandMap for this model OR append an existing one
		EnumCommandMap* pEnumCommandMap;
		if (!m_mapModelToCommandMap.Lookup(strModel, pEnumCommandMap))
		{
			// No EnumValueMap exists for this model, so create one and add it to the model map
			pEnumCommandMap = new EnumCommandMap;
			m_mapModelToCommandMap[strModel] = pEnumCommandMap;
		}

		pEnumCommandMap->SetAt(pCmd->GetCommand(), pCmd->GetValue());
	}
}


///////////////////////////////////////////////////////////////////////////////
//
//	CEnumCommandManager
//

template <typename EnumType>
const CEnumCommandMap<EnumType>* GetEnumCommandMap();			// implemented in generated code	

template <class DriverClass>
class CEnumCommandManager
{
public:				

	template <typename EnumType>
	CString EnumToString(EnumType enumVal)
	{
		CString strCommand;
		EnumToString(enumVal, strCommand);

		return strCommand;
	}

	template <typename EnumType>
	EnumType EnumFromString(const CString& strCommand)
	{
		EnumType enumVal;
		EnumFromString(strCommand, enumVal);

		return enumVal;
	}

	template <typename EnumType>
	bool EnumToString(EnumType enumVal, CString& strCommand)
	{
		DriverClass* pT = static_cast<DriverClass*>(this);
		
		CString strModel = pT->GetModel();
		bool bFound = GetEnumCommandMap<EnumType>()->LookupEnumCommand(enumVal, strModel, strCommand);
		if (!bFound)
		{
			// No model-specific command found, so use the default command
			strModel = _T("");
			bFound = GetEnumCommandMap<EnumType>()->LookupEnumCommand(enumVal, strModel, strCommand);
			ATLASSERT(bFound);
		}

		return bFound;
	}

	template <typename EnumType>
	bool EnumFromString(const CString& strCommand, EnumType& enumVal)
	{
		DriverClass* pT = static_cast<DriverClass*>(this);

		CString strModel = pT->GetModel();
		bool bFound = GetEnumCommandMap<EnumType>()->LookupEnumValue(strCommand, strModel, enumVal);
		if (!bFound)
		{
			// No model-specific command found, so use the default command
			strModel = _T("");
			bFound = GetEnumCommandMap<EnumType>()->LookupEnumValue(strCommand, strModel, enumVal);
			ATLASSERT(bFound);
		}

		return bFound;
	}
};

#endif // __NTLINSTRCOMMAND_INCLUDED
