/* 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   Properties.hpp
  \brief  Extra properties for images, such as support, mask, patient info, etc. 


  See AUTHORS and COPYING files for more information
*/
#ifndef _Properties_hpp
#define _Properties_hpp


#include<sstream>
#include<map>

extern XMLNode *DuplicateXMLNode(XMLNode *src);

//! Base class for image properties
class ImageProperty : public Streamable
{
public:
	string name;
//    	virtual void Write(ImLib3DFile *file,XMLNode *parentNode=NULL,XMLNode *node=NULL) const =0;
//    	virtual void Read( ImLib3DFile *file,XMLNode *parentNode=NULL,XMLNode *node=NULL)=0;
	virtual ImageProperty *Duplicate() const=0;
	virtual bool operator==(const ImageProperty &other){return true;}
	bool IsXML();
	virtual ~ImageProperty(){;}
	ImageProperty(const string &_name):name(_name){;}
};

//! Unparsed image property, in raw XML representation. 
/*! PropertyXML will be automatically parsed into apropriate PropertyWrapPtr
  if  Properties::Get is called on this PropertyXML. For custom property types the
user must do the parsing himself by calling  Properties::Parse<UserDefinedProperty>(name). See
AnnotationsProperty in viewer for an example.  */
class PropertyXML : public ImageProperty
{
public:
	XMLNode *xml;
  	virtual void Write(ImLib3DFile *file,XMLNode *parentNode=NULL,XMLNode *node=NULL) const 
	{
		XMLNode *dup=DuplicateXMLNode(xml);
		parentNode->add_child(dup);
	}
  	virtual void Read(ImLib3DFile *file,XMLNode *parentNode=NULL,XMLNode *node=NULL){xml=DuplicateXMLNode(node);}
	virtual ImageProperty *Duplicate() const
	{
		PropertyXML *res=new PropertyXML(name);
		res->xml=DuplicateXMLNode(xml);
		return res;
	}
	virtual ~PropertyXML(){delete xml;}
	PropertyXML(const string &_name):ImageProperty(_name),xml(NULL){;}
};


//! Base class for templated properties
template<class PType>
class TProperty : public ImageProperty
{
public:
	virtual PType &Get()=0;
	virtual const PType &Get() const =0;
	TProperty(const string &_name):ImageProperty(_name){;}
};

template<class T> inline void PropertyWrapPtr_ReadFromString(const string &str,T *p)
{
	istringstream ist(str);
	ist >> *p;
}
template<> inline void PropertyWrapPtr_ReadFromString<string>(const string &str,string *p){*p=str;}

//! Basic container properties, for containing any propertie types that support som operations
/*! Currently must have: - empty ctr - copy operator - in and out stream */
template<class PType>
class PropertyWrapPtr : public TProperty<PType>
{
	typedef TProperty<PType> _Base;
public:
	PType *ptr;
	virtual PType &Get(){return *ptr;}
	virtual const PType &Get() const {return *ptr;}
	virtual bool operator==(const ImageProperty &other){return true;}//*ptr==*other.ptr;}
  	virtual void Write(ImLib3DFile *file,XMLNode *parentNode=NULL,XMLNode *node=NULL) const 
	{
		node=CreateWriteNode(_Base::name,file,parentNode,node);
		ostringstream ost;
		ost << *ptr;
		node->add_property("value",ost.str());
		node->add_property("PropertyType","PropertyWrapPtr");
		node->add_property("type",TypeName<PType>());
	}

  	virtual void Read(ImLib3DFile *file,XMLNode *parentNode=NULL,XMLNode *node=NULL)
	{
		node=CreateReadNode(_Base::name,file,parentNode,node);
		if(!ptr){ptr=new PType();}
		mprintf("PropertyWrapPtr::Read: valueprop:%x\n",node->property("value" ));
		string value=node->property("value" )->value();
		mprintf("PropertyWrapPtr::Read: value:%s\n",value.c_str());
		PropertyWrapPtr_ReadFromString<PType>(value,ptr);
	}
	virtual ImageProperty *Duplicate() const
	{
		PropertyWrapPtr<PType> *res=new PropertyWrapPtr<PType>(_Base::name);
		res->ptr=new PType();
		*(res->ptr)=*ptr;
		return res;
	}
	virtual ~PropertyWrapPtr(){if(ptr)delete ptr;}
	PropertyWrapPtr(const string &_name,PType *_ptr):TProperty<PType>(_name),ptr(_ptr){;}
  	PropertyWrapPtr(const string &_name):TProperty<PType>(_name),ptr(NULL){;}
};


