/*!
  \file
  \brief UI (user info) class for loading/saving objects from/to configuration files.
  It is designed with use of libconfig C/C++ Configuration File Library
  \ref ui_page
  \author Vaclav Smidl.

  -----------------------------------
  BDM++ - C++ library for Bayesian Decision Making under Uncertainty

  Using IT++ for numerical operations
  -----------------------------------
*/

#ifndef USER_INFO_H
#define USER_INFO_H

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

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


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

namespace bdm {

//! Generic exception for reporting configuration errors
//!
//!  \ref ui_page
class UIException : public std::exception {
private:
	//! Error message
	const string message;

public:
	/*!
	  \brief The constructor
	  \param message the reason for throwing the exception. Should be a complete English sentence (or a couple sentences), starting with "UIException: ".
	*/
	UIException ( const string &message ) :
		message ( message ) {
	}

	//! Overriden method for reporting the error message
	virtual const char* what() const throw() {
		return message.c_str();
	}

	~UIException() throw() {};

protected:
	/*!
	  Formats error messages for derived classes, which use a
	  Setting path in addition to the message.
	*/
	static string format_message( const string &reason, const string &path );
};

//! Exception for reporting configuration errors related to some concrete Setting path
//!
//!  \ref ui_page
class UISettingException : public UIException {
public:
	//! Use this constructor when you can pass the problematical Setting as a parameter
	UISettingException ( const string &message, const Setting &element ):
		UIException ( format_message ( message, string ( element.getPath() ) ) ) {
	}

	//! This constructor is for other occasions, when only path of problematical Setting is known
	UISettingException ( const string &message, const string &path ):
		UIException ( format_message ( message, path ) ) {
	}

	~UISettingException() throw() {};
};

//! Exception for reporting configuration errors in the "class" attribute
//!
//!  \ref ui_page
class UIClassException : public UIException {
public:
	//! Use this constructor when you can pass the problematical Setting as a parameter
	UIClassException ( const string &message, const Setting &element ):
		UIException ( format_message ( message, string ( element.getPath() ) ) ) {
	}

	//! This constructor is for other occasions, when only path of problematical Setting is known
	UIClassException ( const string &message, const string &path ):
		UIException ( format_message ( message, path ) ) {
	}

	~UIClassException() throw() {};
};

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

Firstly, save some user-infos into the new UIFile instance. Then,
call the save method with a filename as its only argument:

\code
	CAudi audi;
	UIFile file;
	UI::save( audi, file, "TT");
	file.save("cars.cfg");
\endcode

In the other way round, when loading object from a configuration file,
the appropriate code looks like this:

\code
	UIFile file("cars.cfg");
	CAudi *audi = UI::build<CAudi>(file,"TT");
\endcode

\ref ui_page
*/
class UIFile : public Config {
public:
	//! Create empty file instance prepared to store Settings
	UIFile();

	//! Creates instance and fills it from the configuration file file_name
	UIFile ( const string &file_name );

	//! Save all the stored Settings into the configuration file file_name
	void save ( const string &file_name );

