/****************************************************************************************************

		ParamHolder.h
		Copyright: (c) 2002 Eovia Corp. All rights reserved.

		Author:	Pierre
		Date:	8/17/2006

****************************************************************************************************/

#ifndef __ParamHolder__
#define __ParamHolder__

#if CP_PRAGMA_ONCE
#pragma once
#endif

#include "IExParamType.h"
#include "MCStream.h"


// This class can be used to manipulate abstract types.
class TParamHolder : public TMCObject
{
public:
	// This function create an untyped param
	TParamHolder();

	// WARNING: these functions are not safe. You need to make sure that the
	// data and the type are coherent.
	TParamHolder(IDType type, void* data);
	TParamHolder(IExParamType* type, void* data);

	TParamHolder(const TParamHolder& param);
	TParamHolder& operator = (const TParamHolder& param);

	inline IDType	GetType() const
	{
		if (fParamType)
			return fParamType->GetType();
		else
			return 0;
	}

	inline int32	GetSupportedOperations() const
	{ return fParamType->GetSupportedOperations(); }

	inline void		Reset()
	{
		fParamType->Reset(fData);
	}

	inline boolean	IsEqual(const TParamHolder& src) const
	{
		MCAssert(fParamType == src.fParamType);
		return fParamType->IsEqual(fData, src.fData);
	}

	inline void		Copy(const TParamHolder& src)
	{
		MCAssert(fParamType == src.fParamType);
		fParamType->Copy(src.fData, fData);
	}

	inline void		Copy(const void* src)	// WARNING: this function is unsafe
	{
		fParamType->Copy(src, fData);
	}

	inline void		Read(TMCiostream& stream)
	{
		fParamType->Read(fData, stream);
	}

	inline void		Write(TMCiostream& stream) const
	{
		fParamType->Write(fData, stream);
	}

	inline void		LinearInterpolate(real t, const TParamHolder& src1, const TParamHolder& src2)
	{
		MCAssert((fParamType->GetSupportedOperations() & IExParamType::kLinearOperations) != 0);
		MCAssert(fParamType == src1.fParamType);
		MCAssert(fParamType == src2.fParamType);
		fParamType->LinearInterpolate(t, src1.fData, src2.fData, fData);
	}

	inline void		ComputeOffset(const TParamHolder& src1, const TParamHolder& src2)
	{
		MCAssert((fParamType->GetSupportedOperations() & IExParamType::kLinearOperations) != 0);
		MCAssert(fParamType == src1.fParamType);
		MCAssert(fParamType == src2.fParamType);
		fParamType->ComputeOffset(src1.fData, src2.fData, fData);
	}

	inline void		ApplyOffset(const TParamHolder& offset)
	{
		MCAssert((fParamType->GetSupportedOperations() & IExParamType::kLinearOperations) != 0);
		MCAssert(fParamType == offset.fParamType);
		fParamType->ApplyOffset(fData, offset.fData, fData);
	}

	inline void		InvertOffset()
	{
		MCAssert((fParamType->GetSupportedOperations() & IExParamType::kInvertOffset) != 0);
		fParamType->InvertOffset(fData, fData);
	}

	// those functions are instanciated for the well known types (real32...) and
	// does the proper checks via asserts
	template<class T> const T&	Get() const;
	template<class T> T&		Get();

	void*						GetData() { return fData; }
	const void*					GetData() const { return fData; }

protected:
	TParamHolder(IDType type);
	TParamHolder(IExParamType* type);

protected:
	TMCCountedPtr<IExParamType>	fParamType;
	void*						fData;
};


// This object owns the parameter. It will allocate, destroy the memory properly.
class TOwnedParamHolder : public TParamHolder
{
public:
	// This function create an untyped param
	TOwnedParamHolder();

	explicit TOwnedParamHolder(IDType type);
	explicit TOwnedParamHolder(IExParamType* type);
	~TOwnedParamHolder();

	TOwnedParamHolder(const TParamHolder& param);
	TOwnedParamHolder(const TOwnedParamHolder& param);
	TOwnedParamHolder& operator = (const TOwnedParamHolder& param);

	void		ChangeType(IDType type);
	void		ChangeType(IExParamType* type);
	boolean		IsValid() const;

	void		Create();
	void		Delete();
};



#define PARAMHOLDER_GET_IMPL(type, assert) \
	template<> const type& TParamHolder::Get<type>() const \
	{ MCAssert(assert);	return *(type*)fData; } \
	template<> type& TParamHolder::Get<type>() \
	{ MCAssert(assert);	return *(type*)fData; }


#endif
