#ifndef USER_INFO_H
#define USER_INFO_H

#include <stdio.h>
#include <string>
#include <typeinfo>
#include <map>

#include "libconfig/libconfig.h++"
#include "../bdmroot.h"
#include "itpp/itbase.h"


using std::string;
using namespace std;
using namespace libconfig;

#ifndef BDMLIB 
//!Macro for registration of classes into map of UserInfos -- registered class is scriptable 
#define UIREGISTER(class_name) template<> const Particular_UI<class_name>& Particular_UI<class_name>::ui = Particular_UI<class_name>(#class_name) 
#else
#define UIREGISTER(class_name)
#endif

#define ASSERT_UITYPE(S,Type) it_assert_debug(S.getType()==Setting::Type, string("Wrong setting type, see input path \"")+string(S.getPath())+string("\""))

namespace bdm
{
class UI_File : public Config
{
private:
	const string file_name;

public:
	//! create empty object prepared to store Settings
	UI_File();

	//! creates object and fills it from a file
	UI_File( const string &file_name );

	//! save UserInfo to the file 
	void save(const string &file_name);

	operator Setting&();
};

/*!
@brief This class serves to load and/or save DOMElements into/from files
stored on a hard-disk.

Firstly, you associate new RootElement instance with some filename during a time of its 
construtcion. Then, you save some object into the new RootElement instance,
and save it into the file this way:
\code
	CAudi audi;
	RootElement root("cars.xml");
	UserInfo::save( audi, root, "TT");
	root.save();
\endcode

In the other way round, when loading object from a XML file, the appropriate code looks like this:
\code
	RootElement root("cars.xml");
	root.load();
	UserInfo::build<T>(root,"TT");
\endcode
*/


/*!
@brief UserInfo is an abstract is for internal purposes only. Use CompoundUserInfo<T> or Particular_UI<T> instead.
The raison d'etre of this class is to allow pointers to its templated descendants. 

Also, the main functions of the whole UserInfo library are included within this class, see
static methods 'build' and 'save'.

*/
class UI 
{
private:
	//! static class encalupsating map of names to related UserInfos
	//! 
	//! The key property of this class is that it initilaized the internal map immediately
	//! when it is used for a first time.
	class Mapped_UI
	{
	private:
		//! Type definition of mapping which transforms type names to the related user infors
		typedef map< const string, const UI* const > String_To_UI_Map;

		//! Type definition of mapping which transforms type names to the related user infors
		typedef map< const type_info * const, const string > Type_Info_To_String_Map;

		//! immediately initialized instance of type String_To_UI_Map
		static String_To_UI_Map& mapped_strings();

		//! immediately initialized instance of type String_To_UI_Map
		static Type_Info_To_String_Map& mapped_type_infos();

	public:
		//! add a pair key-userinfo into the internal map
		static void add_class( const string &class_name, const type_info * const class_type_info, const UI* const ui );

		//! search for an userinfo related to the passed key within the internal map
		static const UI& retrieve_ui( const string &class_name );

		//! search for an userinfo related to the passed key within the internal map
		static const string& retrieve_class_name( const type_info* const class_type_info );
	};




	//! internal method assembling a typeless instance from components obtained by the 'AssemblyComponentsFromSetting()' method
	virtual bdmroot* new_instance() const = 0;
	
   	//! This methods tries to save an instance of type T (or some of its descendant types)
	//! and build DOM tree accordingly. Then, it creates a new DOMNode named according class_name
	//! and connecti it to the passed Setting as a new child node.
	static const Setting& to_child_setting( const Setting &element, const int index );

	static const Setting& to_child_setting( const Setting &element, const string &name );


	//! \name Matematical Operations TODO
	//!@{

	//! This methods tries to build a new double matrix 
	static void from_setting( mat& matrix, const Setting &element );	
	//! This methods tries to build a new integer vector
	static void from_setting( ivec &vector, const Setting &element );
	// jednak kvuli pretypovani, apak taky proto, ze na string nefunguje link_expander..
	static void from_setting( string &str, const Setting &element );
	//! This methods tries to build a new templated array 

	static void from_setting( vec &vector, const Setting &element );

	template<class T> static void from_setting( T* &instance, const Setting &element )
	{			
		const SettingResolver link( element );

		ASSERT_UITYPE(link.result,TypeGroup);

		// we get a velue stored in the "class" attribute 
		string class_name;
		if( !link.result.lookupValue( "class", class_name ) )
			ui_error( "the obligatory ""class"" identifier is missing", link.result );
	
		// and finally we find a UserInfo related to this type
		const UI& related_UI = Mapped_UI::retrieve_ui( class_name );
		
		bdmroot* typeless_instance = related_UI.new_instance();

		instance = NULL;
		try
		{
			instance = (T*) typeless_instance ;
		}
		catch(...)
		{
			it_error ( "UI error: class " + class_name + " is not a descendant of the desired output class. Try to call the UI::build function with a different type parameter." );
		}
		
		try
		{
			instance->from_setting( link.result );
		}
		catch(SettingException xcptn)
		{
			it_error ( "UI error: the method " + class_name + ".from_setting(Setting&) has thrown an exception when parsing the setting " + xcptn.getPath() + ". Try to correct this method." );
		}
	}	


