//===-- MIUtilVariant.h -----------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

//++
// File:		MIUtilVariant.h
//
// Overview:	CMIUtilVariant interface.
//
// Environment:	Compilers:	Visual C++ 12.
//							gcc (Ubuntu/Linaro 4.8.1-10ubuntu9) 4.8.1
//				Libraries:	See MIReadmetxt. 
//
// Gotchas:		See CMIUtilVariant class description.
//
// Copyright:	None.
//--

#pragma once

// In-house headers:
#include "MIDataTypes.h"  

//++ ============================================================================
// Details:	MI common code utility class. The class implements behaviour of a 
//			variant object which holds any data object of type T. A copy of the
//			data object specified is made and stored in *this wrapper. When the
//			*this object is destroyed the data object hold within calls its
//			destructor should it have one.
// Gotchas:	None.
// Authors:	Illya Rudkin 18/06/2014.
// Changes:	None.
//--
class CMIUtilVariant
{
// Methods:
public:
	/* ctor */ CMIUtilVariant( void );
	/* ctor */ CMIUtilVariant( const CMIUtilVariant & vrOther );
	/* dtor */ ~CMIUtilVariant( void );
	
	template< typename T >
		void Set( const T & vArg );
	template< typename T >
		T * Get( void ) const;

	CMIUtilVariant & operator= ( const CMIUtilVariant & vrOther );

// Classes:
private:
	//++ ----------------------------------------------------------------------
	// Details: Base class wrapper to hold the variant's data object when
	//			assigned to it by the Set() function. Do not use the CDataObjectBase 
	//			to create objects, use only CDataObjectBase derived objects,
	//			see CDataObject() class.
	//--
	class CDataObjectBase
	{
	// Methods:
	public:
		/* ctor */ CDataObjectBase( void );
		/* ctor */ CDataObjectBase( const CDataObjectBase & vrOther );
		//
		CDataObjectBase & operator= ( const CDataObjectBase & vrOther ) ;
	
	// Overrideable:
	public:
		virtual ~CDataObjectBase( void );
		virtual CDataObjectBase *	CreateCopyOfSelf( void );
		virtual bool				GetIsDerivedClass( void ) const;

	// Overrideable:
	protected:
		virtual void Copy( const CDataObjectBase & vrOther );
	};

	//++ ----------------------------------------------------------------------
	// Details: Derived from CDataObjectBase, this class is the wrapper for the
	//			data object as it has an aggregate of type T which is a copy
	//			of the data object assigned to the variant object.
	//--
	template< typename T >
	class CDataObject : public CDataObjectBase
	{
	// Statics:
	public:

	// Methods:
	public:
		/* ctor */ CDataObject( void );
		/* ctor */ CDataObject( const T & vArg );
		/* ctor */ CDataObject( const CDataObject & vrOther );
		//
		CDataObject & operator= ( const CDataObject & vrOther );
		//
		T & GetDataObject( void );

	// Overridden:
	public:
		// From CDataObjectBase
		virtual ~CDataObject( void );
		virtual CDataObjectBase *	CreateCopyOfSelf( void );
		virtual bool				GetIsDerivedClass( void ) const;

	// Overridden:
	private:
		// From CDataObjectBase
		virtual void Copy( const CDataObject & vrOther );

	// Attributes:
	private:
		T	m_dataObj;
	};

// Methods
private:
	void Destroy( void );
	void Copy( const CMIUtilVariant & vrOther );

// Attributes:
private:
	CDataObjectBase *	m_pDataObject;
};

//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------

//++ ------------------------------------------------------------------------------------
// Details:	CDataObject constructor.
// Type:	Method.
// Args:	T	- The object's type.
// Return:	None.
// Throws:	None.
//--
template< typename T >
CMIUtilVariant::CDataObject< T >::CDataObject( void )
{
}

//++ ------------------------------------------------------------------------------------
// Details:	CDataObject constructor.
// Type:	Method.
// Args:	T		- The object's type.
//			vArg	- (R) The data object to be stored in the variant object.
// Return:	None.
// Throws:	None.
//--
template< typename T >
CMIUtilVariant::CDataObject< T >::CDataObject( const T & vArg )
{
	m_dataObj = vArg;
}

//++ ------------------------------------------------------------------------------------
// Details:	CDataObject destructor.
// Type:	Overridden.
// Args:	T	- The object's type.
// Return:	None.
// Throws:	None.
//--
template< typename T >
CMIUtilVariant::CDataObject< T >::~CDataObject( void )
{
}

//++ ------------------------------------------------------------------------------------
// Details:	Retrieve the data object hold by *this object wrapper.
// Type:	Method.
// Args:	T	- The object's type.
// Return:	T &	- Reference to the data object.
// Throws:	None.
//--
template< typename T >
T & CMIUtilVariant::CDataObject< T >::GetDataObject( void )
{
	return m_dataObj;
}

//++ ------------------------------------------------------------------------------------
// Details:	Create a new copy of *this class.
// Type:	Overridden.
// Args:	T	- The object's type.
// Return:	CDataObjectBase *	- Pointer to a new object.
// Throws:	None.
//--
template< typename T >
CMIUtilVariant::CDataObjectBase * CMIUtilVariant::CDataObject< T >::CreateCopyOfSelf( void )
{
	CDataObject * pCopy = new CDataObject< T >( m_dataObj );
	
	return pCopy;
}

//++ ------------------------------------------------------------------------------------
// Details:	Determine if *this object is a derived from CDataObjectBase.
// Type:	Overridden.
// Args:	T	- The object's type.
// Return:	bool	- True = *this is derived from CDataObjectBase
//					- False = *this is an instance of the base class.
// Throws:	None.
//--
template< typename T >
bool CMIUtilVariant::CDataObject< T >::GetIsDerivedClass( void ) const
{
	return true;
}

//++ ------------------------------------------------------------------------------------
// Details:	Perform a bitwise copy of *this object.
// Type:	Overridden.
// Args:	T		- The object's type.
//			vrOther	- (R) The other object.
// Return:	None.
// Throws:	None.
//--
template< typename T >
void CMIUtilVariant::CDataObject< T >::Copy( const CDataObject & vrOther )
{
	m_dataObj = vrOther.m_dataObj;
}

//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------

//++ ------------------------------------------------------------------------------------
// Details:	Assign to the variant an object of a specified type.
// Type:	Template method.
// Args:	T		- The object's type.
//			vArg	- (R) The object to store.
// Return:	None.
// Throws:	None.
//--
template< typename T >
void CMIUtilVariant::Set( const T & vArg )
{
	m_pDataObject = new CDataObject< T >(  vArg );
}

//++ ------------------------------------------------------------------------------------
// Details:	Retrieve the data object from *this variant.
// Type:	Template method.
// Args:	T	- The object's type.
// Return:	T *	- Pointer the data object, NULL = data object not assigned to *this variant.
// Throws:	None.
//--
template< typename T >
T * CMIUtilVariant::Get( void ) const
{
	if( (m_pDataObject != nullptr) && m_pDataObject->GetIsDerivedClass() )
	{
		CDataObject< T > * pDataObj = static_cast< CDataObject< T > * >( m_pDataObject );
		return &pDataObj->GetDataObject();
	}

	// Do not use a CDataObjectBase object, use only CDataObjectBase derived objects
	return nullptr;
}