#ifndef TDE_ENGINE_H
#define TDE_ENGINE_H

#include "config.h"
#include "common.h"
#include "DisplayUnit.h"

#include <modbus/modbus.h>

// uncomment if C-strings / Z-strings are stored in Big-Endian
#define STRINGS_BE

/*!
 * \namespace TDE
 * \brief Main TDE-IDE namespace
 *
 * All TDE-IDE symbols are, directly or indirectly, contained
 * inside this namespace.
 */

namespace TDE {

void hexdump(std::ostream& out, const char *area, size_t len);

std::string plc_decimal_repr(plc_decimal_t dValue, int decimals);
std::string decimal_int_repr(long value, int decimals, int repr_decimals = -1);
long decimal_int_parse(std::string repr, int decimals);

class DataStore;
struct VariableBlock;
typedef SHARED_PTR<VariableBlock> VariableBlockSPtr;
typedef SHARED_PTR<const VariableBlock> VariableBlockConstSPtr;

std::string plc_kind_str(plc_kind_t kind);

std::ostream& operator<< ( std::ostream& out, const plc_kind_t& kind );

// based on Monoid pattern of Alexandrescu's "Modern C++ Design"
// see:
//   http://stackoverflow.com/questions/2496918/singleton-pattern-in-c
//   http://stackoverflow.com/a/2498423/1363486
/*!
 * \class StringTable
 * \brief Monoid class handling PLC string variable values.
 *
 * **WARNING**: This is an advanced topic.
 * 
 * \b TODO
 *
 * \see Andrei Alexandrescu, "Modern C++ Design"
 */
class StringTable
{
public:
	typedef long str_id_t;

	// STATIC METHODS

	static SHARED_PTR<StringTable> Instance();

	static bool Has(str_id_t id)							{ return Instance()->has(id);    }
	static str_id_t Alloc(const std::string& str = "")		{ return Instance()->alloc(str); }
	static str_id_t Find(const std::string& str)			{ return Instance()->find(str);  }
	static void Remove(str_id_t id)							{ Instance()->remove(id);  		 }

	static std::string& Get(str_id_t id) 					{ return Instance()->get(id);  }
	static void Set(str_id_t id, const std::string& str)	{ Instance()->set(id, str);    }

	static str_id_t Copy(str_id_t id)						{ return Instance()->copy(id);        }
	static bool Equal(str_id_t id1, str_id_t id2)			{ return Instance()->equal(id1, id2); }
	static int  Cmp(str_id_t id1, str_id_t id2)				{ return Instance()->cmp(id1, id2);   }

	static bool Load(const std::string& pathname, std::map<str_id_t, str_id_t>& remapped, bool& hasRemapped) {
		return Instance()->load(pathname, remapped, hasRemapped);
	}
	static bool Save(const std::string& pathname)			{ return Instance()->save(pathname); }

	// NORMAL METHODS

	StringTable();
	virtual ~StringTable();

	bool has(str_id_t id) const								{ return (m_map.count(id) > 0); }
	str_id_t alloc(const std::string& str = "");
	str_id_t find(const std::string& str);
	void remove(str_id_t id)								{ m_map.erase(id); }

	std::string& get(str_id_t id)							{ return (id > 0) ? m_map[id] : emptyString; }
	StringTable& set(str_id_t id, const std::string& str)	{ m_map[id] = str; return *this; }

	std::string& operator[](str_id_t id) 					{ return get(id); }

	str_id_t copy(str_id_t id)								{ return (id > 0) ? alloc(get(id)) : id; }
	bool equal(str_id_t id1, str_id_t id2) 					{ return ((id1 > 0) && (id2 > 0)) ? (get(id1) == get(id2)) : (id1 == id2); }
	int  cmp(str_id_t id1, str_id_t id2) 					{ return ((id1 > 0) && (id2 > 0)) ? get(id1).compare(get(id2)) : ((id1 < id2) ? -1 : (id1 > id2)); }

	bool load(const std::string& pathname, std::map<str_id_t, str_id_t>& remapped, bool& hasRemapped);
	bool save(const std::string& pathname);

private:
	static std::string emptyString;

	StringTable& lock()										{ m_mutex.lock(); return *this; }
	StringTable& unlock()									{ m_mutex.unlock(); return *this; }

	std::map<str_id_t, std::string>	m_map;
	str_id_t m_next_id;
	boost::mutex m_mutex;

	static void CleanUp();
	static SHARED_PTR<StringTable> m_instance;
};

#define VV_B(vv)	((vv).v.b)
#define VV_U8(vv)	((vv).v.u8)
#define VV_I8(vv)	((vv).v.i8)
#define VV_U16(vv)	((vv).v.u16)
#define VV_I16(vv)	((vv).v.i16)
#define VV_U32(vv)	((vv).v.u32)
#define VV_I32(vv)	((vv).v.i32)
#define VV_F(vv)	((vv).v.f)
#define VV_STR(vv)	((vv).v.str_id)

struct VariableDescr;

/*!
 * \struct VariableValue
 * \brief Structure representing PLC variable values, along with their kind.
 *
 * Instances of this structure can be used to hold values found in
 * PLC variables. The two essential features are:
 *
 * - the value kind
 * - the actual value itself
 */

struct VariableValue
{
	plc_kind_t kind;
	union {
		bool b;
		uint8_t u8;
		int8_t i8;
		uint16_t u16;
		int16_t i16;
		uint32_t u32;
		int32_t i32;
		plc_float_t f;
		StringTable::str_id_t str_id;
	} v;

	~VariableValue()
	{
		if (kind == PLC_STRING)
			_discardIdString();
	}

	VariableValue() : kind(PLC_UNKNOWN) 								{ v.f = 0.0; v.u32 = 0; v.str_id = 0; }
	VariableValue(plc_kind_t kind_) : kind(kind_)						{ v.f = 0.0; v.u32 = 0; v.str_id = 0; }
	VariableValue(bool value) : kind(PLC_BOOL)							{ v.b = value; }
	VariableValue(uint8_t value) : kind(PLC_U8)							{ v.u8 = value; }
	VariableValue(int8_t value) : kind(PLC_I8)							{ v.i8 = value; }
	VariableValue(uint16_t value) : kind(PLC_U16)						{ v.u16 = value; }
	VariableValue(int16_t value) : kind(PLC_I16)						{ v.i16 = value; }
	VariableValue(uint32_t value) : kind(PLC_U32)						{ v.u32 = value; }
	VariableValue(int32_t value) : kind(PLC_I32)						{ v.i32 = value; }
	VariableValue(plc_float_t value) : kind(PLC_FLOAT)					{ v.f = value; }
	VariableValue(const std::string& value) : kind(PLC_STRING)			{ v.str_id = StringTable::Alloc(value); }

	VariableValue(const VariableValue& other) : kind(other.kind)
	{
		v.f = 0.0; v.u32 = 0; v.str_id = 0;
		if (kind != PLC_STRING)
			v = other.v;
		else
			v.str_id = StringTable::Copy(other.v.str_id);
	}

	VariableValue& clone(const VariableValue& other)  {
		plc_kind_t old_kind = kind;
		if (old_kind == PLC_STRING) {
			_discardIdString();
		}
		kind = other.kind;
		if (kind != PLC_STRING)
			v = other.v;
		else {
			_discardIdString();
			v.str_id = StringTable::Copy(other.v.str_id);
		}
		return *this;
	}

	VariableValue& castFrom(const VariableValue& other)  {
		if (kind == other.kind)
			*this = other;
		else
			set(other);
		return *this;
	}

	VariableValue& operator= (const VariableValue& other)  {
		assert(kind == other.kind);
		if (kind != PLC_STRING)
			v = other.v;
		else {
			_discardIdString();
			v.str_id = StringTable::Copy(other.v.str_id);
		}
		return *this;
	}

	bool operator== (const VariableValue& other) const  {
		return ((kind == other.kind) &&
			(((kind != PLC_STRING) && (memcmp(&v, &other.v, sizeof(v)) == 0)) ||
			 ((kind == PLC_STRING) && StringTable::Equal(v.str_id, other.v.str_id)))); }
	bool operator!= (const VariableValue& other) const  { return !(*this == other); }
	bool operator< (const VariableValue& other) const;
	/* other comparison operators are implicitly defined using std::rel_ops from <utility> */

