///////////////////////////////////////////////////////////////////////////////
/// @file deWorld_Helper.hpp
///
/// @brief Destiny "World" storage, helper file
///
/// @author Assassin
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date July 2003
/// @author Assassin
/// @remarks Creation
///
///////////////////////////////////////////////////////////////////////////////

#ifndef DEWORLD_HELPER_HPP
#define DEWORLD_HELPER_HPP

#include "deWorld.hpp"
#include "deFile.hpp"

// this is meant to be an inline-class that has "local" methods inside the
// same DLL as the class that inherits it
//template <class T>
/// inherit from this class instead of IdeWorldObject to get basic functionality implemented
class deWorldObject : virtual public IdeWorldObject, public deRefCountBase
{
private:
	deWorldID	m_WorldID;
	IdeWorld*	m_pWorld;
	deObjectID	m_ObjectID;
	void*		m_PrivateData;
	long		m_PrivDataSize;
protected:
	virtual void OnWorldIDSet() {} // override this if you need to
	deWorldObject()
		:	m_WorldID(0), m_ObjectID(0),
			m_pWorld(NULL), m_PrivateData(NULL), m_PrivDataSize(0)
	{ }
	deWorldObject(deWorldID WorldID, deObjectID ObjectID)
		: m_WorldID(WorldID), m_ObjectID(ObjectID), m_PrivateData(NULL), m_PrivDataSize(0)
	{ m_pWorld = IdeWorld_GetManager()->GetWorld(m_WorldID); }
	virtual ~deWorldObject()
	{
		if (m_pWorld)
			m_pWorld->RemoveWorldObject(m_ObjectID);
		if (m_PrivateData)
			free(m_PrivateData);
	}
	deObjectID GetObjectID() 
	{ return m_ObjectID; }
	deWorldID GetWorldID() 
	{ return m_WorldID; }
	deBoolean SetWorldID(deWorldID WorldID, deObjectID ObjectID = 0)
	{
		if (WorldID == m_WorldID)
			return deTRUE;
		deObjectID oldID = m_ObjectID;
		IdeWorld * newWorld = m_pWorld;
		m_WorldID = 0;
		m_ObjectID = 0;
		m_pWorld = NULL;
		if (newWorld)
			newWorld->RemoveWorldObject(oldID);
		newWorld = IdeWorld_GetManager()->GetWorld(WorldID); 
		if (!newWorld)
			return deTRUE;
		m_pWorld = newWorld;
		if (ObjectID != 0)
		{
			m_ObjectID = ObjectID;
			if (!m_pWorld->AddWorldObjectWithID(((IdeWorldObject*)this)))
				return deFALSE;
		}
		else
		{
			m_ObjectID = m_pWorld->AddWorldObject(((IdeWorldObject*)this));
			if (m_ObjectID == 0)
				return deFALSE;
		}
		m_WorldID = WorldID;
		OnWorldIDSet();
		return deTRUE;
	}
	deBoolean SetPrivateData(void* pData, long Size)
	{
		if (!pData)
		{
			if (m_PrivateData)
				free(m_PrivateData);
			m_PrivateData = NULL;
			m_PrivDataSize = 0;
		}
		if (Size <= 0)
			return deFALSE;
		m_PrivateData = malloc(Size);
		m_PrivDataSize = Size;
		memcpy(m_PrivateData, pData, m_PrivDataSize);
		return deTRUE;
	}
	void* GetPrivateData(long & pSize)
	{
		pSize = m_PrivDataSize;
		return m_PrivateData;
	}
private:
	deBoolean DeSerializeLoad()
	{ return deTRUE; }
};

#define WorldObjectClassDef \
private: \
	static const char* s_WOTypeName; \
public: \
	static IdeWorldObject* CreateWorldObject(void); \
	const char* GetWOTypeName(void); \
	deBoolean Serialize(IdeFile * File); \
	deBoolean DeSerialize(IdeFile * File, long DataLength)
#define WorldObjectClassImp(classname) \
	const char* classname::GetWOTypeName(void) { return s_WOTypeName; } \
	IdeWorldObject* classname::CreateWorldObject(void) { return (IdeWorldObject*)(new classname); } \
	const char* classname::s_WOTypeName = #classname
#define WorldObjectRegister(classname) \
	(IdeWorld_GetManager()->RegisterWOClass(#classname, classname::CreateWorldObject))


// some utility functions for making error-checked file reading & writing easier
template <int N, typename Y>
static deBoolean De3d_Read_CheckLength(IdeFile* FS, Y& Target, u32 &Length, u32 MaxLength)
{
	//T tempvar;
	char tempvar[N];
	if (Length + N > MaxLength)
		return deFALSE;
	if (N != FS->Read(&tempvar, N))
		return deFALSE;
	Length += N;
	//Target = (Y)tempvar;
	memcpy(&Target, tempvar, N);
	return deTRUE;
}

static deBoolean De3d_Read_CheckLength(IdeFile* FS, void* Target, u32 &Length, u32 MaxLength, u32 ByteSize)
{
	if (Length + ByteSize > MaxLength)
		return deFALSE;
	if (ByteSize != (u32)FS->Read(Target, ByteSize))
		return deFALSE;
	Length += ByteSize;
	return deTRUE;
}

template <typename T>
static deBoolean De3d_Write_CheckLength(IdeFile* FS, T Source)
{
	if (sizeof(T) != FS->Write((void*)(&Source), sizeof(T)))
		return deFALSE;
	return deTRUE;
}

static deBoolean De3d_Write_CheckLength(IdeFile* FS, void* Source, u32 ByteSize)
{
	if (ByteSize != (u32)FS->Write(Source, ByteSize))
		return deFALSE;
	return deTRUE;
}

#define DE3D_STREAM_PUT(FS,var,vartype) \
if(!De3d_Write_CheckLength<vartype>(FS,(vartype)var)) return deFALSE
#define DE3D_STREAM_PUTP(FS,var,size) \
if(!De3d_Write_CheckLength(FS,var,size)) return deFALSE

#define DE3D_STREAM_GET(FS,var,vartype,length,maxlength) \
if(!De3d_Read_CheckLength<sizeof(vartype)>(FS,var,length,maxlength)) return deFALSE
#define DE3D_STREAM_GETP(FS,var,size,length,maxlength) \
if(!De3d_Read_CheckLength(FS,var,length,maxlength, size)) return deFALSE

#endif // DEWORLD_HELPER_HPP
