

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  This file is part of CMSDK version 2.1.1.260726 (26 July 2026)
//_/_/  
//_/_/  developed by
//_/_/  
//_/_/  Communicative Machines Ltd. 
//_/_/    http://cmlabs.com/
//_/_/  Copyright (c) 2026
//_/_/  
//_/_/  in collaboration with 
//_/_/  
//_/_/  Icelandic Institute for Intelligent Machines ses
//_/_/    Menntavegur 1
//_/_/    101 Reykjavik
//_/_/    ICELAND
//_/_/    http://www.iiim.is/
//_/_/
//_/_/  and is released under the following
//_/_/  
//_/_/  EXTENDED BSD License
//_/_/
//_/_/  Redistribution and use in source and binary forms, with or without
//_/_/  modification, is permitted provided that the following conditions 
//_/_/  are met:
//_/_/
//_/_/  - Redistributions of source code must retain the above copyright 
//_/_/    and collaboration notice, this list of conditions and the following 
//_/_/    disclaimer.
//_/_/
//_/_/  - Redistributions in binary form must reproduce the above copyright 
//_/_/    notice, this list of conditions and the following disclaimer in the 
//_/_/    documentation and/or other materials provided with the distribution.
//_/_/
//_/_/  - Neither the name of its copyright holders nor the names of its 
//_/_/    contributors may be used to endorse or promote products derived from 
//_/_/    this software without specific prior written permission.
//_/_/
//_/_/  - CADIA Clause: The license granted in and to the software under this 
//_/_/    agreement is a limited-use license. The software may not be used in 
//_/_/    furtherance of: (i) intentionally causing bodily injury or severe 
//_/_/    emotional harm to any person; (ii) invading the personal privacy or 
//_/_/    violating the human rights of any person; or (iii) committing or 
//_/_/    preparing for any act of war. 
//_/_/
//_/_/  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
//_/_/  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
//_/_/  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
//_/_/  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 
//_/_/  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
//_/_/  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
//_/_/  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
//_/_/  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
//_/_/  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
//_/_/  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
//_/_/  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//_/_/
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
/*! @file PsyAPI.h
	@brief PsyAPI — the component-facing API handle of the CMSDK.

	While PsySpace is the process/shared-memory *context* a component lives in,
	PsyAPI is the *handle* an individual component (crank) uses to interact with
	the running Psyclone system: waiting for trigger messages, posting output
	messages, emitting and waiting for signals, storing private data, managing
	parameters, and querying whiteboards and catalogs. Each component gets its own
	PsyAPI instance, created and owned by its hosting PsySpace
	(see PsySpace::getCrankAPI()).

	Typical crank usage:
	@code
	PsySpace space("MyProcess");
	space.connect(1);                              // join system 1
	PsyAPI* api = space.getCrankAPI("MyModule.MyCrank");
	while (api->shouldContinue()) {
		if (DataMessage* msg = api->waitForNewMessage(100)) {
			// process msg (owned by the system, do not delete)
			api->postOutputMessage();              // post per active spec entries
		}
	}
	@endcode
*/
#if !defined(_PSYAPI_H_)
#define _PSYAPI_H_

#include "MemoryManager.h"
#include "PsyTime.h"
#include "xml_parser.h"
#include "DataMessage.h"
#include "Subscriptions.h"
#include "PsySpace.h"

/*! @name Crank API status codes
	States of a crank's API handle within its hosting PsySpace.
	@{ */
#define CRANKAPI_FAILED		1 //!< The API could not be obtained or is invalid.
#define CRANKAPI_INUSE		2 //!< The API is already in use by another thread.
#define CRANKAPI_RUNNING	3 //!< The crank is currently executing.
#define CRANKAPI_IDLE		4 //!< The crank is idle, waiting for input.
/*! @} */

/*! @name Post result codes
	Negative return values of PsyAPI::postOutputMessage(); non-negative values are the number of messages posted.
	@{ */
#define POST_FAILED			-1 //!< An error occurred while posting.
#define POST_NOSPEC			-2 //!< Posting attempted before any trigger arrived (no active spec).
#define POST_OUTOFCONTEXT	-3 //!< The crank's context is no longer active.
/*! @} */

/*! @name Query status codes
	Status values returned by the query/retrieve family of calls (PsyAPI::queryCatalog(), PsySpace::query(), ...).
	@{ */
#define QUERY_FAILED			1 //!< General failure.
#define QUERY_TIMEOUT			2 //!< No reply within the timeout.
#define QUERY_NAME_UNKNOWN		3 //!< The query name is not defined in the spec.
#define QUERY_COMPONENT_UNKNOWN	4 //!< The target component is unknown.
#define QUERY_QUERYFAILED		5 //!< The target processed the query but reported failure.
#define QUERY_SUCCESS			6 //!< The query succeeded.
#define QUERY_NOT_AVAILABLE		7 //!< The target exists but is not available.
#define QUERY_NOT_REACHABLE		8 //!< The target could not be reached (e.g. remote system down).
/*! @} */

namespace cmlabs {

/*! Log source id used by components when writing entries to the system log. */
#define LOG_COMPONENT		8

/*! Pairing of a trigger message and its accompanying data message, as queued for a crank's input. */
typedef std::pair<DataMessage*, DataMessage*> TriggerAndMessage;

/*! @brief The API handle a component (crank) uses to talk to the Psyclone system.

	See the @ref PsyAPI.h "file documentation" for the PsySpace/PsyAPI relationship
	and a usage example. Instances are created and owned by the hosting PsySpace;
	cranks receive theirs as the argument to their CrankFunction, embedders obtain
	one via PsySpace::getCrankAPI(). Messages returned by waitForNewMessage() and
	waitForSignal() are owned by the system and must not be deleted by the crank.
	An instance should only be used from one crank thread at a time.
*/
class PsyAPI {
	friend class PsySpace;
public:

	/*! @name System control message types
		Well-known PsyType values used internally for system lifecycle and control traffic.
		Cranks rarely use these directly, but may see them referenced in specs/logs.
		@{ */
	static struct PsyType CTRL_SYSTEM_READY;             //!< Broadcast when the system has fully started.
	static struct PsyType CTRL_PROCESS_INITIALISE;       //!< Sent to a process to initialise it.
	static struct PsyType CTRL_PROCESS_GREETING;         //!< Handshake from a newly joined process.
	static struct PsyType CTRL_PROCESS_SHUTDOWN;         //!< Orders a single process to shut down.
	static struct PsyType CTRL_CONTEXT_CHANGE;           //!< Announces a system context change.
	static struct PsyType CTRL_SYSTEM_SHUTDOWN;          //!< Orders a full system shutdown.
	static struct PsyType CTRL_SYSTEM_SHUTTINGDOWN;      //!< Broadcast while shutdown is in progress.
	static struct PsyType CTRL_TRIGGER;                  //!< Wraps a trigger delivery to a component.
	static struct PsyType CTRL_TRIGGER_GROUP;            //!< T1.6: wraps a <code>&lt;triggergroup&gt;</code> joined-set delivery (member map) to a component.
	static struct PsyType CTRL_QUERY;                    //!< Carries a query to a component/catalog.
	static struct PsyType CTRL_QUERY_REPLY;              //!< Carries a query reply back to the asker.
	static struct PsyType CTRL_PULLCOMPONENTDATA;        //!< Requests component data from another node.
	static struct PsyType CTRL_CREATECUSTOMPAGE;         //!< Registers a custom PsyProbe view.
	static struct PsyType CTRL_ADDSUBSCRIPTION;          //!< Adds triggers/posts to a registration at runtime.
	static struct PsyType CTRL_RETRIEVESYSTEMIDS;        //!< Requests the full id/name tables.
	static struct PsyType CTRL_INTERSYSTEM_QUERY;        //!< Query crossing to a different Psyclone system.
	static struct PsyType CTRL_INTERSYSTEM_QUERY_REPLY;  //!< Reply from a different Psyclone system.
	/*! @} */

	// ***************** Constructors *****************
	// Create and destroy resources

	/*! Constructor from a PsySpace.

		Normally called only by the hosting PsySpace itself (PsySpace::getCrankAPI()
		creates one per component); application code rarely constructs a PsyAPI
		directly. The handle keeps a plain pointer to @a space and uses it for every
		call, so the space must outlive the API object.

		@param space The space in which the PsyAPI shall be created (not owned; must outlive this object).
	*/
	PsyAPI(PsySpace* space);
	/*! Destructor for the PsyAPI.

		Deletes any internally held trigger/input/signal messages (the ones handed
		out by waitForNewMessage()/waitForSignal()), so pointers previously returned
		by those calls become invalid. Owned and destroyed by the hosting PsySpace —
		do not delete a handle obtained via PsySpace::getCrankAPI().
	*/
	~PsyAPI();

	/*! Internal use only

	*/
	/*! Internal use only — recover a PsyAPI pointer passed to Python as an integer (SWIG bridge).
		@param ptr Pointer value previously handed to Python.
		@return The PsyAPI pointer, or NULL if invalid.
	*/
	static PsyAPI* fromPython(unsigned long long ptr);
	/*! Internal use only — debug helper returning a printable address string for a Python-held pointer. */
	static const char* fromPythonAddressOf(unsigned long long ptr);

	/*! Internal use only

	*/
	bool setAutoDelete(bool autodel);

	// ***************** System & Status *****************
	// All system calls
	// Get or set status info

	/*! Internal use only

	*/
	bool isRunning();

	/*!
		Check whether the crank should keep running, or exit its loop.

		This is the cooperative shutdown mechanism of the CMSDK: when the system (or
		the hosting space) shuts the component down, this starts returning false.
		There is no forced thread kill, so a crank MUST poll this regularly —
		typically as the condition of its main processing loop — or it will keep the
		process from shutting down cleanly.

		@code
		while (api->shouldContinue()) {
			if (DataMessage* msg = api->waitForNewMessage(100)) {
				// handle msg
			}
			// short timeout above ensures shouldContinue() is re-checked often
		}
		@endcode

		@return true if the crank should continue.
		@note If the internal mutex cannot be acquired within 1 second this returns
		true (i.e. it errs on the side of "keep running").
	*/
	bool shouldContinue();
	/*!
		Get the name of the module written into the memory of name and return size
		@param name the chunk of memory that the name of the module will be written into
		@param maxSize the size of the memory chunk of tha name parameter
		@return true if module with that name exists and the size of the name is smaller than maySize
	*/
	bool getModuleName(char* name, uint32 maxSize);
	/*!
	   Get the base dir that Psyclone is running from
	   @return the base dir as a string
	*/
	std::string getCommandlineBasedir();
	/*! Internal use only

	*/
	bool setCommandlineBasedir(const char* cmdlineBasedir);
	/*! Get the module name

		@return module name as String

	*/
	std::string getModuleName();
	/*! Get the name of another module by id
	@param id the id of the other module
	@return module name as String

	*/
	std::string getOtherModuleName(uint32 id);
	/*! Get the size of the input queue, i.e. how many unprocessed trigger messages waiting to be processed

	@return size of the input queue

	*/
	uint32 getInputQueueSize();
	/*! Get textual representation of the type
	@param type type as PsyType object
	@return type as string

	*/
	std::string typeToText(PsyType type);
	/*! Get textual representation of the context
	@param context context as PsyContext object
	@return context as string

	*/
	std::string contextToText(PsyContext context);
	/*! Internal use only

	*/
	bool getCurrentScriptLanguage(char* language, uint32 maxSize);
	/*! Internal use only

	*/
	bool getCurrentScript(char* script, uint32 maxSize);
	/*! Internal use only

	*/
	bool getCurrentScriptFilename(char* filename, uint32 maxSize);
	/*! Internal use only

	*/
	std::string getCurrentScriptLanguage();
	/*! Internal use only

	*/
	std::string getCurrentScript();
	/*! Internal use only

	*/
	std::string getCurrentScriptFilename();
	/*! Get a list of post names currently active - i.e. the post names that can be used right now
	@return the post names as a list of strings

	*/
	std::set<std::string> getCurrentPostNames();

	/*! Check to see if the signal name is currently active - i.e. can this signal be used right now
	@return true if the signal name is active, otherwise false

	*/
	bool hasCurrentSignalName(const char* name);

	/*! Check to see if the query name is currently active - i.e. can this query be used right now
	@return true if the query name is active, otherwise false

	*/
	bool hasCurrentQueryName(const char* name);

	/*! Check to see if the retrieve name is currently active - i.e. can this retrieve be used right now
	@return true if the retrieve name is active, otherwise false

	*/
	bool hasCurrentRetrieveName(const char* name);

	/*! Check to see if the post name is currently active - i.e. can this post be used right now
	@return true if the post name is active, otherwise false

	*/
	bool hasCurrentPostName(const char* name);

	// ***************** Subscription *****************
	// Get or delete current subscriptions
	// Create new subscription
	// *** Not implemented yet