	VariableValue& resetKind(plc_kind_t kind_) {
		plc_kind_t old_kind = kind;
		if (kind_ == old_kind)
			return *this;
		if (old_kind == PLC_STRING) {
			_discardIdString();
		}
		v.f = 0.0; v.u32 = 0; v.str_id = 0;
		kind = kind_;
		assert(kind == kind_);
		return *this;
	}

	bool        getB() const			{ assert(kind == PLC_BOOL);  return VV_B(*this); }
	uint8_t     getU8() const			{ assert(kind == PLC_U8);    return VV_U8(*this); }
	int8_t      getI8() const			{ assert(kind == PLC_I8);    return VV_I8(*this); }
	uint16_t    getU16() const			{ assert(kind == PLC_U16);   return VV_U16(*this); }
	int16_t     getI16() const			{ assert(kind == PLC_I16);   return VV_I16(*this); }
	uint32_t    getU32() const			{ assert(kind == PLC_U32);   return VV_U32(*this); }
	int32_t     getI32() const			{ assert(kind == PLC_I32);   return VV_I32(*this); }
	plc_float_t getF() const			{ assert(kind == PLC_FLOAT); return VV_F(*this); }
	StringTable::str_id_t	getStrId() const		{ assert(kind == PLC_STRING); return VV_STR(*this); }
	const std::string& getStr() const	{ assert(kind == PLC_STRING); return StringTable::Get(VV_STR(*this)); }

	VariableValue& _set(bool value)					{ assert(kind == PLC_BOOL);   VV_B(*this) = value; return *this; }
	VariableValue& _set(uint8_t value)				{ assert(kind == PLC_U8);     VV_U8(*this) = value; return *this; }
	VariableValue& _set(int8_t value)				{ assert(kind == PLC_I8);     VV_I8(*this) = value; return *this; }
	VariableValue& _set(uint16_t value)				{ assert(kind == PLC_U16);    VV_U16(*this) = value; return *this; }
	VariableValue& _set(int16_t value)				{ assert(kind == PLC_I16);    VV_I16(*this) = value; return *this; }
	VariableValue& _set(uint32_t value)				{ assert(kind == PLC_U32);    VV_U32(*this) = value; return *this; }
	VariableValue& _set(int32_t value)				{ assert(kind == PLC_I32);    VV_I32(*this) = value; return *this; }
	VariableValue& _set(plc_float_t value)			{ assert(kind == PLC_FLOAT);  VV_F(*this) = value; return *this; }
	VariableValue& _set(const std::string& value)	{ assert(kind == PLC_STRING); _ensureString(); StringTable::Set(v.str_id, value); return *this; }
	VariableValue& _set(const VariableValue& other) { assert(kind == other.kind); *this = other; }

	// TODO: switch to assignment operator with overloaded types
	VariableValue& set(bool value);
	VariableValue& set(uint8_t value);
	VariableValue& set(int8_t value);
	VariableValue& set(uint16_t value);
	VariableValue& set(int16_t value);
	VariableValue& set(uint32_t value);
	VariableValue& set(int32_t value);
	VariableValue& set(plc_float_t value);
	VariableValue& set(const std::string& value);
	VariableValue& set(const std::string& value, DisplayUnitPtr fromUnit, DisplayUnitPtr toUnit);
	VariableValue& set(const VariableValue& other);

	// TODO: add cast operator
	bool              asBool() const;
	int               asInt() const;
	plc_float_t       asFloat() const;
	uint8_t           asU8() const;
	int8_t            asI8() const;
	uint16_t          asU16() const;
	int16_t           asI16() const;
	uint32_t          asU32() const;
	int32_t           asI32() const;
	std::string       asString() const;

	// TODO: add << and >> operators
	std::string 	  repr() const  { return asString(); }
	VariableValue&    parse(const std::string& repr);
	VariableValue&    parse(const std::string& repr, DisplayUnitPtr fromUnit, DisplayUnitPtr toUnit);

	std::string       reprBool() const;
	std::string       reprInt() const;
	std::string       reprFloat() const;
	std::string       reprDecimal(const VariableDescr& vDescr) const;
	std::string       reprDecimal(const VariableDescr& vDescr, DisplayUnitPtr fromUnit, DisplayUnitPtr toUnit) const;

	friend std::ostream& operator<< ( std::ostream& out, const VariableValue& value );

private:
	VariableValue& _ensureString() {
		assert(kind == PLC_STRING);
		if (v.str_id <= 0)
			v.str_id = StringTable::Alloc();
		assert(v.str_id > 0);
		return *this;
	}
	VariableValue& _discardIdString() {
		assert(kind == PLC_STRING);
		if (v.str_id > 0)
			StringTable::Remove(v.str_id);
		v.str_id = -1;
		assert(v.str_id <= 0);
		return *this;
	}
	VariableValue& _stringSet(const std::string& value) {
		_ensureString();
		return _set(value);
	}
	VariableValue& _stringSet(StringTable::str_id_t str_id) {
		_ensureString();
		StringTable::Set(v.str_id, StringTable::Get(str_id));
		return *this;
	}

	VariableValue& _x_reset() {
		// VERY PRIVATE!: used to initialize from an indeterminate state when mmap()-ing
		v.f = 0.0; v.u32 = 0; v.str_id = 0;
		kind = PLC_UNKNOWN;
	}

	friend class DataStore;
};

std::ostream& operator<< ( std::ostream& out, const VariableValue& value );

/*!
 * \class VariableRemoteInfo
 * \brief Represents remote node connection parameters.
 *
 * Instances of this class represents remote node connection parameters,
 * especially in the case of Modbus TCP.
 *
 * It is used by VariableDescr and ModbusConnector.
 *
 * \see VariableDescr
 * \see ModbusConnector
 */

class VariableRemoteInfo
{
public:
	VariableRemoteInfo() :
		m_remote(false), m_port(-1), m_node(-1), m_slave(-1), m_area(-1), m_address(0), m_is_modbus_tcp(false) {}
	VariableRemoteInfo(const std::string& protocol, const std::string& connection, int port=-1, int node=-1, int slave=-1, int area=-1, uint32_t address=0) :
		m_remote(true), m_protocol(protocol), m_connection(connection), m_port(port), m_node(node), m_slave(slave), m_area(area), m_address(address), m_is_modbus_tcp(false) { _setModbusFlag(); }
	~VariableRemoteInfo() { m_protocol.clear(); m_connection.clear(); m_port = -1; m_node = -1; m_slave = -1; m_area = -1; m_address = 0; m_is_modbus_tcp = false; }

	bool isRemote() const					{ return (m_remote && (!m_protocol.empty()) && (!m_connection.empty())); }
	bool isModbusTCP() const				{ return m_is_modbus_tcp; }
	const std::string& protocol() const		{ return m_protocol; }
	const std::string& connection() const	{ return m_connection; }
	int port() const						{ return m_port; }
	int node() const						{ return m_node; }
	int slave() const						{ return m_slave; }
	int area() const						{ return m_area; }
	uint32_t address() const				{ return m_address; }

	std::string repr() const;

	bool operator< (const VariableRemoteInfo& other) const { return (repr() < other.repr()); }

private:
	void _setModbusFlag();

	bool m_remote;
	std::string m_protocol;
	std::string m_connection;
	int m_port;
	int m_node;
	int m_slave;
	int m_area;
	uint32_t m_address;

	bool m_is_modbus_tcp;
};

typedef unsigned int variable_mode_t;
const variable_mode_t VARIABLE_MODE_UNDEFINED = 0;
const variable_mode_t VARIABLE_MODE_R = 0x01;
const variable_mode_t VARIABLE_MODE_W = 0x02;
const variable_mode_t VARIABLE_MODE_RW = VARIABLE_MODE_R | VARIABLE_MODE_W;

inline bool variable_is_readable(variable_mode_t mode)	{ return (mode & VARIABLE_MODE_R); }
inline bool variable_is_writable(variable_mode_t mode)	{ return (mode & VARIABLE_MODE_W); }

/*!
 * \class ModbusConnector
 * \brief Handles Modbus TCP connections.
 *
 * It also keeps tracks of connection status, errors, ...
 *
 * \see VariableDescr
 * \see VariableRemoteInfo
 */
class ModbusConnector
{
public:
	typedef boost::chrono::steady_clock::time_point clock_t;
	typedef clock_t::duration   					cdiff_t;

