/** 
* @file		basetypeimpl.h
* @brief	Support classes to implement new types within lib-sitplus.
* @author	Cesar Mauri Loba (cesar at crea-si dot com)
*
* -------------------------------------------------------------------------
*
* Copyright:   (C) 2011 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_BASETYPEIMPL_H
#define SPCORE_BASETYPEIMPL_H

#include "spcore/basetype.h"
#include "spcore/coreruntime.h"
#include "spcore/pinimpl.h"
#include <boost/type_traits/is_pointer.hpp>

namespace spcore {

/**
	@brief Template to create basic factories of basic types.
	@tparam T class of objects this factory constructs.

	This template allows to create type factories given a class
	which provides an static method getTypeName() and a constructor
	which accepts the ID as parameter.
*/
template<class T> 
class SimpleTypeFactory : public ITypeFactory
{
public:
	virtual const char * GetName() const {
		return T::getTypeName();
	}
	virtual SmartPtr<CTypeAny> CreateInstance(int id) {
		return SmartPtr<CTypeAny>(new T(id), false);
	}
};

/**
	@brief Adapter to implement composite types.
	
	For non-composite type inherit directly from CTypeAny.
*/
class CompositeTypeAdapter : public CTypeAny
{
public:
	virtual int AddChild(SmartPtr<CTypeAny> component) {
		std::vector<CTypeAny*>::iterator it=
			find(m_children.begin(), m_children.end(), component.get());
		if (it!= m_children.end()) return -1;	// Already registered
		component->AddRef();
		m_children.push_back(component.get());
		return 0;
	}
	virtual SmartPtr<IIterator<CTypeAny*> > QueryChildren() const {
		return SmartPtr<IIterator<CTypeAny*> > (new CIteratorVector<CTypeAny*>(m_children), false);		
	}

protected:
	CompositeTypeAdapter(int id) : CTypeAny(id) {}
	virtual ~CompositeTypeAdapter() {
		std::vector<CTypeAny*>::iterator it= m_children.begin();
		for (; it!= m_children.end(); ++it) (*it)->Release();
	}

	virtual bool CopyTo ( CTypeAny& dst, bool recurse) const {
		assert (dst.GetTypeID()== this->GetTypeID());
		assert (this!= &dst);

		CompositeTypeAdapter* dst_cast= sptype_static_cast<CompositeTypeAdapter>(&dst);
				
		// Contents are copied by the derived class that should call this method

		if (recurse) {
			// 
			// composite copy of a composite type. update structure and contents recursively 
			//
			std::vector<CTypeAny*>::const_iterator it_src= this->m_children.begin();
			std::vector<CTypeAny*>::iterator it_dst= dst_cast->m_children.begin();	

			// While both have children just copy
			while (it_src!= this->m_children.end() && it_dst!= dst_cast->m_children.end()) {
				SmartPtr<CTypeAny> newInstance= (*it_src)->Clone (*it_dst, recurse);
				if (newInstance.get()== NULL) {
					// Should not happen
					assert (false);
					return false;
				}
				if (newInstance.get()!= *it_dst) {
					/**
					 @todo, TODO: remove the following assert which is only intended to
					 detect when this portion of code is used. Note that this means
					 that the old dst had a different type form the src which is
					 strange but not, in principle, wrong
					*/
					assert (false);

					// Copy process created a new instance. Substitute in vector and
					// and destroy the old one
					(*it_dst)->Release();
					*it_dst= newInstance.get();
					(*it_dst)->AddRef();
				}

				++it_src;
				++it_dst;		
			}

			if (it_src!= this->m_children.end()) {
				// src has more children than dst, create and clone
				while (it_src!= this->m_children.end()) {
					SmartPtr<CTypeAny> new_child= (*it_src)->Clone (NULL, recurse);
					if (new_child.get()== NULL) {
						// Should not happen
						assert (false);
						return false;
					}
					new_child.get()->AddRef();	// Goes to non smart ptr'ed vector
					dst_cast->m_children.push_back (new_child.get());
					
					++it_src;
				}
			}
			else {
				// dst has remaining children that need to be removed
				while (it_dst!= dst_cast->m_children.end()) {
					(*it_dst)->Release();
					it_dst= dst_cast->m_children.erase(it_dst);
				}
			}
		}
		else {
			// shallow copy, remove destination children if any
			std::vector<CTypeAny*>::iterator it_dst= dst_cast->m_children.begin();
			while (it_dst!= dst_cast->m_children.end()) {
				(*it_dst)->Release();
				it_dst= dst_cast->m_children.erase(it_dst);
			}
		}	

		return true;
	}

private:
	std::vector<CTypeAny*> m_children;
};