	// ***************** Tags *****************
	// Create, destroy or get info about tags
	// *** Not implemented yet




	// ***************** Messaging *****************
	/*!
	Wait for the next trigger message from the component's input queue.

	This is the heart of a crank's main loop: it blocks (on a semaphore) for up to
	@a ms milliseconds until a trigger delivery is available, dequeues it, makes it
	the "current" message/trigger context (so getCurrentTriggerName(),
	postOutputMessage(), emitSignal(), retrieve() and queryCatalog() know which
	spec entries are active), updates the component's CPU/wall statistics, and
	returns the data message.

	@code
	while (api->shouldContinue()) {
		DataMessage* msg = api->waitForNewMessage(100);
		if (!msg)
			continue;                        // timeout - loop and re-check shouldContinue()
		const char* text = msg->getString("content");
		// ... compute something ...
		DataMessage* out = new DataMessage();
		out->setString("result", "done");
		api->postOutputMessage(NULL, out);   // post per the active spec entries
	}
	@endcode

	@param ms Maximum time to block, in milliseconds. Use a short timeout (e.g.
	100 ms) so the surrounding loop can re-check shouldContinue() promptly.
	@return The message, or NULL if none arrived within the timeout.
	@note Ownership: the returned message is owned by the API handle — do NOT
	delete it. It remains valid until the next call to waitForNewMessage(), which
	frees the previous one (unless auto-delete has been disabled internally).
	@note Backpressure: if the input queue has grown beyond 100 entries, queued
	messages whose policy includes MESSAGE_NON_GUARANTEED are silently dropped
	until the queue is back under the limit — only guaranteed traffic survives an
	overloaded component.
	@warning Call only from the single thread driving this crank; a PsyAPI is not
	meant to be shared between threads.
	*/
	DataMessage* waitForNewMessage(uint32 ms);

	/*! T1.6: Wait for the next <code>&lt;triggergroup&gt;</code> joined-set delivery from the input queue.
	Blocks up to @a ms ms for a CTRL_TRIGGER_GROUP activation, makes it the current
	trigger context (getCurrentTriggerName() returns the group name), and returns the
	completed (or partial, on timeout) set as a map of member trigger name -> message.
	Optional/absent members are simply not in the map (crank checks map.count("name")).
	Ordinary single-message deliveries are ignored here — use waitForNewMessage() for those.
	@param ms Maximum time to block, in milliseconds.
	@param groupName Set to the group name that fired (empty if none/timeout).
	@return Map of trigger name -> message; empty on timeout. Messages are owned by the
	API handle and freed on the next waitForNewMessageGroup()/waitForNewMessage() call. */
	std::map<std::string, DataMessage*> waitForNewMessageGroup(uint32 ms, std::string& groupName);


	/*! Get the name of the current trigger, i.e. the spec trigger entry that matched
	the message most recently returned by waitForNewMessage(). Useful when a crank
	subscribes to several triggers and needs to branch on which one fired.
	@return Trigger name as string (empty before the first trigger arrives).
	*/
	std::string getCurrentTriggerName();

	/*! Get the context of the current trigger, just retrieved using waitForNewMessage()
	@return Context as PsyContext

	*/
	PsyContext getCurrentTriggerContext();


	/*!
	This function waits for the next trigger message to arrive and if it did within the timeout (ms)
	the message is returned and triggerName indicates the name of the trigger from the spec.
	The message is owned by the system and should never be deleted by the crank.
	If no message arrives within the timeout NULL is returned.

	@param ms the timeout in milliseconds
	@param triggerName Receives a pointer to the trigger name from the spec, or
	NULL when the dequeued item was not a trigger delivery (e.g. an incoming
	query). Points into an internal buffer — valid only until the next
	waitForNewMessage() call; copy it if you need to keep it.
	@return the message or NULL if none arrives
	@note Same blocking, ownership and overload-drop semantics as
	waitForNewMessage(uint32); see there for the details and a usage example.
	*/
	DataMessage* waitForNewMessage(uint32 ms, const char* &triggerName);
	/*!
	Get the time the current trigger (or, failing that, the current message) was
	received by the system, as a PsyTime timestamp (µs since year 0 — see
	PsyTime.h). Handy for measuring end-to-end latency:

	@code
	int64 latencyUs = GetTimeAge(api->getCurrentMessageReceiveTime());
	api->logPrint(2, "Trigger latency: %lld us", latencyUs);
	@endcode

	@return Receive time in µs, or 0 if no message has been received yet.
	*/

	uint64 getCurrentMessageReceiveTime();

	// Post output messages
	/**
	Post the crank's output according to the spec's post entries.

	This is the normal way a component produces output. What actually gets sent
	is determined by the &lt;post&gt; entries attached to the *current trigger* in the
	PsySpec: for every matching active post entry the message is stamped with that
	entry's type, destination, tag, TTL and policy (and any content the entry adds)
	and delivered — so one call can fan out to several receivers. Because of this,
	postOutputMessage() can only be used after a trigger has arrived via
	waitForNewMessage(); before that there is no active spec (POST_NOSPEC), and
	after a context change the entries may no longer apply (POST_OUTOFCONTEXT).

	@code
	DataMessage* out = new DataMessage();
	out->setString("result", answer.c_str());
	int32 n = api->postOutputMessage("MyOutput", out);   // out now belongs to the API
	if (n <= 0)
		api->logPrint(0, "post failed: %d", n);          // POST_FAILED/NOSPEC/OUTOFCONTEXT
	@endcode

	Negative return values:

	POST_FAILED -1   Error occurred while trying to post the message

	POST_NOSPEC -2   The crank tried to post a message before a trigger arrived

	POST_OUTOFCONTEXT -3   The crank is no longer active because the context has changed and is no longer active

	@param postName name of the Post. If none is provided then all active post entries will be used and one copy of the message posted per post entry.
	@param msg message. If none is provided an empty message will be used instead.
	@return number of messages sent (0 if no entries matched), or a negative POST_* code.
	@warning Ownership: @a msg is consumed by this call — the system copies it per
	post entry and deletes the original before returning. Never delete or reuse
	@a msg after posting; allocate a fresh message for each call.
	@note If @a msg has no tag of its own, the tag of the current input message is
	propagated onto it, keeping message chains correlated across components.
	*/
	int32 postOutputMessage(const char* postName = NULL, DataMessage* msg = NULL);