	class Connection
	{
	public:
		static const long MAX_ERRORS_COUNT;
		static const double MAX_ERRORS_DURATION;

		Connection(variable_mode_t mode);
		Connection(variable_mode_t mode, const VariableRemoteInfo& vRemoteInfo, bool do_log = false);
		~Connection();

		bool			connect();
		void			close();

		modbus_t* 		modbusPtr();
		bool			connected() const	{ return m_mb ? true : false; }

		const VariableRemoteInfo& remoteInfo() const { return m_remote_info; }

		bool			log() const			{ return m_log; }
		void			setLog(bool value)	{ m_log = value; }

		bool			error() const		{ return m_error; }
		void			setError(bool errorCondition);
		long			nErrors() const		{ return m_error_ctr; }

		bool			online() const		{ return connected() && m_online; }
		void			setOnline(bool online, clock_t when);
		void			setOnline(bool online);
		clock_t			lastOnline()		{ return m_last_online; }
		clock_t			lastOffline()		{ return m_last_offline; }

		Connection&		lock()				{ m_mutex.lock(); return *this; }
		Connection&		unlock()			{ m_mutex.unlock(); return *this; }

	private:
		bool _connect();
		void _disconnect();

		modbus_t* m_mb;
		boost::mutex m_mutex;
		variable_mode_t m_mode;

		bool m_online;
		clock_t m_last_online;
		clock_t m_last_offline;

		bool m_error;
		clock_t m_error_since;
		long m_error_ctr;

		VariableRemoteInfo m_remote_info;
		bool m_log;
	};
	typedef SHARED_PTR<Connection> connection_sptr_t;

	ModbusConnector();
	~ModbusConnector();

	connection_sptr_t getConnection(variable_mode_t mode, const VariableRemoteInfo& vRemoteInfo, bool do_log = false);
	modbus_t* 		  getModbusPtr(variable_mode_t mode, const VariableRemoteInfo& vRemoteInfo, bool do_log = false)	{ return getConnection(mode, vRemoteInfo, do_log)->modbusPtr(); }
	void closeConnection(variable_mode_t mode, const VariableRemoteInfo& vRemoteInfo);

	bool getOnlineStatus(int& nTotal, int& nOnline) const;
	int nConnections() const	{ int nTotal = 0, nOnline = 0; getOnlineStatus(nTotal, nOnline); return nTotal; }
	int nOnline() const			{ int nTotal = 0, nOnline = 0; getOnlineStatus(nTotal, nOnline); return nOnline; }
	int nOffline() const		{ int nTotal = 0, nOnline = 0; getOnlineStatus(nTotal, nOnline); return (nTotal - nOnline); }
	bool online() const			{ int nTotal = 0, nOnline = 0; return getOnlineStatus(nTotal, nOnline); }
	long nErrors() const;

private:
	static std::string GetVariableRemoteInfoSignature(variable_mode_t mode, const VariableRemoteInfo& vRemoteInfo);

	typedef std::map<std::string, connection_sptr_t> modbus_connection_map_t;
	typedef modbus_connection_map_t::iterator modbus_connection_map_iterator_t;
	modbus_connection_map_t m_ctx_map;
};

typedef F_FUNCTION<bool (DataStore&, VariableDescr&, VariableValue&, VariableValue&)> VariableObserver;

typedef F_FUNCTION<void (const DataStore& ds, const VariableDescr& vDescr, const VariableValue& src, VariableValue& dst)> ValueTransformer;
typedef std::pair<ValueTransformer, ValueTransformer> ValueTransformerPair;
typedef std::map<std::string, ValueTransformerPair> value_transformers_map_t;

/*!
 * \struct VariableDescr
 * \brief Structure representing PLC variable _metadata_.
 *
 * Instances of this structure are used to hold everything concerning PLC variables
 * except their values. In other words their _metadata_:
 * 
 * - kind
 * - name
 * - description
 * - default value
 * - min & max values
 * - ...
 *
 * The actual values is kept in VariableValue instances.
 */

struct VariableDescr
{
	typedef long ouid_t;
	typedef std::pair<ouid_t, VariableObserver> observer_pair;

	struct choice_t
	{
		choice_t(int index_, VariableValue vk_, const std::string& text_) :
			index(index_), vk(vk_), text(text_) {}

		int index;
		VariableValue vk;
		std::string text;
	};
	typedef std::vector<choice_t> choices_t;

	int index;
	plc_kind_t kind;
	std::string name;
	std::string description;
	VariableValue defaultValue;
	bool hasDefault;
	variable_mode_t mode;
	int decimals;
	VariableValue min;
	VariableValue max;
	int minReference;		// index of reference variable for minimum (or -1)
	int maxReference;		// index of reference variable for maximum (or -1)
	std::string minReferenceName;
	std::string maxReferenceName;
	bool hasMin;
	bool hasMax;
	int bit;				// bit number [0:31] for booleans (-1 for no bit)
	uint32_t mask;
	int shift;
	int access;
	int refresh;
	int refresh_visible;
	int refresh_current;
	bool do_log;
	std::string notes;
	std::vector<std::string> recipes;

	VariableRemoteInfo remote;
	choices_t choices;

	std::string      transformer;
	ValueTransformer getter;
	ValueTransformer setter;
	bool hasTransformer;

	// cstring / zstring
	plc_string_kind_t strKind;	// if PLC_CSTRING or PLC_ZSTRING, the main kind determines chars-per-var
								// kind can be: PLC_U8 (1 char per var), PLC_U16 (2 chars per var), PLC_U32 (4 chars per var)
	int maxLength;
	int headReference;			// index of variable (descr) for head of the string

	VariableDescr() : index(-1), kind(PLC_UNKNOWN),
		hasDefault(false),
		mode(VARIABLE_MODE_RW),
		decimals(0),
		minReference(-1), maxReference(-1),
		hasMin(false), hasMax(false), bit(-1),
		mask(0), shift(0), access(0),
		refresh(0), refresh_visible(0), refresh_current(0),
		hasTransformer(false),
		strKind(PLC_NO_STRING),
		maxLength(0),
		headReference(-1),
		do_log(false), m_next_observer_uid(1), refresh_offset(0), m_trace(false),
		m_linear_unit_scale(-1.0), m_linear_unit_offset(0.0) {}

	VariableDescr(plc_kind_t kind_) : index(-1), kind(kind_),
		hasDefault(false),
		mode(VARIABLE_MODE_RW),
		decimals(0),
		minReference(-1), maxReference(-1),
		hasMin(false), hasMax(false), bit(-1),
		mask(0), shift(0), access(0),
		refresh(0), refresh_visible(0), refresh_current(0),
		hasTransformer(false),
		strKind(PLC_NO_STRING),
		maxLength(0),
		headReference(-1),
		do_log(false), m_next_observer_uid(1), refresh_offset(0), m_trace(false),
		m_linear_unit_scale(-1.0), m_linear_unit_offset(0.0) {
			defaultValue.resetKind(kind);
			if (kind != PLC_STRING) {
				min.resetKind(kind);
				max.resetKind(kind);
			}
		}

	bool isInitialized() const	{ return (index >= 0) && (kind != PLC_UNKNOWN); }

	std::string kindStr() const;
	std::string strKindStr() const;
	std::string repr() const;
	const VariableDescr& dump() const;

	bool isReadable() const             { return mode & VARIABLE_MODE_R; }
	bool isWriteable() const            { return mode & VARIABLE_MODE_W; }
	bool isReadWrite() const            { return (mode & VARIABLE_MODE_R) && (mode & VARIABLE_MODE_W); }

	bool isRemote() const				{ return remote.isRemote(); }
	bool isModbusTCP() const			{ return remote.isModbusTCP(); }
	ssize_t nBytes() const;				// return the (remote) length in bytes
	ssize_t nRegisters() const;			// return the (remote) length in basic registers (e.g. modbus)

	int getRefreshCurrent() const { return (refresh_current != 0) ? refresh_current : refresh; }

	bool isInRecipe(const std::string& recipeName) const;

	ouid_t addObserver(VariableObserver observer);
	VariableDescr& removeObserver(ouid_t observer_uid);
	VariableDescr& clearObservers();
	VariableDescr& signalObservers(DataStore& ds, VariableValue& old, VariableValue& current);

	VariableBlockConstSPtr getVariableBlock() const { return m_vblock_ptr; }
	VariableBlockSPtr      getVariableBlock() { return m_vblock_ptr; }