/**
	@brief Template to define common static operations of a basic type.
	@tparam BASE class which provide the basic operations of the type.
	@tparam RESULT resulting class (i.e. the class of the type instances).
		Used for return types and type castings.

	Provides convenience functions to allow to obtain the type ID and to
	create instances and pin using the name of the class directly in
	in the C++ sources.	
	
	Intended for being used internally. Use SimpleType template class if you 
	are	implementing a new sort of type.

	Ex: CTypeInt::CreateInstance() creates a new integer instance.
*/
template <class BASE, class RESULT>
class SimpleTypeBasicOperations
{
public:
	/**
		@brief Get numeral type identifier using core services.
		@return type ID or TYPE_INVALID if type is undefined.

		First call uses the type name to obtain the ID, but
		further calls use a cached copy of the ID.
	*/
	static int getTypeID() { 
		static int typeID= TYPE_INVALID;
		if (typeID== TYPE_INVALID) typeID= getSpCoreRuntime()->ResolveTypeID(BASE::getTypeName());
		return typeID;
	}

	/**
		@brief Create an instance of the type.
		@return smart pointer to the new instance which can store NULL when error.
	*/
	static SmartPtr<RESULT> CreateInstance() {
		int typeID= SimpleTypeBasicOperations::getTypeID();
		if (typeID== TYPE_INVALID) return SmartPtr<RESULT>(NULL, false);    			
		return SmartPtr<RESULT>(static_cast<RESULT *>(getSpCoreRuntime()->CreateTypeInstance(typeID).get()));		
	}

	/**
		@brief Create output pin of the type.
		@param name identifier the new pin will be given.
		@return smart pointer to the newly created output pin.
		@todo check usage and decide whether to catch exceptions and log errors

		Note that this method below can throw although shouldn't unless a fatal error occur.
	*/	
	static SmartPtr<IOutputPin> CreateOutputPin(const char* name) {
		return SmartPtr<IOutputPin>(new COutputPin (name, BASE::getTypeName()), false);
	}
	
	/**
		@brief Create a thread safe output pin of the type.
		@param name identifier the new pin will be given.
		@return smart pointer to the newly created output pin.
		@todo check usage and decide whether to catch exceptions and log errors

		Note that this method below can throw although shouldn't unless a fatal error occur.
	*/
	static SmartPtr<IOutputPin> CreateOutputPinLock(const char* name) {
		return SmartPtr<IOutputPin>(new COutputPinLock (name, BASE::getTypeName()), false);
	}
};

/**
	@brief Template to define basic types
	@tparam BASE class which provide the basic operations of the type.
*/

template<class BASE>
class SimpleType : public BASE, public SimpleTypeBasicOperations<BASE, SimpleType<BASE> >
{
	/**
		Template class SimpleTypeFactory is the only allowed.
		to create instances of this class.
	*/
	friend class SimpleTypeFactory<SimpleType>;

protected:
	/**
		@brief Protected constructor.
		@param typeID type identifier the new instance will be given. 
		Note that this constructor only can be called from a 
		SimpleTypeFactory instance.
	*/
	SimpleType(int typeID) : BASE(typeID) {}
		
private:
	// Disable default copy constructor and operator
	SimpleType( const SimpleType& );      // not implemented
	SimpleType& operator=( const SimpleType& );     // not implemented
};

/**
	@brief Contents template class for scalar types.
	@tparam T scalar type to be stored.
*/
template<class T>
class ScalarTypeContents : public CTypeAny {
	BOOST_STATIC_ASSERT (!boost::is_pointer<T>::value);
public:
	/**
		@brief Get the stored value
		@return stored value
		@todo remove const
	*/
	virtual const T getValue() const { return m_value; }

	/**
		@brief Store a new value
		@param value
	*/
	virtual void setValue(T value) { m_value= value; }

protected:
	/**
		@brief Protected constructor.
		@param id identifier the new instance will be given.
	*/
	ScalarTypeContents(int id) : CTypeAny(id) { m_value= 0; }
	
	virtual bool CopyTo ( CTypeAny& dst, bool ) const {
		assert (dst.GetTypeID()== this->GetTypeID());
		sptype_static_cast<ScalarTypeContents>(&dst)->m_value= m_value;
		return true;
	}

	// Destructor not needed
    T m_value;
};


} // namespace spcore
#endif
