/*
 * $Id: environment.h 4813 2017-12-19 22:30:40Z antonin.misek-skoda-auto.cz $
 *
 * Description  : One line description of file.
 * Author       : Vaclav Kyba <mail/Jabber: vaseo1@gmail.com>  <ICQ: 98576293>
 *
 * Purpose      :
 *     Long description of what the file is for.
 */


#ifndef __ENVIRONMENT_H__
#define __ENVIRONMENT_H__

//#include <wx/hashmap.h>
#include <vector>
#include "parameter.h"
#include "VrutApi.h"


///Register module's parameter with its default value using events
#ifndef REGISTER_PARAM
#define REGISTER_PARAM(paramId, pName, value, pDescription) \
	{ paramId.poolName=VRUT::Module::GetName();\
	  paramId.paramName=pName;\
	  paramId.instName=wxString::Format(wxT("%lu"), (unsigned long)GetID().GetInstanceID());\
	  paramId.description=pDescription;\
	  value=KERNEL->environment.SetParam(paramId, value); }
	  /*PostToKernel(GET_EVT_PARAM_REGISTER(paramId, value), true); }*/
#endif // REGISTER_PARAM

///Set module's parameter new value
#ifndef SET_PARAM
#define SET_PARAM(paramId, pValue) \
	do { \
		wxString value = pValue; \
		value=KERNEL->environment.SetParam(paramId, value); \
	} while (false)
	/*PostToKernel(GET_EVT_PARAM_SET_DO(paramId, value)); */
#endif // SET_PARAM

///Set module's parameter new value, set origin ID
#ifndef SET_PARAM_WITH_ORIGIN
#define SET_PARAM_WITH_ORIGIN(paramId, pValue, pOriginId) \
	do { \
		wxString value = pValue; \
		value=KERNEL->environment.SetParam(paramId, value, pOriginId); \
	} while (false)
#endif // SET_PARAM_WITH_ORIGIN

///Check if event is to update given parameter and update it accordingly - \c wxString version
///Use when processing \c EVT_PARAM_SET event only
#ifndef UPDATE_PARAM_FROM_EVENT_STRING
#define UPDATE_PARAM_FROM_EVENT_STRING(paramId, target, event) \
	{ \
		if (((EventParamSet*)event)->paramIdentifier==paramId) \
			target = ((EventParamSet*)event)->value; \
	}
#endif // UPDATE_PARAM_FROM_EVENT_STRING

///Check if event is to update given parameter and update it accordingly - \c integer version
///Use when processing \c EVT_PARAM_SET event only
#ifndef UPDATE_PARAM_FROM_EVENT_INT
#define UPDATE_PARAM_FROM_EVENT_INT(paramId, target, event) \
	{ \
		if (((EventParamSet*)event)->paramIdentifier==paramId) \
		{ \
			int val = 0; \
			TextParser textParser(((EventParamSet*)event)->value.utf8_str()); \
			if (textParser.ParseInt(&val)) \
				target = val; \
			else \
				LOGERROR(wxT("<Param>Invalid parameter type (INT)")+paramId.ToString()); \
		} \
	}
#endif // UPDATE_PARAM_FROM_EVENT_INT

///Check if event is to update given parameter and update it accordingly - \c long version
///Use when processing \c EVT_PARAM_SET event only
#ifndef UPDATE_PARAM_FROM_EVENT_ULONG
#define UPDATE_PARAM_FROM_EVENT_ULONG(paramId, target, event) \
	{ \
		if (((EventParamSet*)event)->paramIdentifier==paramId) \
		{ \
			unsigned long val = 0; \
			TextParser textParser(((EventParamSet*)event)->value.utf8_str()); \
			if (textParser.ParseULong(&val)) \
				target = val; \
			else \
				LOGERROR(wxT("<Param>Invalid parameter type (ULONG)")+paramId.ToString()); \
		} \
	}
#endif // UPDATE_PARAM_FROM_EVENT_ULONG

///Check if event is to update given parameter and update it accordingly - \c unsigned version
///Use when processing \c EVT_PARAM_SET event only
#ifndef UPDATE_PARAM_FROM_EVENT_UNSIGNED
#define UPDATE_PARAM_FROM_EVENT_UNSIGNED(paramId, target, event) \
	{ \
		if (((EventParamSet*)event)->paramIdentifier==paramId) \
		{ \
			long val = 0; \
			TextParser textParser(((EventParamSet*)event)->value.utf8_str()); \
			if (textParser.ParseLong(&val)) \
				target = unsigned(val); \
			else \
				LOGERROR(wxT("<Param>Invalid parameter type (UNSIGNED)")+paramId.ToString()); \
		} \
	}