	bool trace() const					{ return m_trace; }
	bool trace(bool value)				{ bool old = m_trace; m_trace = value; return old; }

	const std::string& unit() const { return m_native_unit_name; }
	void unit(const std::string& name) {
		m_native_unit_name = name;
		nativeUnit = DisplayUnitManager::Get(name);
	}
	void unit(DisplayUnitPtr unitPtr) {
		m_native_unit_name = unitPtr->name();
		nativeUnit = unitPtr;
	}
	double linearUnitScale() const  { return m_linear_unit_scale; }
	double linearUnitOffset() const { return m_linear_unit_offset; }
	void unit(const std::string& name, double scale, double offset) {
		unit(name);
		m_linear_unit_scale = scale;
		m_linear_unit_offset = offset;
	}

	const DisplayUnitPtr unitPtr() const { return nativeUnit; }
	const DisplayUnitKind::Enum unitKind() const
	{
		if (nativeUnit)
			return nativeUnit->kind();
		return DisplayUnitKind::Unknown;
	}

	bool isProperKindForCZString() const	{ return ((kind == PLC_U8) || (kind == PLC_U16) || (kind == PLC_U32)); }
	bool isCString() const					{ return (strKind == PLC_CSTRING) && isProperKindForCZString(); }
	bool isZString() const					{ return (strKind == PLC_ZSTRING) && isProperKindForCZString(); }
	bool isCZString() const					{ return ((strKind == PLC_CSTRING) || (strKind == PLC_ZSTRING)) && isProperKindForCZString(); }

	int CZStringNCharsPerVar() const;		// C-string / Z-string - number of chars (bytes) per variable
	int CZStringNVars() const;				// C-string / Z-string - number of variables for this string

	friend class DataStore;

	friend std::ostream& operator<< ( std::ostream& out, const VariableDescr& value );

private:
	ouid_t m_next_observer_uid;
	std::vector<observer_pair> observers;
	long refresh_offset;
	VariableBlockSPtr m_vblock_ptr;
	bool m_trace;

	DisplayUnitPtr nativeUnit;
	std::string m_native_unit_name;
	double m_linear_unit_scale;
	double m_linear_unit_offset;
};

std::ostream& operator<< ( std::ostream& out, const VariableDescr& value );

typedef SHARED_PTR<VariableDescr> VariableDescrSPtr;
typedef SHARED_PTR<const VariableDescr> VariableDescrConstSPtr;

typedef std::vector<VariableDescrSPtr>::iterator variable_iter_t;
typedef std::vector<VariableDescrSPtr>::const_iterator variable_const_iter_t;
typedef std::pair<variable_iter_t, variable_iter_t> variable_range_t;
typedef std::pair<variable_const_iter_t, variable_const_iter_t> variable_const_range_t;

/*!
 * \struct VariableBlock
 * \brief Represents a contiguous block of variables.
 *
 * An instance of this structure represents a contiguous
 * (in terms of variable indices) block of variables
 * inside DataStore variable memory ranges.
 *
 * **NOTE**: Variable indices are **NOT** equal to variable positions
 * inside the configuration files (`*.cfg`), because for example
 * remote variables are sorted according to their remote addresses
 * (and other parameters). Hence indices should always be obtained
 * through the DataStore itself (and VariableDescr instances), after
 * config file loading.
 *
 * \see DataStore
 * \see VariableDescr
 * \see VariableValue
 */
struct VariableBlock
{
	DataStore* dsP;
	int start;
	int end;

	int refresh;
	int refresh_visible;
	int refresh_current;

	int index;					// index of this block
	int maxVariables;			// limitation of maximum number of variables to break the block eventually
	bool m_trace;				// trace this block

	VariableBlock() :
		dsP(NULL), start(-1), end(-1), refresh(-1), refresh_visible(-1), refresh_current(-1), index(-1), maxVariables(-1),
		m_trace(false) {}
	VariableBlock(DataStore* dsP_) :
		dsP(dsP_), start(-1), end(-1), refresh(-1), refresh_visible(-1), refresh_current(-1), index(-1), maxVariables(-1),
		m_trace(false)  {}
	VariableBlock(DataStore* dsP_, int s, int e) :
		dsP(dsP_), start(s), end(e), refresh(-1), refresh_visible(-1), refresh_current(-1), index(-1), maxVariables(-1),
		m_trace(false)  {}
	~VariableBlock() { dsP = NULL; }

	VariableBlock(const VariableBlock& that) :
		dsP(that.dsP), start(that.start), end(that.end),
		refresh(that.refresh), refresh_visible(that.refresh_visible), refresh_current(that.refresh_current),
		index(that.index),  maxVariables(that.maxVariables), m_trace(that.m_trace) {}

	VariableBlock& operator=(const VariableBlock& that)
		{ dsP = that.dsP; start = that.start; end = that.end;
		  refresh = that.refresh; refresh_visible = that.refresh_visible; refresh_current = that.refresh_current;
		  index = that.index;  maxVariables = that.maxVariables; m_trace = that.m_trace; return *this; }

	std::string repr() const;

	VariableDescr& first() const;
	VariableDescr& last() const;

	plc_kind_t kind() const 			{ return first().kind; }
	std::string kindStr() const			{ return first().kindStr(); }
	VariableRemoteInfo& remote() const 	{ return first().remote; }

	variable_mode_t mode() const		{ return first().mode; }
	bool isReadable() const             { return first().isReadable(); }
	bool isWriteable() const            { return first().isWriteable(); }
	bool isReadWrite() const            { return first().isReadWrite(); }

	bool isRemote() const				{ assert(first().isRemote() == last().isRemote()); return first().isRemote(); }
	bool isModbusTCP() const			{ return isRemote() && first().isModbusTCP(); }

	ssize_t nVariables() const			{ return (last().index - first().index + 1); }
	ssize_t nBytes() const
	{
		assert(first().isRemote() == last().isRemote());
		// contiguous blocks are made of variables of the same kind, hence of the same size
		return nVariables() * first().nBytes();
	}
	ssize_t nRegisters() const			{ return nVariables() * first().nRegisters(); }

	variable_range_t getVariableDescriptions();
	variable_const_range_t getVariableDescriptions() const;

	int getRefreshCurrent() const { return (refresh_current != 0) ? refresh_current : refresh; }

	bool trace() const					{ return m_trace; }
	bool trace(bool value)				{ bool old = m_trace; m_trace = value; return old; }
};

typedef enum {
	VARIABLE_STATE_UNINITIALIZED,
	VARIABLE_STATE_SET,
	VARIABLE_STATE_READ,
	VARIABLE_STATE_WRITTEN
} variable_state_t;

typedef boost::chrono::system_clock stats_clock;
typedef stats_clock::time_point stats_time_point_t;
typedef stats_clock::duration stats_duration_t;
typedef boost::chrono::duration<double> stats_duration_secs_t;

typedef F_FUNCTION<void (DataStore&, int, int)> AccessLevelChangeCallback;

// enum-like functionality in a struct (http://stackoverflow.com/a/2506286)
//   http://stackoverflow.com/questions/2503807/declaring-an-enum-within-a-class/2506286#2506286
/*!
 * \struct BackingStoreMode
 * \brief Enum-like functionality representing the mode of operation of variable backing store.
 *
 * **WARNING**: This is an advanced topic.
 * 
 * \see DataStore
 * \see http://stackoverflow.com/a/2506286
 */
struct BackingStoreMode
{
    enum Type
    {
        NotActive, Created, Loaded
    };


    BackingStoreMode(Type t) : m_t(t) {}
    BackingStoreMode(const BackingStoreMode& other) : m_t(other.m_t) {}
    BackingStoreMode& operator= (const BackingStoreMode &rhs) { m_t = rhs.m_t; return *this; }
    bool operator== (const BackingStoreMode &rhs) { return (m_t == rhs.m_t); }
    bool operator== (const Type &rhs) { return (m_t == rhs); }
    operator Type () const { return m_t; }

    std::string repr(bool extended = false) const { std::ostringstream ss; ss << *this; return ss.str(); }

    friend inline bool operator== (const BackingStoreMode &lhs, const BackingStoreMode &rhs) { return (lhs.m_t == rhs.m_t); }
    friend std::ostream& operator<< ( std::ostream& out, const BackingStoreMode& value );

private:
    Type m_t;