	// Post output messages
	/**
	Post the crank's output, selecting the spec post entries by message type
	rather than by post name.

	Convenience overload of postOutputMessage(const char*, DataMessage*): sets
	@a msgType on @a msg (when given) and posts it; the active post entries whose
	type matches are used. See the by-name overload for the full semantics, the
	POST_* error codes and the ownership rules — in particular, @a msg is consumed
	by the call and must not be deleted or reused afterwards.

	@param msgType msg type of the Post. If none is provided then all active post entries will be used and one copy of the message posted per post entry.
	@param msg message. If none is provided an empty message will be used instead.
	@return number of messages sent, or a negative POST_* code.
	*/
	int32 postOutputMessage(PsyType msgType, DataMessage* msg = NULL);


	// ***************** Signals *****************
	/**
	Emit a signal by its spec name.

	Signals are lightweight, latest-value broadcasts (in contrast to posted
	messages, which are queued per receiver): whoever waits on the signal type
	sees only the most recent emission. The name is resolved against the current
	trigger's &lt;signal&gt; entries in the PsySpec, so this — like
	postOutputMessage() — only works after a trigger has been received; check
	hasCurrentSignalName() to see whether a signal name is usable right now.

	@param name name of the signal to be sent (as defined in the spec)
	@param msg message to be sent, empty message if NULL
	@return true if successful; false if there is no active trigger context, the
	name is unknown in the spec, or emission failed.
	@warning Ownership: @a msg is consumed — the space takes it over on success
	and the call deletes it on spec/emission failure. Do not delete or reuse it
	after this call.
	*/
	bool emitSignal(const char* name, DataMessage* msg = NULL);
	/*!
	Wait for a signal (latest-value broadcast) by its spec name.

	Blocks for up to @a timeout ms until a signal matching the spec entry @a name
	has been emitted. Because signals are latest-value, @a lastReceivedTime is how
	a polling loop avoids seeing the same emission twice: pass the send time of
	the last signal you processed and only newer ones are returned.

	@code
	uint64 lastSeen = 0;
	while (api->shouldContinue()) {
		if (DataMessage* sig = api->waitForSignal("Heartbeat", 500, lastSeen)) {
			lastSeen = sig->getCreatedTime();   // do not delete sig - owned by the API
			// react to the signal ...
		}
	}
	@endcode

	@param name name of the signal that is waited for (from the spec of the current trigger)
	@param timeout maximum time to block, in milliseconds
	@param lastReceivedTime signals sent at or before this PsyTime timestamp are skipped; 0 returns the next unread signal
	@return the signal message, or NULL on timeout / unknown name / no active trigger context.
	@note Ownership: the returned message is owned by the API handle and freed on
	the next successful waitForSignal() call — do not delete it.
	*/
	DataMessage* waitForSignal(const char* name, uint32 timeout, uint64 lastReceivedTime = 0);


	/*!
	This function retrieves all names for types, contexts and components
	@return message containing all names for types, contexts and components

	*/
	DataMessage* retrieveAllSystemIDs() {
		return space->manager->retrieveAllSystemIDs();
	}


	/*!
	This function adds triggers and posts to an existing registration

	@param xml &lt;registration name="modulename"&gt;&lt;trigger.../&gt;...&lt;post.../&gt;&lt;/registration&gt;
	@return true if successful

	*/
	bool addSubscription(const char* xml);


	// ***************** PsyProbe plugins *****************
	/*!
	Add a custom module view tab for the PsyProbe web interface by providing a template file
	- this file will need to use PsyProbe javascript to work - see the PsyProbe documentation for more info.

	@param name name of the custom module view tab
	@param templateURL The path to the template file
	@return true if successful

	*/
	bool addPsyProbeCustomView(const char* name, const char* templateURL);
	/*!
	Remove a custom module view tab for the PsyProbe web interface

	@param name name of the custom view
	@return true if removal was successful


	*/
	bool removePsyProbeCustomView(const char* name) {
		return addPsyProbeCustomView(name, "");
	}

	// ***************** Own Data *****************
	// Load, save or delete own persistent data
	/*!
	Store a named chunk of data centrally, in the system's shared component memory.

	Private data survives the crank exiting (or crashing) and being re-triggered
	later — it lives in the node's shared memory, keyed by component id, not in
	the crank's process memory. Use it for state that must persist across
	activations without setting up a whiteboard or external storage.

	@code
	// save state on the way out ...
	MyState st = ...;
	api->setPrivateData("state", (const char*)&st, sizeof(st));
	// ... and restore it on the next activation:
	uint64 size = 0;
	if (char* data = api->getPrivateDataCopy("state", size)) {
		if (size == sizeof(MyState))
			memcpy(&st, data, sizeof(MyState));
		delete [] data;                 // caller owns the copy
	}
	@endcode

	@param name name of the data chunk
	@param data the data to be stored (copied; the caller keeps ownership of its buffer)
	@param size memory size of the data
	@param mimetype optional mimetype of the data
	@return true if successful
	*/

	bool setPrivateData(const char* name, const char* data, uint64 size, const char* mimetype = NULL) {
		return space->manager->componentMemory->setPrivateData(currentCompID, name, data, size, mimetype);
	}
	/*!
	This returns the size of the private data chunk previously saved by the crank.

	@param name name of the data chunk
	@return size of the private data chunk


	*/
	uint64 getPrivateDataSize(const char* name) {
		return space->manager->componentMemory->getPrivateDataSize(currentCompID, name);
	}
	/*!
	Returns a newly allocated copy of a private data entry, with the size written into size.

	@param name name of the data chunk
	@param size receives the size of the returned data
	@return new copy of the entry — the CALLER owns it and must delete [] it — or NULL if the entry does not exist. See setPrivateData() for a round-trip example.
	*/
	char* getPrivateDataCopy(const char* name, uint64 &size) {
		return space->manager->componentMemory->getPrivateDataCopy(currentCompID, name, size);
	}
	/*!
	Reads a private chunk from the system into existing data space, maximum size is maxSize. 

	@param name name of the data chunk
	@param data pointer to an already allocated memory chunk
	@param maxSize maximum size of the already allocated memory chunk
	@return  true if reading was successful


	*/
	bool getPrivateData(const char* name, char* data, uint64 maxSize) {
		return space->manager->componentMemory->getPrivateData(currentCompID, name, data, maxSize);
	}
	/*!
	Deletes a private data chunk by name.

	@param name the name of the chunk to be deleted
	@return  true if deletion was successful


	*/
	bool deletePrivateData(const char* name) {
		return space->manager->componentMemory->deletePrivateData(currentCompID, name);
	}