	//! This operator allows the ability of substituting Setting parameter by UIFile instance
	operator Setting&();
};

/*!
@brief This class serves to expand links used within configuration files.

Value of any type but string can be linked to some other value of the same type
defined elsewhere in the current configuration file or even in some different
configuration file.

Link have three parts, \<name\> : \<path\> \<\@filename\>. Field \<name\> contains the
name of the new setting, \<path\> is the relative path to the referenced setting, which
has to be taken from the %root Setting element. The last part \<\@filename\> is optional,
it contains filename in the case the link should refer to a variable stored in a different
file. From the previous part \<path\>, it has to be separated by '@'.

\code
    ...
	jardovo :
	{
	  class = "Car";
	  year = 1992;
	  manufacturer = "liaz";
	  kilometers = 1555000;
	};
	ondrejovo :
	{
	  class = "Bike";
	  year = 1996;
	  manufacturer = "author";
	  electricLights = true;
	  matr = ( 2, 2, [ 1.0, 0.0, 0.0, 1.0 ] );
	};

	#this is the example of local link to another mean of transport
	elisky = "jardovo";

	...

	# And this link is external link pointing to the file "other_cars.cfg" stored in the
	# same directory. In that file, it refers to the local Setting "magic_cars.skubankovo".
	kati = "magic_cars.skubankovo@other_cars.cfg";

    ...
\endcode

When you want to expand a possible linked setting "element" within your code, it has to be treated this way:

\code
	...

	const SettingResolver link( element );

	...

	int len = link.result.getLength();

	...
\endcode

The whole point is that a resolved link (class member #result, i.e., "link.result" in the previous example) could point
into a different configuration file. In that case there has to be an UIFile instance managing reading from this
file. As the libconfig::Config deletes all its Settings when dealocated, UIFile must not be dealocated until all
the necessary operation on the linked Setting are finished (otherwise, the link #result would be invalid just after
the UIFile dealocation). And that is exactly the mechanism implemented within SettingResolver class. It assures,
that the #result Setting reference is valid within the scope of SettingResolver instance.

\ref ui_page
 */
class SettingResolver : root {
private:
	//! If necessary, this pointer stores an addres of an opened UIFile, else it equals NULL
	UIFile *file;

	//! This method initialize #result reference, i.e., it executes the main code of SettingResolver class
	//!
	//! This code could be also located directly in constructor. The only reason why we made this
	//! method is the keyword 'const' within the declaration of #result reference . Such a reference
	//! have to be intialized before any other constructor command, exactly in the way it is implemented now.
	const Setting &initialize_reference ( UIFile* &file, const Setting &potential_link );

public:
	//! Reference to a resolved link or to the original Setting in the case it does not contain a link
	const Setting &result;

	//! If potential_link contains a link to some other setting, it is resolved here. Anyway, the Setting reference #result is prepared for use.
	SettingResolver ( const Setting &potential_link );

	//! An opened UIFile file is closed here if necessary.
	~SettingResolver();
};

/*!
@brief UI is an abstract class which collects all the auxiliary functions useful to prepare some concrete
user-infos.

See static methods 'build', 'get' and 'save'. Writing user-infos with these methods is rather simple. The
rest of this class is intended for internal purposes only. Its meaning is to allow pointers to its templated
descendant ParticularUI<T>.

\ref ui_page
*/
class UI {
private:
	//! Class with state shared across all its instances ("monostate"), encapsulating two maps, one mapping names to UI instances and the other mapping type_infos to class names
	//!
	//! The key property of this class is that it initializes the internal maps on global init,
	//! before the instance is used for a first time. Therefore, we do not have to care about initialization
	//! during a call of UIREGISTER macro operating with both these mappings.
	class MappedUI {
	private:
		//! Type definition of mapping which transforms class names to the related UI instances
		typedef map< const string, const UI* const > StringToUIMap;

		//! Type definition of mapping which transforms RTTI type_infos to the related class names
		typedef map< const type_info * const, const string > TypeInfoToStringMap;

		//! Immediately initialized instance of type StringToUIMap
		static StringToUIMap& mapped_strings();

		//! Immediately initialized instance of type TypeInfoToStringMap
		static TypeInfoToStringMap& mapped_type_infos();

		//! Method for reporting a error when an attempt to operate with an unregistered class occures
		static void unregistered_class_error ( const string &unregistered_class_name );

	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 class name within the internal map
		static const UI& retrieve_ui ( const string &class_name );

		//! Search for an class name related to the passed type_info within the internal map
		static const string& retrieve_class_name ( const type_info* const class_type_info );
	};

	//! Function assertting that the setting element is of the SettingType type
	static void assert_type ( const Setting &element, Setting::Type type );

	/*!
	  \brief Method constructing a configured instance

	  The returned pointer must be allocated using operator new
	  (it's deleted at the end of its life cycle). The method is
	  implemented in descendant class ParticularUI<T>, which knows
	  the correct type T.
	*/
	virtual root* new_instance() const = 0;