#endif // UPDATE_PARAM_FROM_EVENT_UNSIGNED

///Check if event is to update given parameter and update it accordingly - \c general ID version
///Use when processing \c EVT_PARAM_SET event only
#ifndef UPDATE_PARAM_FROM_EVENT_ID
#define UPDATE_PARAM_FROM_EVENT_ID(paramId, target, event) \
	{ \
		if (((EventParamSet*)event)->paramIdentifier==paramId) \
		{ \
			long val = 0; \
			TextParser tParser(((EventParamSet*)event)->value.utf8_str()); \
			if (!tParser.IsEmpty()) { \
				if (tParser.ParseLong(&val)) \
					target = VRUT::_ID(val); \
				else \
					LOGERROR(wxT("<Param>Invalid parameter type (ID)")+paramId.ToString()); \
			} \
		} \
	}
#endif // UPDATE_PARAM_FROM_EVENT_ID

///Check if event is to update given parameter and update it accordingly - \c float version
///Use when processing \c EVT_PARAM_SET event only
#ifndef UPDATE_PARAM_FROM_EVENT_FLOAT
#define UPDATE_PARAM_FROM_EVENT_FLOAT(paramId, target, event) \
	{ \
		if (((EventParamSet*)event)->paramIdentifier==paramId) \
		{ \
			float val = 0; \
			TextParser textParser(((EventParamSet*)event)->value.utf8_str()); \
			if (textParser.ParseFloat(&val)) \
				target = val; \
			else \
				LOGERROR(wxT("<Param>Invalid parameter type (FLOAT) ")+paramId.ToString()+wxT(" ")+((EventParamSet*)event)->value); \
		} \
	}
#endif // UPDATE_PARAM_FROM_EVENT_FLOAT

///Check if event is to update given parameter and update it accordingly - \c float version
///Use when processing \c EVT_PARAM_SET event only
#ifndef UPDATE_PARAM_FROM_EVENT_VECTOR
#define UPDATE_PARAM_FROM_EVENT_VECTOR(paramId, target, dim, event) \
	{ \
		if (((EventParamSet*)event)->paramIdentifier==paramId) \
		{ \
			float *val = new float[dim]; \
			TextParser textParser(((EventParamSet*)event)->value.utf8_str()); \
			if (textParser.ParseVector(val, dim)==dim) \
				for (int i=0; i<dim; i++) target[i] = val[i]; \
			else \
				LOGERROR(wxT("<Param>Invalid parameter type (VECTOR)")+paramId.ToString()); \
			delete [] val; \
		} \
	}
#endif // UPDATE_PARAM_FROM_EVENT_VECTOR

///Check if event is to update given parameter and update it accordingly - \c bool version
///Use when processing \c EVT_PARAM_SET event only
#ifndef UPDATE_PARAM_FROM_EVENT_BOOL
#define UPDATE_PARAM_FROM_EVENT_BOOL(paramId, target, event) \
	{ \
		if (((EventParamSet*)event)->paramIdentifier==paramId) \
			target = isTrue(((EventParamSet*)event)->value); \
	}
#endif // UPDATE_PARAM_FROM_EVENT_BOOL

///Check if event is to update given parameter and update it accordingly - \c table version (col, row and wxString)
///Use when processing \c EVT_PARAM_SET event only
#ifndef UPDATE_PARAM_FROM_EVENT_TABLE
#define UPDATE_PARAM_FROM_EVENT_TABLE(paramId, col, row, target, event) \
	{ \
		if (((EventParamSet*)event)->paramIdentifier==paramId) \
		{\
			TextParser tmpParser(((EventParamSet*)event)->value.utf8_str()); \
			if (!tmpParser.ParseLong(&col))\
				col=-1;\
			tmpParser.GetChar();\
			if (!tmpParser.ParseLong(&row))\
				row=-1;\
			tmpParser.GetChar();\
			target = tmpParser.GetWxLine(); \
		}\
	}
#endif // UPDATE_PARAM_FROM_EVENT_TABLE