	// ***************** Own Parameters *****************
	// Get, set, reset, create or destroy own parameters
	/*!
	Creates a new string parameter.

	Parameters are the component's tunable settings: they live in shared
	component memory (like private data), persist across activations, and are
	visible and editable live in the PsyProbe web interface — which is the main
	reason to prefer a parameter over an ordinary variable. If a parameter with
	the same name already exists the call fails (returns false) and the existing
	value is left untouched, so calling createParameter() unconditionally at the
	top of a crank will not clobber a value tuned via PsyProbe.

	@code
	if (!api->hasParameter("Threshold"))
		api->createParameter("Threshold", (float64)0.5, 0.0, 1.0, 0.05);
	float64 threshold = api->getParameterFloat("Threshold");
	@endcode

	@param name name of the parameter
	@param val value of the parameter
	@param defaultValue default value when resetting parameter, NULL if val should be used
	@return true if creation was successful
	*/
	bool createParameter(const char* name, const char* val, const char* defaultValue = NULL) {
		return space->manager->componentMemory->createParameter(currentCompID, name, val, defaultValue);
	}
	/*!
	Creates a new string list parameter with room for count entries

	@param name name of the parameter
	@param val value array of the parameter, separed by binary \0
	@param count number of entries in the array
	@param defaultIndex an int value that sets the default index in the list
	@return true if creation was successful


	*/
	bool createParameter(const char* name, const char* val, uint32 count, uint32 defaultIndex) {
		return space->manager->componentMemory->createParameter(currentCompID, name, val, count, defaultIndex);
	}
	/*!
	Creates a new string parameter with a list of values

	@param name name of the parameter
	@param values vector of values of the parameter
	@param defaultValue default value or NULL for the first entry
	@return true if creation was successful


	*/
	bool createParameter(const char* name, std::vector<std::string> values, const char* defaultValue = NULL) {
		return space->manager->componentMemory->createParameter(currentCompID, name, values, defaultValue);
	}
	/*!
	Creates a new integer list parameter with room for count entries 

	@param name name of the parameter
	@param val array of values of the parameter
	@param count number of entries in the array
	@param defaultIndex an int value that sets the default index in the list
	@return true if creation was successful


	*/
	bool createParameter(const char* name, int64* val, uint32 count, uint32 defaultIndex) {
		return space->manager->componentMemory->createParameter(currentCompID, name, val, count, defaultIndex);
	}
	/*!
	Creates a new String parameter with list of integer values (?)

	@param name name of the parameter
	@param values vector with values of the parameter
	@param defaultValue default as int
	@return true if creation was successful


	*/
	bool createParameter(const char* name, std::vector<std::string> values, int64 defaultValue) {
		return space->manager->componentMemory->createParameter(currentCompID, name, values, defaultValue);
	}
	/*!
	Creates a new integer parameter with list of values

	@param name name of the parameter
	@param values vector of values of the parameter
	@param defaultValue default is 0
	@return true if creation was successful


	*/
	bool createParameter(const char* name, std::vector<int64> values, int64 defaultValue = 0) {
		return space->manager->componentMemory->createParameter(currentCompID, name, values, defaultValue);
	}
	/*!
	Creates a new float parameter

	@param name name of the parameter
	@param val array of value of the parameter
	@param count number of values in the array
	@param defaultIndex an int value that sets the default index in the list
	@return true if creation was successful


	*/
	bool createParameter(const char* name, float64* val, uint32 count, uint32 defaultIndex) {
		return space->manager->componentMemory->createParameter(currentCompID, name, val, count, defaultIndex);
	}
	/*!
	Creates a new string parameter with list of float values (?)

	@param name name of the parameter
	@param values vector of values of the parameter
	@param defaultValue float value as default
	@return true if creation was successful


	*/
	bool createParameter(const char* name, std::vector<std::string> values, float64 defaultValue) {
		return space->manager->componentMemory->createParameter(currentCompID, name, values, defaultValue);
	}
	/*!
	Creates a new float parameter with list of float values (?)

	@param name name of the parameter
	@param values vector of values of the parameter
	@param defaultValue default is 0
	@return true if creation was successful


	*/
	bool createParameter(const char* name, std::vector<float64> values, float64 defaultValue = 0) {
		return space->manager->componentMemory->createParameter(currentCompID, name, values, defaultValue);
	}
	/*!
	Creates a new integer parameter

	@param name name of the parameter
	@param val value of the parameter
	@param min the minimum value
	@param max the maximum value
	@param interval interval limit, amount to jump on every tweak
	@return true if creation was successful


	*/
	bool createParameter(const char* name, int64 val, int64 min = 0, int64 max = 0, int64 interval = 0) {
		return space->manager->componentMemory->createParameter(currentCompID, name, val, min, max, interval);
	}
	/*!
	Creates a new float parameter with min, max and interval limits for allowed values

	@param name name of the parameter
	@param val value of the parameter
	@param min minimum value
	@param max maximum value
	@param interval interval limit, amount to jump on every tweak
	@return true if creation was successful


	*/
	bool createParameter(const char* name, float64 val, float64 min = 0, float64 max = 0, float64 interval = 0) {
		return space->manager->componentMemory->createParameter(currentCompID, name, val, min, max, interval);
	}
	/*!
	Checks if a named parameter exists

	@param name name of the parameter
	@return  true if it exists, false otherwise


	*/
	bool hasParameter(const char* name) {
		return space->manager->componentMemory->hasParameter(currentCompID, name);
	}
	/*!
	Deletes a named parameter

	@param name name of the parameter
	@return true if deletion was successful


	*/
	bool deleteParameter(const char* name) {
		return space->manager->componentMemory->deleteParameter(currentCompID, name);
	}
	/*!
	Gets the parameter type

	\#define PARAM_STRING 1 

	\#define PARAM_INTEGER 2 

	\#define PARAM_FLOAT 3 

	\#define PARAM_STRING_COLL 4 

	\#define PARAM_INTEGER_COLL 5 

	\#define PARAM_FLOAT_COLL 6 

	\#define PARAM_TYPE_COLL 7

	@param name name of the parameter
	@return  1 for String, 2 for int, 3 for float, 4 for String Collection, 5 for int collection, 6 for florat collection, 7 for type collection


	*/
	uint8 getParameterDataType(const char* name) {
		return space->manager->componentMemory->getParameterDataType(currentCompID, name);
	}
	/*!
	Return the amount of memory in bytes used by the parameter

	@param name name of the parameter
	@return  amount of memory in bytes


	*/
	uint32 getParameterValueSize(const char* name) {
		return space->manager->componentMemory->getParameterValueSize(currentCompID, name);
	}
	/*!
	Read string parameter into existing data chunk val, maximum size maxSize. 

	@param name name of the parameter
	@param val value of the parameter
	@param maxSize maximal size of the parameter to be read
	@return  true if reading was successful


	*/
	bool getParameter(const char* name, char* val, uint32 maxSize) {
		return space->manager->componentMemory->getParameter(currentCompID, name, val, maxSize);
	}
	/*!
	Read and return value of parameter by name

	@param name name of the parameter
	@return  value of the paramer


	*/
	std::string getParameterString(const char* name) {
		return space->manager->componentMemory->getParameterString(currentCompID, name);
	}
	/*!
	Read and interpret value of parameter by name as boolean
	True: String 'Yes', Integer and Float non-zero
	False: String not 'Yes', Integer and Float zero

	@param name name of the parameter
	@return  boolean interpretation of value of the paramer


	*/
	bool getParameterAsBool(const char* name) {
		return space->manager->componentMemory->getParameterAsBool(currentCompID, name);
	}
	/*!
	Read and return value of parameter by name

	@param name name of the parameter
	@return  value of the paramer


	*/
	int64 getParameterInt(const char* name) {
		return space->manager->componentMemory->getParameterInt(currentCompID, name);
	}
	/*!
	Read and return value of parameter by name

	@param name name of the parameter
	@return  value of the paramer


	*/
	float64 getParameterFloat(const char* name) {
		return space->manager->componentMemory->getParameterFloat(currentCompID, name);
	}
	/*!
	Read integer parameter value

	@param name name of the parameter
	@param val value of the parameter
	@return  true if reading was successful


	*/
	bool getParameter(const char* name, int64& val) {
		return space->manager->componentMemory->getParameter(currentCompID, name, val);
	}
	/*!
	Read float parameter value

	@param name name of the parameter
	@param val value of the parameter
	@return true if reading was successful


	*/
	bool getParameter(const char* name, float64& val) {
		return space->manager->componentMemory->getParameter(currentCompID, name, val);
	}
	/*!
	Set string parameter value 

	@param name name of the parameter
	@param val value of the parameter
	@return true if successful


	*/
	bool setParameter(const char* name, const char* val) {
		return space->manager->componentMemory->setParameter(currentCompID, name, val);
	}
	/*!
	Set integer parameter value

	@param name name of the parameter
	@param val value of the parameter
	@return  true if successful


	*/
	bool setParameter(const char* name, int64 val) {
		return space->manager->componentMemory->setParameter(currentCompID, name, val);
	}
	/*!
	Set float parameter value 

	@param name name of the parameter
	@param val value of the parameter
	@return true if successful


	*/
	bool setParameter(const char* name, float64 val) {
		return space->manager->componentMemory->setParameter(currentCompID, name, val);
	}
	/*!
	Reset parameter to its default (or initial) value

	@param name name of the parameter
	@return true if successful


	*/
	bool resetParameter(const char* name) {
		return space->manager->componentMemory->resetParameter(currentCompID, name);
	}
	/*!
	Step up or down list or range parameter value.
	If list steps to next or previous value, if range adds or subtracts interval while staying in the min-max range. 

	@param name name of the parameter
	@param tweak number of steps to tweak, positive is upwards, negative is downwards
	@return true if successful


	*/
	bool tweakParameter(const char* name, int32 tweak) {
		return space->manager->componentMemory->tweakParameter(currentCompID, name, tweak);
	}