	//! Method switching from the \a element to its child Setting according the passed \a index, it also does all the necessary error-checking
	static const Setting& to_child_setting ( const Setting &element, const int index );

	//! Method switching from the \a element to its child Setting according the passed \a name, it also does all the necessary error-checking
	static const Setting& to_child_setting ( const Setting &element, const string &name );

	//! This method converts a Setting into a matrix
	static void from_setting ( mat& matrix, const Setting &element );
	//! This method converts a Setting into an integer vector
	static void from_setting ( ivec &vector, const Setting &element );
	//! This method converts a Setting into a string
	static void from_setting ( string &str, const Setting &element );
	//! This method converts a Setting into a real vector
	static void from_setting ( vec &vector, const Setting &element );
	//! This method converts a Setting into a integer scalar
	static void from_setting ( int &integer, const Setting &element );
	//! This method converts a Setting into a real scalar
	static void from_setting ( double &real, const Setting &element );

	//! This method converts a Setting into a class T descendant
	template<class T> static void from_setting ( T* &instance, const Setting &element ) {
		const SettingResolver link ( element );
		assert_type ( link.result, Setting::TypeGroup );

		// we get a value stored in the "class" attribute
		string class_name;
		if ( !link.result.lookupValue ( "class", class_name ) )
			throw UIClassException ( "UIException: the obligatory \"class\" identifier is missing.", link.result );

		// then we find a user-info related to this type
		const UI& related_UI = MappedUI::retrieve_ui ( class_name );

		root *typeless_instance = related_UI.new_instance();
		bdm_assert_debug ( typeless_instance, "UI::new_instance failed" );

		instance = dynamic_cast<T*> ( typeless_instance );
		if ( !instance ) {
			delete typeless_instance;
			throw UIClassException ( "UIException: class " + class_name + " is not a descendant of the desired output class. Try to call the UI::build<T> function with a different type parameter.", link.result );
		}

		try {
			instance->from_setting ( link.result );
		} catch ( SettingException &sttng_xcptn ) {
			delete instance;
			instance = 0;
			string msg = "UIException: method ";
			msg += class_name;
			msg += ".from_setting(Setting&) has thrown a SettingException.";
			throw UISettingException(msg, sttng_xcptn.getPath());
		} catch (...) {
			delete instance;
			instance = 0;
			throw;
		}
	}

	//! This method converts a Setting into a descendant of class
	//! T, wrapped in an instance of shared_ptr<T> .
	template<class T>
	static void from_setting ( shared_ptr<T> &instance, const Setting &element ) {
		T *tmp_inst = 0;
		from_setting ( tmp_inst, element );
		bdm_assert_debug ( tmp_inst, "UI::from_setting failed" );
		instance = tmp_inst;
	}

	//! This methods converts a Setting into a new templated array of type Array<T>
	template<class T> static void from_setting ( Array<T> &array_to_load, const Setting &element ) {
		const SettingResolver link ( element );

		assert_type ( link.result, Setting::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] );
	}

	//! This is dummy version of the from_setting method for other, unsupported types. It just throws an exception.
	//!
	//! At the moment, this is the only way how to compile the library without obtaining the compiler error c2665.
	//! The exception can help to find the place where the template is misused and also to correct it.
	template<class T> static void from_setting ( T &variable_to_load, const Setting &element ) {
		std::string msg = "UIException: from_setting is not implemented for type ";
		msg += typeid(T).name();
		msg += '.';
		throw UISettingException ( msg, element );
	}


protected:
	//! Constructor for internal use only, see \sa ParticularUI<T>
	UI ( const string& class_name, const type_info * const class_type_info ) {
		MappedUI::add_class ( class_name, class_type_info, this );
	}

public:

	//! Enum type used to determine whether the data for concrete Settingis is compulsory or optional
	enum SettingPresence { optional, compulsory } ;