class Mask3D;
//! Image properties. Usually properties are mapped by name (string). 
/*! Exceptionally important properties like "Mask" are hardcoded */
class Properties : public Streamable
{
public:
	typedef ImageProperty Prop;
	typedef PropertyXML PropXML;
	mutable map<string,Prop *> properties;

	//! Erase all properties of this image
	void ClearProperties();

	//! Transforms an xml property to final property
	template<class PropClass>
	void Parse(const string &name)
	{
		map<string,Prop *>::iterator p=properties.find(name);
		if(p==properties.end()){ThrowError("Properties::Parse: could not find property:\"%s\"",name);}
		PropXML *propxml=(PropXML *)((*p).second);
		if(!propxml->IsXML())
		{
			mprintf("WARNING: Properties::Parse: \"%s\" not xml prop. Maybe it has already been parsed?\n",name);
			return;
		}
		properties.erase(p);
		properties[name]=new PropClass(name);
		ImLib3DFile file("","m");
		properties[name]->Read(&file,NULL,propxml->xml);
		delete propxml;
	}

	template<class PType>
	void DefaultParse(const string &name) 
	{
		Parse<PropertyWrapPtr<PType> >(name);
	}

	//! Retrieves a property that should descend from TProperty
	template<class PType>
	PType       &Get(const string &name)
	{
		map<string,Prop *>::iterator p=properties.find(name);
		if(p==properties.end()){ThrowError("Properties::Get: could not find property:\"%s\"",name);}
		if((*p).second->IsXML()){ThrowError("Properties::Get: unparsed property \"%s\", you must parse it before accesing it",name);}
		
		return ((TProperty<PType> *)(*p).second)->Get();
	}

	//! Retrieves a property that should descend from TProperty
	template<class PType>
	const PType  &Get(const string &name,PType dud=PType()) const
	{
		map<string,Prop *>::iterator p=properties.find(name);
		if(p==properties.end()){ThrowError("Properties::Get: could not find property:\"%s\"",name);}
		if((*p).second->IsXML()){ThrowError("Properties::Get: unparsed property \"%s\", you must parse it before accesing it",name);}
		
		return ((TProperty<PType> *)(*p).second)->Get();
	}

	bool Has(const string &propname) const {return properties.find(propname)!=properties.end();}

	//! Add a string property instead of a char* property
	void Add(const string &name,const char * str){Add(name,string(str));}

	//! Adds a new property using the automatic PropertyWrapPtr property wrapper
	template<class PType>
	void Add(const string &name,PType* ptr)
	{
		properties[name]=new PropertyWrapPtr<PType>(name,ptr);
	}
	//! Adds a new property using the automatic PropertyWrapPtr property wrapper
	template<class PType>
	void Add(const string &name,PType obj)
	{
		PType *ptr=new PType(obj);
//		*ptr=obj;
		Add(name,ptr);
	}

	//! remove a property
	void Remove(const string &name);

	Mask3D    *mask;
	Vect3Di   *iCenter;

  	void operator=(const Properties &other);
	bool operator==(const Properties &other) const;

	void Read( ImLib3DFile *file,XMLNode *parentNode=NULL,XMLNode *node=NULL);
	void Write(ImLib3DFile *file,XMLNode *parentNode=NULL,XMLNode *node=NULL) const;

  	void AddMask(const Size3D &size);

	virtual ~Properties(){ClearProperties();}

	template<class ImageType>
	Properties(ImageType *_image):
		mask(NULL),
		iCenter(NULL)
	{
		;
	}
};
#endif // _Properties_hpp