    // prevent automatic conversion for any other built-in types such as bool, int, etc
    // template<typename T> operator T () const;

    // prevent automatic operator==(int)
    bool operator== (int);
};

class User;
class UserManager;

// enum-like functionality in a struct (http://stackoverflow.com/a/2506286)
//  http://stackoverflow.com/questions/2503807/declaring-an-enum-within-a-class/2506286#2506286
/*!
 * \struct AuthOp
 * \brief Enum-like functionality representing authorization operations (`login`, `logout`, `check`).
 *
 * Possible values:
 * - AuthOp::Login
 * - AuthOp::Logout
 * - AuthOp::Check
 *
 * \see User
 * \see UserManager
 * \see http://stackoverflow.com/a/2506286
 */
struct AuthOp
{
    enum Type
    {
        Login, Logout, Check
    };

    AuthOp(Type t) : m_t(t) {}
    AuthOp(const AuthOp& other) : m_t(other.m_t) {}
    AuthOp& operator= (const AuthOp &rhs) { m_t = rhs.m_t; return *this; }
    bool operator== (const AuthOp &rhs) { return (m_t == rhs.m_t); }
    bool operator== (const Type &rhs) { return (m_t == rhs); }
    operator Type () const { return m_t; }

    std::string repr(bool extended = false) const { std::ostringstream ss; ss << *this; return ss.str(); }

    friend inline bool operator== (const AuthOp &lhs, const AuthOp &rhs) { return (lhs.m_t == rhs.m_t); }
    friend std::ostream& operator<< ( std::ostream& out, const AuthOp& value );

private:
    Type m_t;
    //prevent automatic conversion for any other built-in types such as bool, int, etc
    //template<typename T> operator T () const;

    //prevent automatic operator==(int)
    bool operator== (int);
};

std::ostream& operator<< ( std::ostream& out, const AuthOp& value );

typedef F_FUNCTION<void (const UserManager&, boost::optional<const User>, AuthOp op, bool)> UserManagerAuthCallback;

class User
{
public:
	User(const std::string& username, int accessLevel);
	User(const std::string& username, const std::string& password, int accessLevel);
	User(const User& other) {
		m_username = other.m_username; m_password_hash = other.m_password_hash; m_accessLevel = other.m_accessLevel;
	}

	const std::string& username() const		{ return m_username; }

	void passwordHash(const std::string& password_hash) { m_password_hash = password_hash; }
	const std::string& passwordHash() const	{ return m_password_hash; }

	int accessLevel() const					{ return m_accessLevel; }

	void setPassword(const std::string& password);
	bool checkPassword(const std::string password);

	User& operator= (const User& rhs) {
		m_username = rhs.m_username; m_password_hash = rhs.m_password_hash; m_accessLevel = rhs.m_accessLevel;
		return *this;
	}
	bool operator== (const User &rhs) const {
		return ((m_username == rhs.m_username) &&
			(m_password_hash == rhs.m_password_hash) &&
			(m_accessLevel == rhs.m_accessLevel));
	}

	friend std::ostream& operator<< ( std::ostream& out, const User& user );

private:
	std::string m_username;
	std::string m_password_hash;
	int m_accessLevel;
};

std::ostream& operator<< ( std::ostream& out, const User& user );

/*!
 * \class UserManager
 * \brief Handles users and user authentication and authorization.
 *
 * The DataStore delegates user handling to an instance of this class.
 *
 * \see User
 * \see DataStore
 */
class UserManager
{
public:
	UserManagerAuthCallback getAuthCallback() const { return m_auth_cb; }
	UserManager& setAuthCallback(UserManagerAuthCallback cb) { m_auth_cb = cb; return *this; }

	bool addUser(const User& user);
	bool delUser(const User& user);
	bool delUser(const std::string& username);

	const std::vector<User> users() { return m_users; }
	const boost::optional<User> findUser(const std::string& username);

	UserManager& logout();
	bool login(const std::string& username, const std::string& password);
	boost::optional<User> loggedIn() const { return m_current_user; }

	UserManager& clear();
	UserManager& load(const std::string pathname);
	UserManager& save(const std::string pathname);

	friend std::ostream& operator<< ( std::ostream& out, const UserManager& userManager );

private:
	std::vector<User>			 m_users;
	boost::optional<User>		 m_current_user;
	UserManagerAuthCallback		 m_auth_cb;
};

std::ostream& operator<< ( std::ostream& out, const UserManager& userManager );


typedef F_FUNCTION<void (DataStore& ds)> DataStoreInitializer;

typedef F_FUNCTION<void (const DataStore& ds, const std::string& pathname, const std::string& recipeName, double progress)> RecipeProgressUpdateCallback;

typedef F_FUNCTION<bool (int src)> func_b_to_b;
typedef F_FUNCTION<int (int src)> func_i_to_i;
typedef F_FUNCTION<double (double src)> func_d_to_d;
typedef F_FUNCTION<uint8_t (uint8_t src)> func_u8_to_u8;
typedef F_FUNCTION<int8_t (int8_t src)> func_i8_to_i8;
typedef F_FUNCTION<uint16_t (uint16_t src)> func_u16_to_u16;
typedef F_FUNCTION<int16_t (int16_t src)> func_i16_to_i16;
typedef F_FUNCTION<uint32_t (uint32_t src)> func_u32_to_u32;
typedef F_FUNCTION<int32_t (int32_t src)> func_i32_to_i32;

class MPRecipeHeader;

/*!
 * \class DataStore
 * \brief The main component of the Engine.
 *
 * The DataStore is the core of the Engine, acting as the director
 * of the system, being reponsible for maintaining the descriptions of
 * all PLC variables (through VariableDescr instances), their up-to-date
 * values (through proper VariableValue arrays), and for maintianing
 * communications with remote nodes through Modbus TCP.
 *
 * A DataStore can be instantiated explicitly, and then a configuration file
 * loaded with \ref loadConfig() or the DataStoreProvider can be used to obtain
 * a default one (or the widgets-aware version through TDE::Widgets::LoadDefaultDataStore()
 * when writing a Qt-based program).
 *
 * \see VariableDescr
 * \see VariableValue
 * \see UserManager
 * \see DataStoreProvider
 */
class DataStore
{
public:
	DataStore();		//!< Constructor
	~DataStore();		//!< Destructor

	/*!
	 * \brief Flag running threads to be stopped and release memory allocated for variables.
	 */
	void shutdown();

	/*!
	 * \brief Add a variable description
	 * \warning Internal use only.
	 * \warning Variables can't be added after storage for them has been
	 * allocated (through \ref _allocStorage()).
	 * @param  descr description of the variable to add
	 * @return       reference to this DataStore instance
	 */
	DataStore& addVariable(const VariableDescr& descr);

	//! Returns true if a variable with the given index exists.
	bool       hasVariable(int index) const;
	//! Returns true if a variable with the given name exists.
	bool       hasVariable(const std::string& name) const;
	//! Raise an execption if the variable with the given index doesn't exist.
	bool	   checkVariable(int index) const;
	//! Raise an execption if the variable with the given name doesn't exist.
	bool	   checkVariable(const std::string& name) const;

	//! Returns the VariableDescr for the variable with the given index
	const VariableDescr& getDescription(int index) const					{ checkVariable(index); return *(descr_v.at(index)); }
	//! Returns the VariableDescr for the variable with the given name
	const VariableDescr& getDescription(const std::string& name) const		{ checkVariable(name); return *(descr_m.at(name)); }
	//! Returns the (mutable reference to) VariableDescr for the variable with the given index
	VariableDescr& getDescription(int index)								{ checkVariable(index); return *(descr_v.at(index)); }
	//! Returns the (mutable reference to) VariableDescr for the variable with the given name
	VariableDescr& getDescription(const std::string& name)					{ checkVariable(name); return *(descr_m.at(name)); }

	//! Returns a vector of descriptions for all the variables
	std::vector<VariableDescr> getDescriptions() const;

	//! Returns a shared pointer to the description of the variable with the given index
	VariableDescrSPtr getDescriptionPtr(int index);
	//! Returns a shared pointer to the description of the variable with the given name
	VariableDescrSPtr getDescriptionPtr(const std::string& name);
	//! Returns a shared pointer to the immutable description of the variable with the given index
	VariableDescrConstSPtr getDescriptionPtr(int index) const					{ return static_cast<VariableDescrConstSPtr>(getDescriptionPtr(index)); }
	//! Returns a shared pointer to the immutable description of the variable with the given name
	VariableDescrConstSPtr getDescriptionPtr(const std::string& name) const		{ return static_cast<VariableDescrConstSPtr>(getDescriptionPtr(name)); }