	//! This methods tries to build a new templated array , 
	// efektivne jen pro vect, mat a string, pro dalsi je nutne pridat from_setting metodu.. ale to asi necceme
	template<class T> static void from_setting( Array<T> &array_to_load, const Setting &element )
	{
		const SettingResolver link( element );

		ASSERT_UITYPE(link.result,TypeList);

		int len = link.result.getLength();
		array_to_load.set_length( len );
		if( len == 0 ) return;
		
		for( int i=0; i < len; i++ ) 
			from_setting( array_to_load(i), link.result[i] ); 
	}

	  //!@}


	static void ui_error( string message, const Setting &element );

protected:
	//! default constructor 
	UI( const string& class_name, const type_info * const class_type_info ) 
	{	
		Mapped_UI::add_class( class_name, class_type_info, this );
	}

	//! Virtual destructor for future use;
	virtual ~UI(){};

public: 
	//! This methods tries to build a new instance of type T (or some of its descendant types)
	//! according to a data stored in a DOMNode named class_name within a child nodes of the passed element.
	//! If an error occurs, it returns a NULL pointer.

	// vraci true, kdyz to byl platny link, jinak false.. v pripade chyby konci it_errorem..
	// do elementu vrati setting prislusny po rozbaleni linku, jinak ponecha beze zmeny
	class SettingResolver	
	{
	private:
		UI_File *file;

		const Setting &initialize_reference( UI_File* &file, const Setting &potential_link);

	public:
		const Setting &result;

		SettingResolver( const Setting &potential_link );
		
		~SettingResolver();		
	};



	//! Prototype of a UI builder. Return value is by the second argument since it type checking via \c dynamic_cast.
	template<class T> static T* build( const Setting &element, const int index )
	{
		T* instance;
		from_setting<T>( instance, to_child_setting( element, index ) );
		return instance;
	}

	template<class T> static T* build( const Setting &element, const string &name )
	{			
		T* instance;
		from_setting<T>( instance, to_child_setting( element, name ) );
		return instance;
	}

	//! This methods tries to build a new double matrix 
	template<class T> static void get( T &instance, const Setting &element, const string &name )
	{
		from_setting( instance, to_child_setting( element, name ) );
	}

	//! This methods tries to build a new double matrix 
	template<class T> static void get( T &instance, const Setting &element, const int index )
	{
		from_setting( instance, to_child_setting( element, index ) );
	}

	//! This methods tries to build a new double matrix 
	template<class T> static void get( Array<T> &array_to_load, const Setting &element, const string &name )
	{
		from_setting( array_to_load, to_child_setting( element, name ) );
	}

	//! This methods tries to build a new double matrix 
	template<class T> static void get( Array<T> &array_to_load, const Setting &element, const int index )
	{
		from_setting( array_to_load, to_child_setting( element, index ) );
	}

	template< class T> static void save( const T * const instance, Setting &element, const string &name = "")
	{
		Setting &set = (name == "") ? element.add( Setting::TypeGroup )							
									 : element.add( name, Setting::TypeGroup );		

		const string &class_name = Mapped_UI::retrieve_class_name( &typeid(*instance) );
			
		// add attribute "class" 
		Setting &type = set.add( "class", Setting::TypeString );
		type = class_name;

		try
		{
			instance->to_setting( set );
		}
		catch(SettingException xcptn)
		{
			it_error ( "UI error: the method " + class_name + ".to_setting(Setting&) has thrown an exception when filling the setting " + xcptn.getPath() + ". Try to correct this method." );
		}	
	}

	//! This methods tries to save a double vec 
	template<class T> static void save( const Array<T> &array_to_save, Setting &element, const string &name = "" )
	{
		ASSERT_UITYPE(element,TypeGroup);
		Setting &list = (name == "") ? element.add( Setting::TypeList )							
									 : element.add( name, Setting::TypeList );		
		for( int i=0; i<array_to_save.length(); i++ ) 
			save( array_to_save(i), list );
	}


	//! This methods tries to save a double matrix 
	static void save( const mat &matrix, Setting &element, const string &name = "" );

	//! This methods tries to save a double vec 
	static void save( const ivec &vec, Setting &element, const string &name = "" );
	
	static void save( const vec &vector, Setting &element, const string &name);

	private: 
	//! This methods tries to save a double vec 
	static void save( const string &str, Setting &element);	

};


/*!
@brief The main userinfo template class. You should derive this class whenever you need 
a new userinfo of a class which is compound from smaller elements (all having its
own userinfo class prepared).
*/
template<typename T> class Particular_UI : private UI
{
	public:

	//! default constructor, which is intentionally declared as private
	Particular_UI<T>( const string &class_name) : UI( class_name, &typeid(T) ) 
	{	cout << class_name << endl;
	};

	//! the only instance of this class (each type T has its own instance)
	//! which is used as a factory for processing related UI
	static const Particular_UI<T>& ui;	

	bdmroot* new_instance() const
	{
		return new T();
	}
};




}

/*! Recursive build of objects defined in the same file

\code 
{type="internal";
path="system.profile.[0]";    // Path from the root 
};
\endcode
 */



/*! Recursive build of objects defined in external file

\code 
{type="external";
filename="my_file.cfg";       // name of file from which to read
path="system.profile.[0]";    // Path in the external file
};
\endcode
/

*/

#endif // #ifndef USER_INFO_H