	// ***************** Other Modules' Parameters *****************
	// Get, set or reset other module's parameters
	// *** Not implemented yet



	// ***************** Queries *****************
	// Query a Data storage devices (Whiteboard, Stream, Catalogue) for data
	// Named queries in PsySpec may be used
	/*!
	Retrieve stored messages from a whiteboard, by named &lt;retrieve&gt; spec entry.

	The retrieve entry in the PsySpec (looked up by @a name under the *current
	trigger*, so a trigger must have arrived first) says which whiteboard to ask
	and how to filter; @a maxcount / @a maxage further narrow the result at call
	time. Under the hood this sends a CTRL_QUERY message to the whiteboard
	component and blocks up to @a timeout ms for the reply.

	@code
	std::list<DataMessage*> hits;
	uint8 status = api->retrieve(hits, "RecentUtterances", 10, 60000);
	if (status == QUERY_SUCCESS) {
		for (DataMessage* m : hits) {
			// use m ...
			delete m;                      // caller owns each retrieved message
		}
	}
	@endcode

	@param result Output list the function fills with the retrieved messages.
	@param name Name of the retrieve entry in the PsySpec.
	@param maxcount Maximum number of messages to retrieve (0 = spec/unlimited).
	@param maxage Maximum age of the messages in ms (0 = spec/unlimited).
	@param timeout Maximum time to wait for the whiteboard's reply, in ms.
	@return One of the QUERY_* status codes (QUERY_SUCCESS on success).
	@note Ownership: the messages placed in @a result are fresh copies — the
	CALLER owns them and must delete each one. Blocking call (up to @a timeout ms).
	*/
	uint8 retrieve(std::list<DataMessage*> &result, const char* name, uint32 maxcount = 0, uint32 maxage = 0, uint32 timeout = 5000);
	/*!
	Used by a module to retrieve messages from a whiteboard. The retrieve details are provided in the module spec by name.
	maxcount and maxage can be provided to add to the spec details.

	@param result a bucket parameter filled by the function. A list of messages.
	@param name name of the whiteboard defined in the PsySpec
	@param startTime only messages created at or after this time are retrieved
	@param endTime only messages created at or before this time are retrieved (0 = no limit)
	@param maxcount the maximal number of messages to be retrieved (0 = spec/unlimited)
	@param maxage the maximal age of the messages in ms (0 = spec/unlimited)
	@param timeout maximum time to wait for the whiteboard's reply, in ms
	@return One of the QUERY_* status codes. Retrieved messages in @a result are
	owned by the caller (delete each); see retrieve() for details and an example.
	*/
	uint8 retrieveTimeParam(std::list<DataMessage*> &result, const char* name, uint64 startTime, uint64 endTime = 0, uint32 maxcount = 0, uint32 maxage = 0, uint32 timeout = 5000);
	/*!
	Used by a module to retrieve messages from a whiteboard. The retrieve details are provided in the module spec by name. 
	startTime and endTime can be provided to add to the spec details.

	@param result a bucket parameter filled by the function. A list of messages.
	@param name name of the whiteboard defined in the PsySpec
	@param startString this String marks the beginning of the selection of Strings that the functions shall return
	@param endString this String marks the end of the selection of Strings that the functions shall return
	@param maxcount the maximal number of messages to be retrieved (0 = spec/unlimited)
	@param maxage the maximal age of the messages in ms (0 = spec/unlimited)
	@param timeout maximum time to wait for the whiteboard's reply, in ms
	@return One of the QUERY_* status codes. Retrieved messages in @a result are
	owned by the caller (delete each); see retrieve() for details and an example.
	*/
	uint8 retrieveStringParam(std::list<DataMessage*> &result, const char* name, const char* startString, const char* endString = NULL, uint32 maxcount = 0, uint32 maxage = 0, uint32 timeout = 5000);
	/*!
	Used by a module to retrieve messages from a whiteboard. The retrieve details are provided in the module spec by name. 
	startInteger and endInteger can be provided to add to the spec details.

	@param result a bucket parameter filled by the function. A list of messages.
	@param name name of the whiteboard defined in the PsySpec
	@param startInteger this value marks the beginning of the selection of values that the functions shall return
	@param endInteger this value marks the end of the selection of values that the functions shall return
	@param maxcount the maximal number of messages to be retrieved (0 = spec/unlimited)
	@param maxage the maximal age of the messages in ms (0 = spec/unlimited)
	@param timeout maximum time to wait for the whiteboard's reply, in ms
	@return One of the QUERY_* status codes. Retrieved messages in @a result are
	owned by the caller (delete each); see retrieve() for details and an example.
	*/
	uint8 retrieveIntegerParam(std::list<DataMessage*> &result, const char* name, int64 startInteger, int64 endInteger = INT64_NOVALUE, uint32 maxcount = 0, uint32 maxage = 0, uint32 timeout = 5000);
	/*!
	Used by a module to retrieve messages from a whiteboard. The retrieve details are provided in the module spec by name.
	maxcount and maxage can be provided to add to the spec details.

	@param result a bucket parameter filled by the function. A list of messages.
	@param name name of the whiteboard defined in the PsySpec
	@param startFloat this value marks the beginning of the selection of values that the functions shall return
	@param endFloat this value marks the end of the selection of values that the functions shall return
	@param maxcount the maximal number of messages to be retrieved (0 = spec/unlimited)
	@param maxage the maximal age of the messages in ms (0 = spec/unlimited)
	@param timeout maximum time to wait for the whiteboard's reply, in ms
	@return One of the QUERY_* status codes. Retrieved messages in @a result are
	owned by the caller (delete each); see retrieve() for details and an example.
	*/
	uint8 retrieveFloatParam(std::list<DataMessage*> &result, const char* name, float64 startFloat, float64 endFloat = FLOAT64_NOVALUE, uint32 maxcount = 0, uint32 maxage = 0, uint32 timeout = 5000);
	/*!
	Used by a module to retrieve messages from a whiteboard. The retrieve details are provided in the module spec by name. 

	@param result Output list the function fills with the retrieved messages (caller owns each; delete them).
	@param spec A fully populated RetrieveSpec describing the retrieval (advanced use; the named overloads build this from the PsySpec).
	@param timeout maximum time to wait for the whiteboard's reply, in ms
	@return One of the QUERY_* status codes; see retrieve(std::list<DataMessage*>&, const char*, uint32, uint32, uint32).
	*/
	uint8 retrieve(std::list<DataMessage*> &result, RetrieveSpec* spec, uint32 timeout = 5000);