	//! Returns an iterator to the descriptions for all the variables
	variable_iter_t getVariableDescriptions()								{ return descr_v.begin(); }
	//! Returns a constant iterator to the descriptions for all the variables
	variable_const_iter_t getVariableDescriptions() const					{ return descr_v.begin(); }

	//! Returns a slice to the variable descriptions between the given indices
	variable_range_t getVariableDescriptions(int first, int last)				{ checkVariable(first); checkVariable(last); return variable_range_t(descr_v.begin() + first, descr_v.begin() + last); }
	//! Returns a constant slice to the variable descriptions between the given indices
	variable_const_range_t getVariableDescriptions(int first, int last) const	{ checkVariable(first); checkVariable(last); return variable_const_range_t(descr_v.begin() + first, descr_v.begin() + last); }

	/*!
	 * \brief Load variables database configuration from a file
	 * @param  pathname database configuration file pathname
	 * @return          reference to this DataStore instance
	 */
	DataStore& loadConfig(const std::string& pathname);

	/*!
	 * \brief Save current variables database configuration to a file
	 * @param  pathname database configuration file pathname
	 * @return          reference to this DataStore instance
	 */
	DataStore& saveConfig(const std::string& pathname);

	/*!
	 * \brief Load the provided database configuration or a default one.
	 * @param  pathname         optional database configuration file pathname
	 * @param  initializer_func optional initialization callback
	 * @return                  true in case of success
	 */
	bool setupDefault(const std::string& pathname = "", DataStoreInitializer initializer_func = 0);

	/*!
	 * \brief Load a default database configuration.
	 * @param  initializer_func optional initialization callback
	 * @return                  true in case of success
	 */
	bool setupDefault(DataStoreInitializer initializer_func) { return setupDefault("", initializer_func); }

	/*!
	 * \brief Returns true if the DataStore is configured and operative.
	 */
	bool ready() const;

	/*!
	 * \brief Returns the number of configured variables.
	 */
	size_t getNVariables() const;

	// -- value -------------------------------------------------------

	/*!
	 * \brief Returns a reference to the raw value of the variable with the given index
	 * The value returned is the actual raw value, not a copy.
	 * @param  index variable index
	 * @return       reference to the actual raw variable value
	 */
	VariableValue&	  		getRawValue(int index);
	//! Returns a const reference to the raw value of the variable with the given index
	const VariableValue&	getRawValue(int index) const;
	//! Returns a reference to the raw value of the variable with the given name
	VariableValue&	  		getRawValue(const std::string& name);
	//! Returns a const reference to the raw value of the variable with the given name
	const VariableValue&	getRawValue(const std::string& name) const;
	//! Returns a reference to the raw value of the variable with the given description
	VariableValue&	 		getRawValue(const VariableDescr& vDescr)			{ return data[vDescr.index]; }
	//! Returns a const reference to the raw value of the variable with the given description
	const VariableValue&	getRawValue(const VariableDescr& vDescr) const		{ return data[vDescr.index]; }

	/*!
	 * \brief Returns the processed value of the variable with the given index
	 * The value returned is the processed value (e.g. passed through an eventual _transformer_ getter)
	 * @param  index variable index
	 * @return       processed variable value
	 */
	VariableValue	  getValue(int index) const;
	//! Returns the processed value of the variable with the given name
	VariableValue	  getValue(const std::string& name) const;

	std::string getCString(int index) const;
	std::string getCString(const VariableDescr& vDescr) const;
	std::string getCString(const std::string& name) const;

	std::string getCStringU8(const VariableDescr& vDescr) const;
	std::string getCStringU16(const VariableDescr& vDescr) const;
	std::string getCStringU32(const VariableDescr& vDescr) const;

	DataStore& setCString(int index, const std::string& newString);
	DataStore& setCString(const VariableDescr& vDescr, const std::string& newString);
	DataStore& setCString(const std::string& name, const std::string& newString);

	DataStore& setCStringU8(const VariableDescr& vDescr, const std::string& newString);
	DataStore& setCStringU16(const VariableDescr& vDescr, const std::string& newString);
	DataStore& setCStringU32(const VariableDescr& vDescr, const std::string& newString);

	//! \name Getters for converted values
	///@{
	/** Returns a (raw or processed) variable value properly converted and cast to the return type. */
	bool              getRawValueAsBool(int index) const;
	bool              getRawValueAsBool(const std::string& name) const;
	bool              getValueAsBool(int index) const;
	bool              getValueAsBool(const std::string& name) const;
	int               getRawValueAsInt(int index) const;
	int               getRawValueAsInt(const std::string& name) const;
	int               getValueAsInt(int index) const;
	int               getValueAsInt(const std::string& name) const;
	plc_float_t       getRawValueAsFloat(int index) const;
	plc_float_t       getRawValueAsFloat(const std::string& name) const;
	plc_float_t       getValueAsFloat(int index) const;
	plc_float_t       getValueAsFloat(const std::string& name) const;
	std::string       getRawValueAsString(int index) const;
	std::string       getRawValueAsString(const std::string& name) const;
	std::string       getValueAsString(int index) const;
	std::string       getValueAsString(const std::string& name) const;
	std::string       getValueAsCString(int index) const;
	std::string       getValueAsCString(const std::string& name) const;
	std::string       getValueAsZString(int index) const;
	std::string       getValueAsZString(const std::string& name) const;
	///@}

	//! \name Getters for string representations of values
	///@{
	/** Returns a string representation of the (raw or processed) variable value. */
	std::string       getRawValueRepr(int index) const;
	std::string       getRawValueRepr(const std::string& name) const;
	std::string       getValueRepr(int index) const;
	std::string       getValueRepr(const std::string& name) const;
	///@}

	//! \name Getters for string representations of converted values
	///@{
	/** Returns a string representation of the (raw or processed) variable value converted to a specific type. */
	std::string       getRawValueBoolRepr(int index) const;
	std::string       getRawValueBoolRepr(const std::string& name) const;
	std::string       getValueBoolRepr(int index) const;
	std::string       getValueBoolRepr(const std::string& name) const;
	std::string       getRawValueIntRepr(int index) const;
	std::string       getRawValueIntRepr(const std::string& name) const;
	std::string       getValueIntRepr(int index) const;
	std::string       getValueIntRepr(const std::string& name) const;
	std::string       getRawValueFloatRepr(int index) const;
	std::string       getRawValueFloatRepr(const std::string& name) const;
	std::string       getValueFloatRepr(int index) const;
	std::string       getValueFloatRepr(const std::string& name) const;
	std::string       getRawValueDecimalRepr(int index) const;
	std::string       getRawValueDecimalRepr(const std::string& name) const;
	std::string       getValueDecimalRepr(int index) const;
	std::string       getValueDecimalRepr(const std::string& name) const;
	std::string       getValueDecimalRepr(int index, DisplayUnitPtr asUnit) const;
	std::string       getValueDecimalRepr(const std::string& name, DisplayUnitPtr asUnit) const;
	std::string       getRawValueStringRepr(int index) const;
	std::string       getRawValueStringRepr(const std::string& name) const;
	std::string       getValueStringRepr(int index) const;
	std::string       getValueStringRepr(const std::string& name) const;
	///@}

	//! \name Setters for values
	///@{
	/** Sets a variable to the specified value (properly converted to the destination type). */
	DataStore&        setValue(int index, bool value);
	DataStore&        setValue(const std::string& name, bool value);
	DataStore&        setValue(int index, uint8_t value);
	DataStore&        setValue(const std::string& name, uint8_t value);
	DataStore&        setValue(int index, int8_t value);
	DataStore&        setValue(const std::string& name, int8_t value);
	DataStore&        setValue(int index, uint16_t value);
	DataStore&        setValue(const std::string& name, uint16_t value);
	DataStore&        setValue(int index, int16_t value);
	DataStore&        setValue(const std::string& name, int16_t value);
	DataStore&        setValue(int index, uint32_t value);
	DataStore&        setValue(const std::string& name, uint32_t value);
	DataStore&        setValue(int index, int32_t value);
	DataStore&        setValue(const std::string& name, int32_t value);
	DataStore&        setValue(int index, plc_float_t value);
	DataStore&        setValue(const std::string& name, plc_float_t value);
	DataStore&        setValue(int index, const std::string& value);
	DataStore&        setValue(const std::string& name, const std::string& value);
	DataStore&        setValue(int index, const VariableValue& value);
	DataStore&        setValue(const std::string& name, const VariableValue& value);
	///@}