#ifdef VRUT_HPC
//in HPC mode is not GUI so we will imitate GUI registration by simple parameter registration
//TODO: I am not sure if it will work in all situations
#ifndef REGISTER_PARAM_GUI_TEXTCONTROL
#define REGISTER_PARAM_GUI_TEXTCONTROL(paramId, pName, value, pDescription) \
	REGISTER_PARAM(paramId, pName, value, pDescription);
#endif // REGISTER_PARAM_GUI_TEXTCONTROL

#ifndef REGISTER_PARAM_GUI_SCENEID
#define REGISTER_PARAM_GUI_SCENEID(paramId, pName, value, pDescription) \
	{ wxString valueTmp(wxString::Format(wxT("%lu"), (unsigned long)value)); \
	REGISTER_PARAM(paramId, pName, valueTmp, pDescription); \
	int valueTmpI; \
	TextParser(valueTmp.utf8_str()).ParseInt(&valueTmpI); \
	value=valueTmpI; }
#endif // REGISTER_PARAM_GUI_SCENEID

#ifndef REGISTER_PARAM_GUI_NODEID
#define REGISTER_PARAM_GUI_NODEID(paramId, pName, value, sceneId, pNodeTypes, pDescription) \
	{ wxString valueTmp((value==NODE_ID_NONE-1)?wxT(""):wxString::Format(wxT("%lu"), (unsigned long)value)); \
	REGISTER_PARAM(paramId, pName, valueTmp, pDescription); \
	int valueTmpI; \
	TextParser(valueTmp.utf8_str()).ParseInt(&valueTmpI); \
	value=valueTmpI; }
#endif // REGISTER_PARAM_GUI_NODEID

#ifndef REGISTER_PARAM_GUI_CHECKBOX
#define REGISTER_PARAM_GUI_CHECKBOX(paramId, pName, value, pDescription) \
	{ wxString valueTmp(value?wxT("1"):wxT("0")); \
	  REGISTER_PARAM(paramId, pName, valueTmp, pDescription); \
	  value=isTrue(valueTmp); }
#endif // REGISTER_PARAM_GUI_CHECKBOX

#ifndef REGISTER_PARAM_GUI_SLIDER
#define REGISTER_PARAM_GUI_SLIDER(paramId, pName, value, minValue, maxValue, pDescription) \
	REGISTER_PARAM(paramId, pName, value, pDescription);
#endif // REGISTER_PARAM_GUI_SLIDER

#ifndef REGISTER_PARAM_GUI_COMBOBOX
#define REGISTER_PARAM_GUI_COMBOBOX(paramId, pName, value, possibilities, pDescription) \
	REGISTER_PARAM(paramId, pName, value, pDescription);
#endif // REGISTER_PARAM_GUI_COMBOBOX

#ifndef REGISTER_PARAM_GUI_BUTTON
#define REGISTER_PARAM_GUI_BUTTON(paramId, pName, pDescription) \
	{ wxString valueTmp; \
	  REGISTER_PARAM(paramId, pName, valueTmp, pDescription); }
#endif // REGISTER_PARAM_GUI_BUTTON

#ifndef REGISTER_PARAM_GUI_TABLE
#define REGISTER_PARAM_GUI_TABLE(paramId, pName, definition, pDescription) \
	{ wxString valueTmp;\
	REGISTER_PARAM(paramId, pName, valueTmp, pDescription); }
#endif // REGISTER_PARAM_GUI_TABLE

#ifndef ENABLE_PARAM_GUI_CONTROL
#define ENABLE_PARAM_GUI_CONTROL(_paramId, value)
#endif //ENABLE_PARAM_GUI_CONTROL

#ifndef REGISTER_PARAM_GUI_FILECONTROL
#define REGISTER_PARAM_GUI_FILECONTROL(paramId, pName, value, pFileMode, pWildcard, pDescription) \
	{ REGISTER_PARAM(paramId, pName, value, pDescription); }
#endif // REGISTER_PARAM_GUI_TEXTCONTROL

#ifndef REGISTER_PARAM_GUI_LISTCONTROL
#define REGISTER_PARAM_GUI_LISTCONTROL(paramId, pName, value, possibilities, sceneID, pDescription) \
	{ REGISTER_PARAM(paramId, pName, value, pDescription); }
#endif // REGISTER_PARAM_GUI_LISTCONTROL

#ifndef REGISTER_PARAM_GUI_BITMAP
#define REGISTER_PARAM_GUI_BITMAP(paramId, pName, value, pDescription) \
	{ REGISTER_PARAM(paramId, pName, value, pDescription); }