	//! \name Initialization of classes
	//!@{
	//! The type T has to be a #bdm::root descendant class

	//! The new instance of type T* is constructed and initialized with values stored in the Setting element[name]
	//!
	//! If there is not any sub-element named #name and settingPresence is #optional, an empty shared_ptr<T> is returned. When settingPresence is #compulsory, the returned shared_ptr<T> is never empty (an exception is thrown when the object isn't found).
	template<class T>
	static shared_ptr<T> build ( const Setting &element, const string &name, SettingPresence settingPresence = optional ) {
		if ( !element.exists ( name ) ) {
			if ( settingPresence == optional )
				return shared_ptr<T>();
			else
				throw UISettingException ( "UIException: the compulsory Setting named \"" + name + "\" is missing.", element );
		}

		shared_ptr<T> instance;
		from_setting<T> ( instance, to_child_setting ( element, name ) );
		return instance;
	}

	//! The new instance of type T* is constructed and initialized with values stored in the Setting element[index]
	//!
	//! If there is not any sub-element indexed by #index, and settingPresence is #optional, an empty shared_ptr<T> is returned. When settingPresence is #compulsory, the returned shared_ptr<T> is never empty (an exception is thrown when the object isn't found).
	template<class T>
	static shared_ptr<T> build ( const Setting &element, const int index, SettingPresence settingPresence = optional ) {
		if ( element.getLength() <= index ) {
			if ( settingPresence == optional )
				return shared_ptr<T>();
			else {
				stringstream stream;
				stream << index;
				throw UISettingException ( "UIException: the compulsory Setting with the index " + stream.str() + " is missing.", element );
			}
		}

		shared_ptr<T> instance;
		from_setting<T> ( instance, to_child_setting ( element, index ) );
		return instance;
	}

	//!@}

	//! \name Initialization of structures
	//!@{
	//! The type T has to be int, double, string, vec, ivec or mat.

	//! The existing instance of type T is initialized with values stored in the Setting element[name]
	//! If there is not any sub-element named #name, this method returns false.
	template<class T> static bool get ( T &instance, const Setting &element, const string &name, SettingPresence settingPresence = optional ) {
		if ( !element.exists ( name ) ) {
			if ( settingPresence == optional )
				return false;
			else
				throw UISettingException ( "UIException: the compulsory Setting named \"" + name + "\" is missing.", element );
		}

		from_setting ( instance, to_child_setting ( element, name ) );
		return true;
	}

	//! The existing instance of type T is initialized with values stored in the Setting element[index]
	//! If there is not any sub-element indexed by #index, this method returns false.
	template<class T> static bool get ( T &instance, const Setting &element, const int index, SettingPresence settingPresence = optional ) {
		if ( element.getLength() <= index ) {
			if ( settingPresence == optional )
				return false;
			else {
				stringstream stream;
				stream << "UIException: the compulsory Setting with the index " << index << " is missing.";
				stream << index;
				throw UISettingException (stream.str(), element );
			}
		}

		from_setting ( instance, to_child_setting ( element, index ) );
		return true;
	}

	//! The existing instance of type T is initialized with values stored in the Setting element directly
	template<class T> static bool get ( T &instance, const Setting &element ) {
		from_setting ( instance, element );
		return true;
	}
	//!@}

	//! \name Initialization of arrays Array<T>
	//!@{
	//! The type T has to be int, double, string, vec, ivec or mat, or pointer to any root descendant.

	//! The existing array of type T is initialized with values stored in the Setting element[name]
	//! If there is not any sub-element named #name, this method returns false.
	template<class T> static bool get ( Array<T> &array_to_load, const Setting &element, const string &name, SettingPresence settingPresence = optional ) {
		if ( !element.exists ( name ) )
			return false;

		from_setting ( array_to_load, to_child_setting ( element, name ) );
		return true;
	}