	//! \name Setters for values from strings
	///@{
	/** Sets a variable to the specified string representation of a value (properly converted to the destination type). */
	DataStore&        setValueFromRepr(const VariableDescr& vDescr, const std::string& repr);
	DataStore&        setValueFromRepr(int index, const std::string& repr);
	DataStore&        setValueFromRepr(const std::string& name, const std::string& repr);

	DataStore&        setValueFromRepr(const VariableDescr& vDescr, const std::string& repr, DisplayUnitPtr fromUnit);
	DataStore&        setValueFromRepr(int index, const std::string& repr, DisplayUnitPtr fromUnit);
	DataStore&        setValueFromRepr(const std::string& name, const std::string& repr, DisplayUnitPtr fromUnit);
	///@}

	// -- default value -----------------------------------------------

	//! Returns the default value for the variable with the given index
	VariableValue	  getDefaultValue(int index) const;
	//! Returns the default value for the variable with the given name
	VariableValue	  getDefaultValue(const std::string& name) const;

	// -- min & max values --------------------------------------------

	//! Returns the minimum value for the variable with the given index
	VariableValue	  getMinValue(int index) const;
	//! Returns the minimum value for the variable with the given name
	VariableValue	  getMinValue(const std::string& name) const;
	//! Returns the maximum value for the variable with the given index
	VariableValue	  getMaxValue(int index) const;
	//! Returns the maximum value for the variable with the given name
	VariableValue	  getMaxValue(const std::string& name) const;

	// -- value remapping ---------------------------------------------

	DataStore&				setValueTransformers(const std::string& name, ValueTransformer getter, ValueTransformer setter);
	ValueTransformerPair 	getValueTransformers(const std::string& name);
	ValueTransformer 		getValueTransformerGetter(const std::string& name);
	ValueTransformer 		getValueTransformerSetter(const std::string& name);

	DataStore&				registerSimpleTransformers(const std::string& name, func_b_to_b getter, func_b_to_b setter);
	DataStore&				registerSimpleTransformers(const std::string& name, func_d_to_d getter, func_d_to_d setter);
	DataStore&				registerSimpleTransformers(const std::string& name, func_u8_to_u8 getter, func_u8_to_u8 setter);
	DataStore&				registerSimpleTransformers(const std::string& name, func_i8_to_i8 getter, func_i8_to_i8 setter);
	DataStore&				registerSimpleTransformers(const std::string& name, func_u16_to_u16 getter, func_u16_to_u16 setter);
	DataStore&				registerSimpleTransformers(const std::string& name, func_i16_to_i16 getter, func_i16_to_i16 setter);
	DataStore&				registerSimpleTransformers(const std::string& name, func_u32_to_u32 getter, func_u32_to_u32 setter);
	DataStore&				registerSimpleTransformers(const std::string& name, func_i32_to_i32 getter, func_i32_to_i32 setter);

	// -- force remote/modbus -----------------------------------------

	bool fetchRemoteVariable(int index);
	bool fetchRemoteVariable(const std::string& name);
	bool fetchRemoteVariable(const VariableDescr& vDescr);

	bool pushRemoteVariable(int index);
	bool pushRemoteVariable(const std::string& name);
	bool pushRemoteVariable(const VariableDescr& vDescr);

	// -- modbus ------------------------------------------------------

	ModbusConnector::connection_sptr_t getModbusConnection(int index, variable_mode_t mode, bool do_log = false);
	ModbusConnector::connection_sptr_t getModbusConnection(const std::string& name, variable_mode_t mode, bool do_log = false);
	ModbusConnector::connection_sptr_t getModbusConnection(const VariableDescr& vDescr, variable_mode_t mode, bool do_log = false)
	{ return m_modbus.getConnection(mode, vDescr.remote, do_log); }

	// -- online status -----------------------------------------------

	bool variableOnline(int index, variable_mode_t mode = VARIABLE_MODE_R);
	bool variableOnline(const std::string& name, variable_mode_t mode = VARIABLE_MODE_R);
	bool variableOnline(const VariableDescr& vDescr, variable_mode_t mode);
	bool variableOnline(const VariableDescr& vDescr);

	//! Returns true if all modbus connections are online
	bool online() const		{ return m_modbus.online(); }
	long nErrors() const	{ return m_modbus.nErrors(); }

	// -- modified status ---------------------------------------------

	bool       isDirty(int index) const                    { return dirty[index]; }
	DataStore& setDirty(int index, bool value = true)      { dirty[index] = value; return *this; }
	DataStore& clearDirty(int index)                       { setDirty(index, false); }
	DataStore& clearDirty();

	// signal observers, after updating the dirty status, through the shadow copy, every X ms (run in a separate thread)
	void updateObservers(long ms);
	void stopUpdateObserver();

	// update remote variable values, every X ms (run in a separate thread)
	bool remoteUpdatePauseStatus() {
		boost::mutex::scoped_lock lock(m_remote_update_pause_mutex);
		bool value = m_remote_update_pause;
		return value;
	}
	bool pauseRemoteUpdate(bool value);
	void blockRemoteUpdateWhilePaused() {
		// http://stackoverflow.com/a/4459302/1363486
		// http://stackoverflow.com/questions/4459194/pausing-a-boostthread-for-unlimited-time
		//std::cout << "blockRemoteUpdateWhilePaused(): waiting for lock" << std::endl;
	    boost::mutex::scoped_lock lock(m_remote_update_pause_mutex);
		//std::cout << "blockRemoteUpdateWhilePaused(): got lock (pause is " << (m_remote_update_pause ? "TRUE" : "FALSE") << ")" << std::endl;
	    while (m_remote_update_pause)
	    {
			//std::cout << "blockRemoteUpdateWhilePaused(): waiting for lock" << std::endl;
	        m_remote_update_pause_changed.wait(lock);
	    }
	}
	void stopRemoteUpdate();
	void updateRemotes(long ms);
	void updateRemotesBlocks(long ms);		// operate on blocks instead of on single variables

	VariableDescr::ouid_t addVariableObserver(int index, VariableObserver observer);
	VariableDescr::ouid_t addVariableObserver(const std::string& name, VariableObserver observer);
	DataStore& removeVariableObserver(int index, VariableDescr::ouid_t observer_uid);
	DataStore& removeVariableObserver(const std::string& name, VariableDescr::ouid_t observer_uid);
	DataStore& clearVariableObservers(int index);
	DataStore& clearVariableObservers(const std::string& name);

	// -- access/password level ---------------------------------------

	AccessLevelChangeCallback accessLevelChangeCallback()	{ return m_access_level_change_cb; }
	AccessLevelChangeCallback accessLevelChangeCallback(AccessLevelChangeCallback cb) {
		AccessLevelChangeCallback old = m_access_level_change_cb;
		m_access_level_change_cb = cb;
		return old;
	}

	int accessLevel() const { return m_current_access_level; }
	int accessLevel(int value) {
		int old = m_current_access_level;
		m_current_access_level = value;
		std::cerr << "ENGINE: setting current access level to " << value << std::endl;
		if ((old != m_current_access_level) && m_access_level_change_cb)
			m_access_level_change_cb(*this, old, m_current_access_level);
		return old;
	}

	boost::optional<User> currentUser() const { return userManager().loggedIn(); }

	UserManager& userManager()	            { return m_user_manager; }
	const UserManager& userManager() const	{ return m_user_manager; }

	// -- recipes -----------------------------------------------------

	std::set<std::string> recipes() const;
	bool loadRecipe(const std::string& pathname, const std::string& recipeName, RecipeProgressUpdateCallback updateCallback = 0);
	bool saveRecipe(const std::string& pathname, const std::string& recipeName);

	// -- modbus logging ----------------------------------------------

	bool modbusLogging() const          { return m_modbus_logging; }
	bool modbusLogging(bool value)      { bool old = m_modbus_logging; m_modbus_logging = value; return old; }

protected:
	DataStore& _detectContiguousBlocks();
	DataStore& _allocStorage();
	DataStore& _freeStorage();

private:
	DataStore& _loadConfigBin(const std::string& pathname);
	DataStore& _saveConfigBin(const std::string& pathname);
	DataStore& _loadConfigTxt(const std::string& pathname);
	DataStore& _saveConfigTxt(const std::string& pathname);

