#ifndef UIBUILD
#define UIBUILD

#include <itpp/itbase.h>
#include "stat/libBM.h"
#include "libconfig/libconfig.h++"

namespace bdm {

	using namespace libconfig;
	using namespace std;

#define CHECK_UITYPE(S,Type) it_assert_debug(S.getType()==Setting::Type, string("Wrong input path \"")+string(S.getPath())+string("\""));
#define UIREGISTER(UI) UI* UI##_global_instance = new UI();

////////// GLOBAL VAriables

	class UIbuilder;
//! Internal structure mapping strings to UIBuilder objects
	typedef map<const string, const UIbuilder*> UImap;
	extern UImap __uimap__;

	class UIFile : public Config {
	public:
		UIFile ( const char * fname ) :Config() {
			try{Config::readFile ( fname );}
			catch ( ParseException& P ) {
				char msg[200];
				sprintf ( msg,"Error in file %s  on line %d.", fname, P.getLine() );
				it_error ( msg );
			}
			catch ( FileIOException f ) {it_error ( "File " + string ( fname ) + " not found" );}
		}
	};

	/*!\brief Builds computational object from a UserInfo structure

	Return value is a pointer to the created object (memory management issue?)
	*/
	class UIbuilder {
	protected:
		const vec getvec ( Setting& S ) const {
			CHECK_UITYPE ( S,TypeArray );
			vec tmp;
			tmp.set_size ( S.getLength() );
			for ( int i=0;i<S.getLength();i++ ) {
				switch ( S[i].getType() ) {
					case Setting::TypeFloat :
						tmp[i]=double ( S[i] );break;
					case Setting::TypeInt :
						tmp[i]=int ( S[i] );break;
					case Setting::TypeBoolean :
						tmp[i]=bool ( S[i] );break;
					default: it_error ( "libconfig error?" );
				}
			}
			return tmp;
		};
		const mat getmat ( Setting& S , int ncols) const {
			CHECK_UITYPE ( S,TypeArray );
			mat tmp;
			int nrows=S.getLength()/ncols;
			int r=0,c=0;
			tmp.set_size ( nrows, ncols );
			// Build matrix row-wise
			for ( int i=0;i<S.getLength();i++ ) {
				switch ( S[i].getType() ) {
					case Setting::TypeFloat :
						tmp(r,c)=double ( S[i] );break;
					case Setting::TypeInt :
						tmp(r,c)=int ( S[i] );break;
					case Setting::TypeBoolean :
						tmp(r,c)=bool ( S[i] );break;
					default: it_error ( "libconfig error?" );
				}
				c++; if (c==ncols) {c=0;r++;}
			}
			return tmp;
		};
		const ivec getivec ( Setting& S ) const {
			CHECK_UITYPE ( S,TypeArray );
			ivec tmp;
			tmp.set_size ( S.getLength() );
			for ( int i=0;i<S.getLength();i++ ) {
				switch ( S[i].getType() ) {
					case Setting::TypeFloat :
						tmp[i]=double ( S[i] );break;
					case Setting::TypeInt :
						tmp[i]=int ( S[i] );break;
					case Setting::TypeBoolean :
						tmp[i]=bool ( S[i] );break;
					default: it_error ( "libconfig error?" );
				}
			}
			return tmp;
		};
	public:
		//!Constructor needs to be run only once macro UIREGISTER
		UIbuilder ( const string &typ ) {__uimap__.insert ( make_pair ( typ,this ) );}
		//! Function building the computational object
		virtual bdmroot* build ( Setting &S ) const =0;
	};

	class UIexternal:public UIbuilder {
	public:
		UIexternal() :UIbuilder ( "external" ) {}
		bdmroot* build ( Setting &S ) const;
	};

	class UIinternal:public UIbuilder {
	public:
		UIinternal() :UIbuilder ( "internal" ) {}
		bdmroot* build ( Setting &S ) const;
	};

	//! [Debugging] Print values in current S to cout
	void UI_DBG ( Setting &S, const string &spc );

//! Prototype of a UI builder. Return value is by the second argument since it type checking via \c dynamic_cast.
	template<class T>
	void UIbuild ( Setting &S, T* &ret ) {
		CHECK_UITYPE ( S,TypeGroup );
		// Check if field "type" is present, if not it is not a valid UI
		it_assert_debug ( S.exists ( "type" ), string ( S.getPath() ) +" is not a valid UI!" );

		const string typ=S["type"];
		// Find "type" in list of registred UI builders
		UImap::const_iterator iter = __uimap__.find ( typ );
		if ( iter == __uimap__.end() ) {
			it_error ( "UI of type \"" + typ + "\" is not registered!" );
		}

		//BUILD the result
		try {
			ret = dynamic_cast<T*> ( iter->second->build ( S ) );
		}
		catch ( SettingTypeException e ) {
			UI_DBG(S,""); 
			it_error ( "Setting " +string ( e.getPath() ) +" is of incorrect Type" );}
		catch ( SettingNotFoundException e ) {
			UI_DBG(S,"");
			it_error ( "Setting " + string ( e.getPath() ) +" was not found" );}
	};

//! Auxiliary function allowing recursivity in S (too complex, remove?)
	template<class T>
	void UIcall ( Setting &S, void ( *func ) ( Setting&, T ), T Tmp ) {
		CHECK_UITYPE ( S,TypeGroup );
		// Check if field "type" is present, if not it is not a valid UI
		it_assert_debug ( S.exists ( "type" ), string ( S.getPath() ) +" is not a valid UI!" );

		const string typ=S["type"];
		if ( typ=="internal" ) {
			try {
				Setting* Stmp = &S;
				do {Stmp=& ( Stmp->getParent() );}
				while ( !Stmp->isRoot() );
				Setting& intS=Stmp->lookup ( ( const char* ) S["path"] );
				func ( intS, Tmp ); // <======== calling func
				return;
			}
			catch ( ... ) {
				it_error ( "Internal field " + string ( S.getPath() ) + " not valid" );
			}
		}
		if ( typ=="external" ) {
			UIFile C(S["filename"]);
			try {
				func ( C.lookup ( ( const char* ) S["path"] ), Tmp );
			}
			catch ( ... ) {
				it_error ( "External field " + string ( S.getPath() ) + " not valid" );
			}
			return;
		}

		// v======================= calling final func
		func ( S, Tmp );
	};

}
#endif //UIBUILD