	/*!
	Send a query to another module or catalog by named &lt;query&gt; spec entry, receiving raw bytes back.

	The spec entry @a name (looked up under the current trigger, so a trigger must
	have arrived first) defines the target component and default operation/keys;
	the call composes a CTRL_QUERY message, sends it and blocks up to @a timeout
	ms for the reply, whose "ReplyData" payload is copied into a new buffer for
	the caller. If the spec entry names a remote host, the query transparently
	becomes an inter-system query (CTRL_INTERSYSTEM_QUERY).

	@code
	char* bytes = NULL;
	uint32 size = 0;
	uint8 status = api->queryCatalog(&bytes, size, "ReadConfig", "config.xml", "read");
	if (status == QUERY_SUCCESS && bytes) {
		// use bytes[0..size-1] ...
		delete [] bytes;               // caller owns the reply buffer
	}
	@endcode

	@param result Receives a newly allocated copy of the reply data (NULL if none); the CALLER owns it and must delete [] it.
	@param resultsize Receives the size of the reply data in bytes.
	@param name name of the query defined in the PsySpec
	@param query the actual query sent to the catalog
	@param operation specify the custom operation by name (example could be 'read' or 'write' for the FileCatalog)
	@param data provide optional data to send to as part of the query (example could be the data to write to a file for the FileCatalog)
	@param datasize size of the optional data
	@param timeout maximum time to wait for the reply, in ms
	@return One of the QUERY_* status codes (QUERY_SUCCESS on success).
	@note Blocking call (up to @a timeout ms).
	*/
	uint8 queryCatalog(char** result, uint32 &resultsize, const char* name, const char* query, const char* operation = NULL, const char* data = NULL, uint32 datasize = 0, uint32 timeout = 5000);

	/*!
	Send a message-based query to another module or catalog by named spec entry.

	Like the raw-bytes overload, but the caller supplies a full DataMessage as the
	query input and receives a full DataMessage reply, which is more convenient
	when the exchange carries structured key/value data.

	@code
	DataMessage* q = new DataMessage();
	q->setString("Query", "lookup");
	DataMessage* reply = NULL;
	uint8 status = api->queryCatalog(&reply, "MyQuery", q, 3000); // q is consumed
	if (status == QUERY_SUCCESS && reply) {
		// use reply ...
		delete reply;                  // caller owns the reply
	}
	@endcode

	@param resultMsg Receives the reply message, or NULL. The CALLER owns and must delete it.
	@param name name of the query defined in the PsySpec
	@param msg the msg to be sent to the catalog as the query input
	@param timeout maximum time to wait for the reply, in ms
	@return One of the QUERY_* status codes.
	@warning Ownership: @a msg is consumed (deleted) by the call — do not delete
	or reuse it. Blocking call (up to @a timeout ms).
	*/
	uint8 queryCatalog(DataMessage** resultMsg, const char* name, DataMessage* msg, uint32 timeout = 5000);

