#include <vector>


class ATL_NO_VTABLE CVersionInfo : public IVersionInfo
{

public:

	class Info
	{
		Info () : 
			m_Implid(GUID_NULL), 
			m_MajorVer(0), 
			m_MinorVer(0), 
			m_cComponents(0), 
			m_ComponentDescription(NULL), 
			m_InstanceDescription(NULL),
			m_punk(NULL) { }
			
		Info (GUID Implid, DWORD MajorVer, DWORD MinorVer, LPCWSTR ComponentDescription, LPCWSTR InstanceDescription, IUnknown * punk) :
				m_Implid(Implid), 
				m_MajorVer(MajorVer), 
				m_MinorVer(MinorVer), 
				m_cComponents(0), 
				m_punk(punk)
		{
			m_ComponentDescription = SysAllocString( ComponentDescription ); 
			m_InstanceDescription = SysAllocString( InstanceDescription ); 
		}

		~Info()
		{
			SysFreeString( m_ComponentDescription );
			SysFreeString( m_InstanceDescription );
		}

	private:

		friend CVersionInfo;
		GUID m_Implid;
		DWORD m_MajorVer;
		DWORD m_MinorVer;
		BSTR m_ComponentDescription;
		BSTR m_InstanceDescription;
		ULONG m_cComponents;
		IUnknown * m_punk;
		
	};

public:
	CVersionInfo() { };
	~CVersionInfo()
	{ 
		for ( int i = 0; i < m_VersionInfos.size(); i++ )
		{
			delete m_VersionInfos[i];
		}
	};

	void Add(GUID Implid, DWORD MajorVer, DWORD MinorVer, LPCWSTR ComponentDescription, LPCWSTR InstanceDescription, IUnknown * punk)
	{
		Info * pInfo = new Info( Implid, MajorVer, MinorVer, ComponentDescription, InstanceDescription, punk );
		BuildVersionInfos( *pInfo );
		m_VersionInfos[0]->m_cComponents = m_VersionInfos.size() - 1;
	}
	
	STDMETHOD(GetSubcomponentCount)( ULONG ulSub, ULONG *ulCount )
	{
		if (ulSub > m_VersionInfos.size())
			return E_INVALIDARG;

		if (m_VersionInfos.empty())
			*ulCount = 0;
		else
			*ulCount = m_VersionInfos[ulSub]->m_cComponents;

	    return S_OK;
	}

	STDMETHOD(GetImplementationID)( ULONG ulSub, GUID * implid )
	{
		if (ulSub > m_VersionInfos.size())
			return E_INVALIDARG;

		*implid = m_VersionInfos[ulSub]->m_Implid;
	    return S_OK;
	}

	STDMETHOD(GetBuildVersion)( ULONG ulSub, DWORD * pdwMajor, DWORD * pdwMinor)
	{
		if (ulSub > m_VersionInfos.size())
			return E_INVALIDARG;

		*pdwMajor = m_VersionInfos[ulSub]->m_MajorVer;
		*pdwMinor = m_VersionInfos[ulSub]->m_MinorVer;
	    return S_OK;
	}

	// Expect string of the form "Company suite component version"
	// for human consumption only - not expected to be parsed.
	STDMETHOD(GetComponentDescription)( ULONG ulSub, BSTR * pImplStr )
	{
		if (ulSub > m_VersionInfos.size())
			return E_INVALIDARG;
			
		*pImplStr = SysAllocString( m_VersionInfos[ulSub]->m_ComponentDescription );
	    return S_OK;
	}

	// Implementation can put any useful string here. (eg. internal object state)
	STDMETHOD(GetInstanceDescription)( ULONG ulSub, BSTR * pImplStr)
	{
		if (ulSub > m_VersionInfos.size())
			return E_INVALIDARG;
			
		*pImplStr = SysAllocString( m_VersionInfos[ulSub]->m_InstanceDescription );
	    return S_OK;
	}

private:

	void BuildVersionInfos( Info& info )
	{
		IUnknown * punk = NULL;
		IVersionInfo * pIVer = NULL;
		HRESULT hr;

		m_VersionInfos.push_back(&info);
		
		if (!info.m_punk)
			return;
			
		hr = info.m_punk->QueryInterface( IID_IVersionInfo, (void **)&pIVer );
		if (hr != S_OK || pIVer == NULL)
			return;

		ULONG cCount = 0;
		pIVer->GetSubcomponentCount( 0, &cCount );
		if ( cCount )
		{
			info.m_cComponents = cCount;
			for ( int i = 1; i <= cCount; i++ )
			{
				
				Info * pInfo = new Info;
				
				pIVer->GetImplementationID( i, &pInfo->m_Implid );
				pIVer->GetBuildVersion( i, &pInfo->m_MajorVer, &pInfo->m_MinorVer );
				pIVer->GetComponentDescription( i, &pInfo->m_ComponentDescription );
				pIVer->GetInstanceDescription( i,&pInfo->m_InstanceDescription );
				BuildVersionInfos( *pInfo );
			}
		}
	}

private:
	std::vector < Info *> m_VersionInfos;

};