	//! The existing array of type T is initialized with values stored in the Setting element[index]
	//! If there is not any sub-element indexed by #index, this method returns false.
	template<class T> static bool get ( Array<T> &array_to_load, const Setting &element, const int index, SettingPresence settingPresence = optional ) {
		if ( element.getLength() <= index )
			return false;

		from_setting ( array_to_load, to_child_setting ( element, index ) );
		return true;
	}

	//! The existing array of type T is initialized with values stored in the Setting element
	template<class T> static bool get ( Array<T> &array_to_load, const Setting &element ) {
		from_setting ( array_to_load, element );
		return true;
	}
	//!@}

	//! \name Serialization of objects and structures into a new Setting
	//!@{
	//! The new child Setting can be accessed either by its name - if some name is passed as a parameter -
	//! or by its integer index. In that case, the new element is added at the very end of the current list of child Settings.

	//! A root descendant instance is stored in the new child Setting appended to the passed element
	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 = MappedUI::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 &sttng_xcptn ) {
			string msg = "UIException: method ";
			msg += class_name;
			msg += ".to_setting(Setting&) has thrown a SettingException.";
			throw UISettingException(msg, sttng_xcptn.getPath());
		}
	}

	template< class T> static void save ( const shared_ptr<T> &instance, Setting &element, const string &name = "" ) {
		save<T> ( instance.get(), element, name );
	}

	//! An Array<T> instance is stored in the new child Setting appended to the passed element
	template<class T> static void save ( const Array<T> &array_to_save, Setting &element, const string &name = "" ) {
		assert_type ( element, Setting::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 );
	}

	//! A matrix(of type mat) is stored in the new child Setting appended to the passed element
	static void save ( const mat &matrix, Setting &element, const string &name = "" );

	//! An integer vector (of type ivec) is stored in the new child Setting appended to the passed element
	static void save ( const ivec &vec, Setting &element, const string &name = "" );

	//! A double vector (of type vec) is stored in the new child Setting appended to the passed element
	static void save ( const vec &vector, Setting &element, const string &name = "" );

	//! A string is stored in the new child Setting appended to the passed element
	static void save ( const string &str, Setting &element, const string &name = "" );

	//! An integer is stored in the new child Setting appended to the passed element
	static void save ( const int &integer, Setting &element, const string &name = "" );

	//! A double is stored in the new child Setting appended to the passed element
	static void save ( const double &real, Setting &element, const string &name = "" );
	//!@}

};


//! The only UI descendant class which is not intended for direct use. It should be accessed within the UIREGISTER macro only.
//! \ref ui_page
template<typename T> class ParticularUI : private UI {
public:
	//! Constructor used by the UIREGISTER macro.
	ParticularUI<T> ( const string &class_name ) : UI ( class_name, &typeid ( T ) ) {};

	//! A method returning a brand new instance of class T, this method is the reason why there have to be a parameterless constructor in class T
	root* new_instance() const {
		return new T();
	}
};

}

/*!
  \def UIREGISTER(class_name)
  \brief Macro for registration of class into map of user-infos, registered class is scriptable using UI static methods

  Argument \a class_name has to be a descendant of root class and also to have a default constructor.
  This macro should be used in header file, immediately after a class declaration.

  \ref ui_page
*/
#ifndef BDMLIB
#define UIREGISTER(class_name) static bdm::ParticularUI<class_name> UI##class_name(#class_name)
#else
#define UIREGISTER(class_name)
#endif

//! Instrumental macro for UIREGISTER2
#define QUOTEME(x) #x

/*!
  \def UIREGISTER2(class_name,template_name)
  \brief Variant of UIREGISTER for templated classes

  Technical meann of registering UIREGISTER(class_name<template_name>).

  \ref ui_page
 */
#ifndef BDMLIB
#define UIREGISTER2(class_name, temp_name) static bdm::ParticularUI<class_name<temp_name> > UI##class_name##_##temp_name( QUOTEME(class_name<temp_name>) )
#else
#define UIREGISTER2(class_name,temp_name) 
#endif

#endif // #ifndef USER_INFO_H
