/**
* @file		basetype.h
* @brief	CTypeAny definition and associated helpers
* @author	Cesar Mauri Loba (cesar at crea-si dot com)
*	
* -------------------------------------------------------------------------
*
* Copyright:   (C) 2010 Cesar Mauri Loba - CREA Software Systems
* 
*  This program is free software: you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation, either version 3 of the License, or
*  (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SPCORE_BASETYPE_H
#define SPCORE_BASETYPE_H

#include "spcore/baseobj.h"
#include "spcore/iterator.h"
#include "spcore/coreruntime.h"

namespace spcore {

// Forward declarations
class IOutputPin;

/**
	@brief Type identifiers.
*/
enum EBasicID {
  TYPE_INVALID = -1,
  TYPE_ANY = 0
};

/**
	@brief Base class for all types.
*/
class CTypeAny : public IBaseObject {
public:
	/**
		@brief Get type ID.
		@return the identifier of this instance.
	*/
	virtual int GetTypeID() const { return m_typeID; }
	
	/**
		@brief Add child instance (interface for composite types).
		@param component smart pointer to an instance to add as child.
		@return 0: child added successfully or -1 if error.

		Default definition doesn't allow child instances.
	*/
	virtual int AddChild(SmartPtr<CTypeAny> component) {
		return -1;
	}

	/**
		@brief Query children instances (interface for composite types).
		@return smart pointer to an iterator which can be NULL.

		Default definition doesn't allow child instances.
	*/
    virtual SmartPtr<IIterator<CTypeAny*> > QueryChildren() const {
		return SmartPtr<IIterator<CTypeAny*> >(NULL);
	}

	static const char* getTypeName() { return "any"; }

	/**
		@brief Performs a copy of this object.
		@param dst if not NULL and its ID coincides with the ID of this then
			overwrites dst. If type IDs mismatch a new instance is created and
			returned.
		@param recurse is passed untouched to CopyTo. if it is true
			deep copies are performed on composite types, if false only
			copies the instance but not its children.
		@return smart pointer to the new instance which can be dst, a new one or 
			NULL if this doesn't support copies.
	*/
	virtual SmartPtr<CTypeAny> Clone (CTypeAny * dst, bool recurse) const {
		if (this== dst) return SmartPtr<CTypeAny>(dst);

		if (dst && this->GetTypeID()== dst->GetTypeID()) {
			if (CopyTo (*dst, recurse)) 
				// Copy performed
				return SmartPtr<CTypeAny>(dst);
			else 
				// Same type but copy cannot be performed
				return SmartPtr<CTypeAny>();
		}
		else {
			// Different types or dst NULL
			ICoreRuntime* cr= getSpCoreRuntime(); assert (cr);
			SmartPtr<CTypeAny> newInstance= cr->CreateTypeInstance(m_typeID);
			if (newInstance.get()== NULL) {
				// Something went really wrong
				assert (false);
				return newInstance;
			}		
						
			if (CopyTo(*newInstance, recurse))
				return newInstance;
			else
				// Copy failed
				return SmartPtr<CTypeAny>();
		}
	}

	/**
		@brief Create output pins of type CTypeAny.
		@param name name that will be given to the pin.
		
		Pins created with this function can be attached to components 
		and instances of any type. If calls to api functions of the pin
		need to be thread safe see CreateOutputPinLockAny() method.
	*/
	static SmartPtr<IOutputPin> CreateOutputPinAny(const char* name) {
		return getSpCoreRuntime()->CreateOutputPin(CTypeAny::getTypeName(), name, false);
	}
	
	/**
		@brief Create thread safe output pins of type CTypeAny.
		@param name name that will be given to the pin.
		
		Pins created with this function can be attached to components 
		and instances of any type.
	*/
	static SmartPtr<IOutputPin> CreateOutputPinLockAny(const char* name) {
		return getSpCoreRuntime()->CreateOutputPin(CTypeAny::getTypeName(), name, true);
	}

protected:
	/**
		@brief Protected constructor.
		@param id numerical ID given to this instance.
	*/
	CTypeAny(int id) { assert (id>= 0); m_typeID= id; }
	
	/**
		@brief Virtual destructor.
	*/
	virtual ~CTypeAny() {}

	/**
		@brief Copies this into dst
		@param dst copy destination. Is always an existing instance.
		@param recurse perform deep copies of	composite objects when true.
		@todo make pure virtual?
	*/
	virtual bool CopyTo ( CTypeAny& dst, bool recurse) const {
		// This method should not be called directly
		// Calls are expected to be performed on derived classes
		assert (false);
		// Unused parameters
		(void) dst;
		(void) recurse;
		return false;
	}

	/**
		@brief Assignment operator. 

		Available to derived classes to allow to use the compiler generated copy 
		constructor. Note that copying between instances with different IDs it 
		is not allowed and also derived classed must ensure this. Use with care.
	*/
	CTypeAny& operator=( const CTypeAny& that )	{
		assert (this->m_typeID== that.m_typeID);
		return *this;
	}

private:
	// Disable copy constructor.
	CTypeAny ( const CTypeAny& );      

	//
	// Attributes
	//
	int m_typeID;
};

/**
	@brief Interface for intances factory.
	
	Conforming implementation should provide definition of GetName() and 
	CreateInstance() methods.
*/
class ITypeFactory : public IBaseObject {
protected:
	virtual ~ITypeFactory() {}

public:

	/**
		@brief Return the name of the type of the instances this factory creates.
		@return name of the type of the instances this factory creates.
	*/
    virtual const char* GetName() const = 0;

	/**
		@brief Create instance.
		@param id numerical ID of the type.
		@todo store the ID in the factory once registered.
	*/
    virtual SmartPtr<CTypeAny> CreateInstance(int id) = 0;
};



//
// Template functions for downcasting (smart pointed) CTypeAny instances
//
template <typename T>
T* sptype_static_cast (CTypeAny* v){
	return static_cast<T*>(v);
}

template <typename T>
T const * sptype_static_cast (CTypeAny const * v){
	return static_cast<T const *>(v);
}

template <typename T>
T* sptype_dynamic_cast (CTypeAny* v){
	if (T::getTypeID()!= v->GetTypeID()) return NULL;
	return static_cast<T*>(v);
}

template <typename T>
T const * sptype_dynamic_cast (CTypeAny const * v){
	if (T::getTypeID()!= v->GetTypeID()) return NULL;
	return static_cast<T const *>(v);
}

template <typename T>
SmartPtr<T> sptype_static_cast (const SmartPtr<const CTypeAny> &v){
	return smartptr_static_cast<T,const CTypeAny>(v);
}

template <typename T>
SmartPtr<T> sptype_static_cast (const SmartPtr<CTypeAny> &v){
	return smartptr_static_cast<T,CTypeAny>(v);
}

template <typename T>
SmartPtr<T> sptype_dynamic_cast (const SmartPtr<const CTypeAny> &v){
	if (T::getTypeID()!= v->GetTypeID()) return SmartPtr<T>(NULL);
	return smartptr_static_cast<T,const CTypeAny>(v);
}

template <typename T>
SmartPtr<T> sptype_dynamic_cast (const SmartPtr<CTypeAny> &v){
	if (T::getTypeID()!= v->GetTypeID()) return SmartPtr<T>(NULL);
	return smartptr_static_cast<T,CTypeAny>(v);
}


} // namespace spcore
#endif
