// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010  Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.


#include "std_afx.h"

#include "nel/3d/ps_located.h"
#include "nel/3d/particle_system.h"


#include "nel/misc/stream.h"
#include "nel/misc/mem_stream.h"

#include "dup_ps.h"

#include <memory>

using namespace NL3D;

//=======================================================================



/** This can duplicate any serializable type by using a serialization policy (polymorphic, non polymorphic ..)
  * The serialization policy must have a method to serial a pointer on the object (see example below)  
  * NB : of course this is slow (but convenient) way of performing a copy 
  * TODO maybe this could be used elsewhere ?    
  */
template <class TSerializePolicy, typename T>
static T	*DupSerializable(const T *in) throw(NLMISC::EStream)
{
	NLMISC::CMemStream ms;	
	nlassert(!ms.isReading());
	T *nonConstIn = const_cast<T *>(in);
	TSerializePolicy::serial(nonConstIn, ms);
	std::vector<uint8> datas(ms.length());
	std::copy(ms.buffer(), ms.buffer() + ms.length(), datas.begin());		
	ms.resetPtrTable();
	ms.invert();
	ms.fill(&datas[0], (uint32)datas.size());
	nlassert(ms.isReading());	
	T *newObj = NULL;
	TSerializePolicy::serial(newObj, ms);
	return newObj;
}

/** A policy to duplicate a non-polymorphic type
  */
struct CDupObjPolicy
{
	template <typename T>
	static void serial(T *&obj, NLMISC::IStream &dest)  throw(NLMISC::EStream)
	{ 	
		dest.serialPtr(obj);
		/*if (dest.isReading())
		{
			std::auto_ptr<T> newObj(new T);
			newObj->serialPtr(dest);
			delete obj;
			obj = newObj.release();
		}
		else
		{		
			obj->serial(dest);
		}*/
	}	
};

/** A policy to duplicate a polymorphic type
  */
struct CDupPolymorphicObjPolicy
{
	template <typename T>
	static void serial(T *&obj, NLMISC::IStream &dest)  throw(NLMISC::EStream)
	{ 	
		dest.serialPolyPtr(obj);		
	}
};



//=======================================================================
/////////////////////////////////////////
// temp until there is a clone method  //
/////////////////////////////////////////
NL3D::CParticleSystemProcess	*DupPSLocated(const CParticleSystemProcess *in)
{
	if (!in) return NULL;
	try
	{
		// if the located doesn't belon to a system, copy it direclty
		if (in->getOwner() == NULL)
		{
			return DupSerializable<CDupPolymorphicObjPolicy>(in);
		}
		else
		{
			uint index = in->getOwner()->getIndexOf(*in);
			/** Duplicate the system, and detach.
			  * We can't duplicate the object direclty (it may be referencing other objects in the system, so these objects will be copied too...)
			  */
			 std::auto_ptr<CParticleSystem> newPS(DupSerializable<CDupObjPolicy>(in->getOwner()));	
			 // scene pointer is not serialised, but 'detach' may need the scene to be specified
			 newPS->setScene(in->getOwner()->getScene());
			return newPS->detach(index);			
		}
	}
	catch (NLMISC::EStream &e)
	{
		nlwarning (e.what());
		return NULL;
	}
}

//=======================================================================
/////////////////////////////////////////
// temp until there is a clone method  //
/////////////////////////////////////////
NL3D::CPSLocatedBindable	*DupPSLocatedBindable(CPSLocatedBindable *in)
{
	if (!in) return NULL;
	try
	{
		// if no owner, can copy the object directy
		if (in->getOwner() == NULL)
		{
			return DupSerializable<CDupPolymorphicObjPolicy>(in);
		}
		else
		{
			CParticleSystem *srcPS = in->getOwner()->getOwner();
			std::auto_ptr<CParticleSystem> newPS(DupSerializable<CDupObjPolicy>(srcPS));	
			// scene pointer is not serialised, but 'detach' may need the scene to be specified
			newPS->setScene(in->getOwner()->getOwner()->getScene());
			//
			uint index	  = srcPS->getIndexOf(*(in->getOwner()));
			uint subIndex = in->getOwner()->getIndexOf(in);
			//									
			newPS->setScene(in->getOwner()->getScene()); // 'unbind' require the scene to be attached
			CPSLocated *loc = NLMISC::safe_cast<CPSLocated *>(newPS->getProcess(index));
			return loc->unbind(subIndex);
		}
	}
	catch (NLMISC::EStream &e)
	{
		nlwarning (e.what());
		return NULL;
	}
}