	bool loadRecipe_1_0_0(const std::string& pathname, const std::string& recipeName, RecipeProgressUpdateCallback updateCallback,
		MPRecipeHeader& header, FILE *fh);
	bool loadRecipe_1_1_0(const std::string& pathname, const std::string& recipeName, RecipeProgressUpdateCallback updateCallback,
		MPRecipeHeader& header, FILE *fh);

	void _on_user_manager_auth(const UserManager&, boost::optional<const User>, AuthOp op, bool success);
	void _updateForTransformers(const std::string& name, ValueTransformer getter, ValueTransformer setter);
	bool _checkVariableRange(const VariableDescr& vDescr);

	bool _updateRemoteBlock(VariableBlock& vBlock, int n_refresh);
	bool _fetchModbusBlock(const VariableBlock& vDescr);
	bool _fetchModbusBlockB(const VariableBlock& vDescr);
	bool _fetchModbusBlockU8(const VariableBlock& vDescr);
	bool _fetchModbusBlockI8(const VariableBlock& vDescr);
	bool _fetchModbusBlockU16(const VariableBlock& vDescr);
	bool _fetchModbusBlockI16(const VariableBlock& vDescr);
	bool _fetchModbusBlockU32(const VariableBlock& vDescr);
	bool _fetchModbusBlockI32(const VariableBlock& vDescr);
	bool _fetchModbusBlockF(const VariableBlock& vDescr);

	bool _fetchModbusVariable(const VariableDescr& vDescr);
	bool _fetchModbusVariableB(const VariableDescr& vDescr);
	bool _fetchModbusVariableU8(const VariableDescr& vDescr);
	bool _fetchModbusVariableI8(const VariableDescr& vDescr);
	bool _fetchModbusVariableU16(const VariableDescr& vDescr);
	bool _fetchModbusVariableI16(const VariableDescr& vDescr);
	bool _fetchModbusVariableU32(const VariableDescr& vDescr);
	bool _fetchModbusVariableI32(const VariableDescr& vDescr);
	bool _fetchModbusVariableF(const VariableDescr& vDescr);

	bool _pushModbusVariable(const VariableDescr& vDescr);
	bool _pushModbusVariableB(const VariableDescr& vDescr);
	bool _pushModbusVariableU8(const VariableDescr& vDescr);
	bool _pushModbusVariableI8(const VariableDescr& vDescr);
	bool _pushModbusVariableU16(const VariableDescr& vDescr);
	bool _pushModbusVariableI16(const VariableDescr& vDescr);
	bool _pushModbusVariableU32(const VariableDescr& vDescr);
	bool _pushModbusVariableI32(const VariableDescr& vDescr);
	bool _pushModbusVariableF(const VariableDescr& vDescr);

	// min/max reference dependents
	void _fetchDependents(const VariableDescr& vDescr, std::set<VariableBlockSPtr>& fetched);
	std::set<VariableBlockSPtr> _computeAffectedBlocks(const std::vector<VariableDescrSPtr>& variables);
	void _computeAffectedBlocks(std::set<VariableBlockSPtr> dst, const std::vector<VariableDescrSPtr>& variables);
	void addBlockSet(std::set<VariableBlockSPtr> dst, const std::set<VariableBlockSPtr>& src);

	// backing store
	bool backingStore() const 								{ return m_backing_store_enabled; }
	bool backingStore(bool value)							{ bool old = m_backing_store_enabled; if (! data) m_backing_store_enabled = value; return old; }
	void backingStoreFile(const std::string& pathname)		{ m_backing_store_pathname = pathname; }
	bool backingStoreActive() const 						{ return ((m_backing_mmap_base != MAP_FAILED) && m_backing_mmap_base && (data == m_backing_mmap_base)); }
	bool _mmapData(bool& created);
	bool _munmapData();

	bool _stringsSave();
	bool _stringsLoad(std::map<StringTable::str_id_t, StringTable::str_id_t>& remapped, bool& hasRemapped);

	// remote update trace
	void _trace_remotes_printStart();
	void _trace_remotes_printStop();

	// tracing
	bool globalTrace() const			{ return m_global_trace; }
	bool globalTrace(bool value)		{ bool old = m_global_trace; m_global_trace = value; return old; }
	bool trace() const					{ return m_trace; }
	bool trace(bool value)				{ bool old = m_trace; m_trace = value; return old; }

	size_t size;
	std::vector<VariableDescrSPtr> descr_v;
	std::map<std::string, VariableDescrSPtr> descr_m;
	VariableValue *data;
	VariableValue *shadow;
	VariableValue *remote;								// value fetched from/pushed to remote node
	bool *dirty;
	variable_state_t *variable_state;
	unsigned long *remote_cycle;						// # of remote update cycle per variable
	unsigned long *remote_cycle_block;					// # of remote update cycle per variable block

	unsigned long remote_cycle_ctr;						// # of next global remote update cycle

	ModbusConnector m_modbus;

	// minReference & maxReference index to list of dependent variable descriptions
	std::map< int, std::vector<VariableDescrSPtr> > m_minRef_dependents;
	std::map< int, std::vector<VariableDescrSPtr> > m_maxRef_dependents;

	std::string      m_db_config_pathname;

	value_transformers_map_t m_transformers_map;

	bool m_update_observers_run;						// flag indicating updateObservers() should run/quit

	// mmap-based backing store
	bool             m_backing_store_enabled;
	int              m_backing_fd;
	void            *m_backing_mmap_base;
	size_t           m_backing_mmap_size;
	std::string      m_backing_store_pathname;
	BackingStoreMode m_backing_mode;

	boost::mutex m_remote_update_pause_mutex;
	boost::condition_variable m_remote_update_pause_changed;
	bool m_remote_update_pause;
	bool m_remote_update_run;							// flag indicating updateRemotesBlocks() / updateRemotes() should run/quit

	bool m_global_trace;
	bool m_trace;

	bool m_modbus_logging;

	int m_current_access_level;
	AccessLevelChangeCallback m_access_level_change_cb;
	UserManager m_user_manager;

	// contiguous blocks of remote variables
	std::vector<VariableBlockSPtr> m_vblocks;

	// remote stats
	long r_st_r_ops;									// [remote stats] total number of remote read operations
	long r_st_w_ops;									// [remote stats] total number of remote write operations
	long r_st_rw_ops;									// [remote stats] total number of remote read/write operations
	stats_time_point_t r_st_updated;					// [remote stats] time of last update
	stats_time_point_t r_st_period_start;				// [remote stats] start of stats period
	long r_st_period_r_ops;								// [remote stats] number of remote read operations in last stat period
	long r_st_period_w_ops;								// [remote stats] number of remote write operations in last stat period
	long r_st_period_rw_ops;							// [remote stats] number of remote read/write operations in last stat period

	// remote update trace
	stats_clock::time_point ru_trace_start;
	stats_clock::time_point ru_trace_cycle_start;

	DataStore& _statsIncRead(int rc);
	DataStore& _statsIncWrite(int rc);
	DataStore& _statsPrint();

	static const std::string DEFAULT_DB_CONFIG;
	static const std::string DEFAULT_DB_CONFIG_ENVVAR;
	static const bool DEFAULT_MODBUS_LOGGING;
	static const int DEFAULT_REFRESH_CYCLES;
	static const int DEFAULT_ACCESS_LEVEL;
	static const bool DEFAULT_BACKING_STORE_ENABLE;
	static const std::string DEFAULT_BACKING_STORE_PATHNAME;
};

/*!
 * \class DataStoreProvider
 * \brief Monoid factory for constucting/loading a default DataStore instance.
 *
 * \b TODO
 *
 * \see DataStore
 * \see Andrei Alexandrescu, "Modern C++ Design"
 */
class DataStoreProvider
{
public:
  static SHARED_PTR<DataStore> LoadDefault(const std::string& pathname, DataStoreInitializer initializer_func = 0);
  static SHARED_PTR<DataStore> Default(DataStoreInitializer initializer_func = 0);

private:
  static void CleanUp();

  static SHARED_PTR<DataStore> m_default;
};


} /* end of namespace TDE */

#endif /* TDE_ENGINE_H */
