/* ImLib3D
 * Copyright (c) 2001, ULP-IPB Strasbourg.
 *
 * 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 2 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, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*!  \file   TypeSafeGenericPointer.hpp */

#ifndef _TypeSafeGenericPointer_hpp
#define _TypeSafeGenericPointer_hpp

#include<ImLib3D/CppTools.hpp>

class TypeSafeGenericPointer
{
	void *ptr;
	const char *typeinfoName;
	//! casting with multiple inheritance might change pointer... so we must keep a pointer for each cast
	map<string,void *> alternateCasts;
public:
	string DemangledTypeName(){return TypeNameParser(typeinfoName);}
	string TypeName(){return typeinfoName;}
	bool IsSet(){return string(typeinfoName)!=string("");}
	void Clear(){ptr=NULL;typeinfoName="";alternateCasts.clear();}
	template<class T>
	T Get()
	{
		if(!IsSet()){ThrowError("TypeSafeGenericPointer::Get Trying to read value unset pointer");}
		string castTypeName=typeid(T).name();
		if(castTypeName!=typeinfoName)
		{
			// normal cast failed.. try alternate casts
			if(alternateCasts.find(castTypeName)!=alternateCasts.end())
			{
				return (T)alternateCasts[castTypeName];
			}
			string alt;
			for(map<string,void *>::iterator p=alternateCasts.begin();p!=alternateCasts.end();++p)
			{
				alt+=TypeNameParser((*p).first)+",";
			}
			ThrowError("TypeSafeGenericPointer::Get: Attempted to use pointer as:\"%s\". Originally,  pointer was stored as:\"%s\": available alternate casts where:\"%s\"",
					 TypeNameParser(castTypeName).c_str(),
					 DemangledTypeName().c_str(),
					 alt.c_str());
		}
		return (T)ptr;
	}
	template<class T>
	void Set(T _ptr)
	{
		typeinfoName=typeid(T).name();
		ptr=(void*)_ptr;
	}

	template<class OriginalType,class AlternateType>
	void AddAlternateCast()
	{
		alternateCasts[typeid(AlternateType).name()]=(void*)(AlternateType)(OriginalType)ptr;
	}


	template<class T>
	TypeSafeGenericPointer(T _ptr)
	{
		Set(_ptr);
	}
	TypeSafeGenericPointer &operator=(const TypeSafeGenericPointer &other)
	{
		ptr=other.ptr;
		typeinfoName=other.typeinfoName;
		alternateCasts=other.alternateCasts;
		return *this;
	}
	TypeSafeGenericPointer(const TypeSafeGenericPointer &other)
	{
		ptr=other.ptr;
		typeinfoName=other.typeinfoName;
		alternateCasts=other.alternateCasts;
	}
	TypeSafeGenericPointer():ptr(NULL),typeinfoName("")
	{
	}
};


#endif // _TypeSafeGenericPointer_hpp