#endif // REGISTER_PARAM_GUI_BITMAP

#ifndef REGISTER_PARAM_GUI_NUMBER_INPUT
#define REGISTER_PARAM_GUI_NUMBER_INPUT(paramId, pName, value, minValue, maxValue, increment, pFlags, pDescription) \
	{ REGISTER_PARAM(paramId, pName, value, pDescription); }
#endif // REGISTER_PARAM_GUI_NUMBER_INPUT

#ifndef REGISTER_PARAM_GUI_COLOURPICKER
#define REGISTER_PARAM_GUI_COLOURPICKER(paramId, pName, value, pDialogMode, pDescription) \
	{ REGISTER_PARAM(paramId, pName, value, pDescription); }
#endif // REGISTER_PARAM_GUI_COLOURPICKER

#ifndef REGISTER_PARAM_GUI_BEGIN_GROUP
#define REGISTER_PARAM_GUI_BEGIN_GROUP(groupName)
#endif // REGISTER_PARAM_GUI_BEGIN_GROUP

#ifndef SHOW_PARAM_GUI_CONTROL
#define SHOW_PARAM_GUI_CONTROL(_paramId, value)
#endif // SHOW_PARAM_GUI_CONTROL

#endif //VRUT_HPC

namespace VRUT
{
	class Module;

	///Environment for registered parameters management class
	class VRUT_CORE_API Environment
	{
	protected:
		///Hash map type mapping instance (usually module instance) identifier to list of parameters registered for this instance
		WX_DECLARE_STRING_HASH_MAP(Parameter, InstParamList);
		///Hash map type mapping parameter name to instance list of parameters registered
		WX_DECLARE_STRING_HASH_MAP(InstParamList, NameInstParamList);
		///Hash map type mapping pool identifier (usually module's name) to list of parameters registered
		WX_DECLARE_STRING_HASH_MAP(NameInstParamList, PoolNameInstParamList);

		///Hash map mapping pool identifier (usually module's name) to list of parameters registered
		PoolNameInstParamList registeredParameters;

		wxCriticalSection registeredParametersCS;

	private:
		bool isMask(const wxString &str)
		{
			return ((str.Find('*')!=wxNOT_FOUND) || (str.Find('?')!=wxNOT_FOUND));
		}
		void processName(const wxString &poolName, NameInstParamList::iterator paramIt, const Parameter::ParameterIdentifier &paramId, wxString &value, bool create, const wxString & originId);
		void processPool(PoolNameInstParamList::iterator poolIt, const Parameter::ParameterIdentifier &paramId, wxString &value, bool create, const wxString & originId);
	public:
		typedef std::pair<wxString, wxString> NameValue;

		///Class constructor
		Environment();
		///Class destructor
		~Environment();

		///Get parameter value as string, much faster than \c GetParams
		///\param[in] paramId Parameter identifier of registered parameter, '*' and '?' NOT allowed
		///\return Value as string if found, empty string otherwise
		wxString GetParam(const Parameter::ParameterIdentifier & paramId);
		///Get parameter(s') value(s) as string
		///\param[in] paramId Parameter identifier of registered parameter(s), '*' and '?' allowed in all members
		///\param[out] values Filled with full parameter names (first) and its values (second)
		void GetParams(const Parameter::ParameterIdentifier & paramId, std::vector<NameValue> &values);
		///Set parameter value and/or its default, will be applied to all matches
		///Given parameter is registered if no match found and \c paramId.poolName and \c paramId.paramName have no '?','*' characters
		///\param[in] paramId Parameter identifier of registered parameter(s), '*' and '?' allowed in all members
		///\param[in] value New value as string
		///\param[in] fromGui Distinguish if the parameter is set from guienvironment.
		///\return \c true if any match found, \c false otherwise (doesn't mean necessarily that parameter was registered)
		wxString SetParam(const Parameter::ParameterIdentifier & paramId, wxString value, const wxString & originId = wxEmptyString);
		///Unregister parameter
		///\param[in] paramId paramId Parameter identifier of registered parameter(s), '*' and '?' allowed (not in \c poolName member)
		///\return \c true if successful
		bool UnregisterParam(const Parameter::ParameterIdentifier & paramId);

		///Save parameters to a given file
		void SaveParams(const Parameter::ParameterIdentifier &pi, const wxString &fileName);
		///Load parameters from a given file
		void LoadParams(const wxString &fileName);
	};
}

#endif