	/*!
	Send a query to a component in a *different* Psyclone system by hostname/IP.

	The query is wrapped as CTRL_INTERSYSTEM_QUERY and routed via the local
	system's network layer to the remote system's main port; no PsySpec entry is
	needed. Useful for bridging independent Psyclone deployments.

	@param resultMsg Receives the reply message, or NULL. The CALLER owns and must delete it.
	@param componentName name of the component to be queried in the remote system
	@param ipAddress IP address or hostname of the remote system in text (resolved via DNS)
	@param port main Psyclone port of the remote system
	@param msg the msg to be sent to the catalog as the query input; consumed (deleted) by the call
	@param timeout maximum time to wait for the reply, in ms
	@return One of the QUERY_* status codes (QUERY_NOT_AVAILABLE if the name does not resolve).
	@note Blocking call (up to @a timeout ms, plus DNS resolution time).
	*/
	uint8 queryRemoteCatalog(DataMessage** resultMsg, const char* componentName, const char* ipAddress, uint16 port, DataMessage* msg, uint32 timeout = 5000);

	/*!
	Send a query to a component in a different Psyclone system by numeric IP.
	See the hostname overload for semantics; this variant skips DNS resolution.

	@param resultMsg Receives the reply message, or NULL. The CALLER owns and must delete it.
	@param componentName name of the component to be queried in the remote system
	@param ipAddress IP address of the remote system in UINT32 format
	@param port main Psyclone port of the remote system
	@param msg the msg to be sent to the catalog as the query input; consumed (deleted) by the call
	@param timeout maximum time to wait for the reply, in ms
	@return One of the QUERY_* status codes. Blocking call.
	*/
	uint8 queryRemoteCatalog(DataMessage** resultMsg, const char* componentName, uint32 ipAddress, uint16 port, DataMessage* msg, uint32 timeout = 5000);

	/*!
	Send a query to a multi-homed remote Psyclone system, trying a list of addresses.

	Failover variant: if @a chosenAddress is >0 that (1-based) address is tried
	first; on QUERY_NOT_REACHABLE the remaining addresses are tried in order.
	On success @a chosenAddress is updated to the index of the address that
	worked, so a caller can remember it and skip the scan next time.

	@param resultMsg Receives the reply message, or NULL. The CALLER owns and must delete it.
	@param chosenAddress In: 1-based index of the address to try first (0 = scan all). Out: index of the first working address.
	@param componentName name of the component to be queried in the remote system
	@param ipAddresses list of IP addresses of the remote system in UINT32 format
	@param numAddresses number of addresses presented
	@param port main Psyclone port of the remote system
	@param msg the msg to be sent to the catalog as the query input; consumed by the call
	@param timeout maximum time to wait per attempt, in ms
	@return One of the QUERY_* status codes; QUERY_NOT_REACHABLE if no address connected.
	@warning Worst-case blocking time is roughly @a timeout × @a numAddresses when
	all addresses are unreachable.
	*/
	uint8 queryRemoteCatalog(DataMessage** resultMsg, uint32& chosenAddress, const char* componentName, uint32* ipAddresses, uint32 numAddresses, uint16 port, DataMessage* msg, uint32 timeout = 5000);

	/*!
	Used by a module to reply to an incoming query by id. Status can be 

	\#define QUERY_FAILED 1 

	\#define QUERY_TIMEOUT	2 

	\#define QUERY_NAME_UNKNOWN 3 

	\#define QUERY_COMPONENT_UNKNOWN 4 

	\#define QUERY_QUERYFAILED 5 

	\#define QUERY_SUCCESS 6 

	\#define QUERY_NOT_AVAILABLE 7 

	Binary reply data of size size can be added and a count can indicate the number of entries in the data chunk.


	@param id id of the incoming query  to reply to
	@param status see description
	@param data optional binary data to reply with (copied into the reply; caller keeps ownership of the buffer)
	@param size size of optional binary data
	@param count optional resulting count for operation, if appropriate
	@return  true if reply was successful


	*/
	bool queryReply(uint32 id, uint8 status, char* data, uint32 size, uint32 count);
	/*!
	Used by a module to send a message as a reply to an incoming query.

	@param id id of the incoming query to reply to
	@param status see description of queryReply(uint32 id, uint8 status, char* data, uint32 size, uint32 count);
	@param msg reply payload; ownership transfers to the system (do not delete or reuse). May be NULL for a status-only reply.
	@return  true if reply was successful
	*/
	bool queryReply(uint32 id, uint8 status, DataMessage* msg = NULL);

	// ***************** Performance *****************
	// For querying about performance
	// *** Not implemented yet



	// ***************** Monitoring *****************
	// For providing custom data to monitoring tools
	// *** Not implemented yet



	// ***************** Supervisor *****************
	// API for supervisor modules
	// *** Not implemented yet


	/*! Write a printf-style entry to the component's log (and the console when @a level is below the logging threshold).

		The entry is automatically prefixed with the component's name and routed
		through the space's logging framework, so it shows up attributed to this
		component in PsyProbe and the system log.
		@param level Log severity/verbosity level; lower is more important (0 = error, higher = increasingly verbose).
		@param formatstring printf-style format followed by matching variadic arguments.
		@return true if the entry was logged.
	*/
	bool logPrint(int level, const char *formatstring, ... );

private:
	PsySpace* space;
	utils::Mutex apiMutex;
	uint64 startedRunning;
	bool shouldContinueRunning;
	uint32 currentCrankID;
	uint32 currentCompID;
	uint32 msgReceivedCount;
	uint32 msgInputCount;
	uint32 msgSentCount;
	uint32 msgPostedCount;
	uint64 lastCPUTicks;
	uint64 lastWallTime;
	uint32 chainCPUTicks;
	uint32 chainWallTime;
	uint32 chainCount;

	std::queue<TriggerAndMessage> inputQueue;
	utils::Semaphore inputQueueSemaphore;

	bool autoDelete;
	DataMessage* currentTriggerMsg;
	DataMessage* currentMsg;
	std::map<std::string, DataMessage*> currentGroupMap;	// T1.6: member messages of the current joined set (owned; freed on next wait)
	DataMessage* currentSignalMsg;
	TriggerSpec* currentTriggerSpec;
	char currentTriggerName[MAXKEYNAMELEN+1];
	char commandlineBasedir[MAXCOMMANDLINELEN + 1];

	// Only called by PsySpace
	/*! Internal use only

	*/
	uint64 checkLastWaitForMessage();
	/*! Internal use only

	*/
	uint8 addInputTrigger(uint32 crankID, uint32 compID, DataMessage* trigger, DataMessage* msg);
	/*! Internal use only

	*/
	bool begin();
	/*! Internal use only

	*/
	bool finish();
	/*! Internal use only

	*/
	bool stop();
};

} // namespace cmlabs

#endif //_PSYAPI_H_

