

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  This file is part of PSYCLONE 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/
//_/_/
//_/_/  PSYCLONE 2.1 is released under the GNU Lesser General Public License 
//_/_/  (LGPL) version 3.0, and is subject to the following
//_/_/  
//_/_/  - 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. 
//_/_/    
//_/_/  A commercial license for PSYCLONE 2.1 is also available to 
//_/_/  any interested party from the copyright holder.
//_/_/  
//_/_/  You can redistribute this copy of PSYCLONE 2.1 and/or modify it under 
//_/_/  the terms of the GNU General Public License as published by the Free 
//_/_/  Software Foundation, either version 3 of the License, or (at your 
//_/_/  option) any later version.
//_/_/  
//_/_/  You should have received a copy of the GNU General Public License
//_/_/  and the GNU Lesser General Public License along with PSYCLONE 2.1.  
//_/_/  If not, see <http://www.gnu.org/licenses/>.
//_/_/  
//_/_/  PSYCLONE 2.1 is distributed in the hope that it will be useful,
//_/_/  but WITHOUT ANY WARRANTY; without even the implied warranty of
//_/_/  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//_/_/  GNU General Public License for more details.
//_/_/
//_/_/  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.
//_/_/
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
#include "PsyProbe.h"

namespace	cmlabs{


PsyProbe::PsyProbe() {
	status = PSYPROBE_SYSTEM_IDLE;
	node = NULL;
	httpCalls["/"] = &MainPage;
	httpCalls["index.html"] = &MainPage;
	httpCalls["simplepage"] = &SimplePage;
	httpCalls["shutdown"] = &ShutdownPage;
	httpCalls["subs"] = &SubscriptionPage;
	httpCalls["ids"] = &IDPage;
	httpCalls["loadelement"] = &LoadElement;

	httpCalls["xml"] = &XMLMain;
	httpCalls["main.xml"] = &XMLMain;
	httpCalls["system.xml"] = &XMLSystem;
	httpCalls["time.xml"] = &XMLSystem;
	httpCalls["performancematrix.xml"] = &XMLPerformanceMatrix;

	apiCalls["triggeractivate"] = &API_TriggerActivate;
	apiCalls["postactivate"] = &API_PostActivate;
	apiCalls["getcomponentdata"] = &API_GetComponentData;
	apiCalls["getcomponentkeys"] = &API_GetComponentKeys;
	apiCalls["getmessage"] = &API_GetMessage;
	apiCalls["retrievemessages"] = &API_RetrieveMessages;
	apiCalls["query"] = &API_PerformQuery;
	apiCalls["post"] = &API_PostMessage;
	apiCalls["getlog"] = &API_GetLog;

	templates["SimplePage"] = "simplepage_template.html";
	templates["MainPage"] = "main.html";
	templates["LoadElement"] = "loadelement.html";
}

PsyProbe::~PsyProbe() {
	stop();
	std::map<std::string, PostSpec*>::iterator i = posts.begin(), e = posts.end();
	while (i != e) { delete(i->second); i++; }
	posts.clear();
	node = NULL;
}

bool PsyProbe::init(Node* parent) {
	if (!parent)
		return false;
	node = parent;

	if (!ThreadManager::CreateThread(PsyProbeRun, this, threadID)) {
		LogPrint(0, 0, 0, "Could not start PsyProbe thread...");
		return false;
	}
	status = PSYPROBE_SYSTEM_INIT;
	return true;
}

bool PsyProbe::setRootDir(const char* root) {
	rootdir = root;
	if (!utils::TextEndsWith(root, "/") && !utils::TextEndsWith(root, "\\"))
		rootdir += "/";
	return true;
}

bool PsyProbe::addPost(const char* name, PostSpec* postSpec) {
	if (!name || !postSpec)
		return false;

	std::map<std::string, PostSpec*>::iterator i = posts.find(name);
	if (i != posts.end())
		delete(i->second);
	posts[name] = postSpec;
	return true;
}

bool PsyProbe::addAlias(const char* name, const char* loc, const char* defaultFile){
	if (!name || !strlen(name) || !loc || !strlen(loc))
		return false;
	WebAlias* alias = &(aliases[name]);
	utils::strcpyavail(alias->name, name, MAXKEYNAMELEN, true);
	snprintf(alias->suburl, MAXFILENAMELEN, "%s/", name);
	if (utils::TextEndsWith(loc, "/"))
		utils::strcpyavail(alias->location, loc, MAXFILENAMELEN, true);
	else
		snprintf(alias->location, MAXFILENAMELEN, "%s/", loc);
	if (defaultFile && strlen(defaultFile))
		utils::strcpyavail(alias->defaultFile, defaultFile, MAXKEYNAMELEN, true);
	else
		alias->defaultFile[0] = 0;
	return true;
}

WebAlias* PsyProbe::checkForAlias(const char* reqStr) {
	std::map<std::string, WebAlias>::iterator i = aliases.begin(), e = aliases.end();
	while (i != e) {
		if (utils::TextStartsWith(reqStr, i->second.suburl))
			return &(i->second);
		else if (stricmp(reqStr, i->first.c_str()) == 0)
			return &(i->second);
		i++;
	}
	return NULL;
}

bool PsyProbe::setSystemReady() {
	status = PSYPROBE_SYSTEM_READY;
	return true;
}

bool PsyProbe::run() {

	isRunning = true;

	uint32 tid;
	utils::GetCurrentThreadOSID(tid);
	while (shouldContinue) {
		utils::Sleep(50);
	}

	isRunning = false;
	return true;
}


std::string PsyProbe::getTemplate(std::string name) {
	std::string filename = templates[name];
	if (!filename.size())
		return "";
	return utils::ReadAFileString(rootdir + filename);
}

THREAD_RET THREAD_FUNCTION_CALL PsyProbeRun(THREAD_ARG arg) {
	if (arg == NULL) thread_ret_val(1);
	thread_ret_val((int)(((PsyProbe*)arg)->run() ? 0 : 1));
}


bool PsyProbe::receiveHTTPRequest(HTTPRequest* req, NetworkChannel* channel, uint64 conid) {
	// printf("--- PsyProbe got new request (%s)...\n", req->getRequest());
	HTTPReply* reply = NULL;
	const char* reqStr = req->getHeaderEntry("http_request");

	if (status != PSYPROBE_SYSTEM_READY) {
		reply = new HTTPReply();
		if ((strstr(reqStr, "api/") == reqStr) || strstr(reqStr, ".xml"))
			reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", "<error code=\"-1\" text=\"PsyProbe starting up, please wait...\" />", 0,
				"Refresh: 3");
		else
			reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/html", "PsyProbe starting up, please wait...", 0,
				"Refresh: 3");
		channel->sendHTTPReply(reply, conid);
		delete(reply);
		delete(req);
		return true;
	}

	if (!rootdir.size()) {
		// try to auto detect root dir location
		std::string testfile = "simplepage_template.html";
		if (utils::GetFileDetails(utils::StringFormat("html/%s", testfile.c_str()).c_str()).doesExist)
			rootdir = "html/";
		else if (utils::GetFileDetails(utils::StringFormat("../html/%s", testfile.c_str()).c_str()).doesExist)
			rootdir = "../html/";
		else if (utils::GetFileDetails(utils::StringFormat("../../html/%s", testfile.c_str()).c_str()).doesExist)
			rootdir = "../../html/";
		else {
			reply = new HTTPReply();
			reply->createPage(HTTP_INTERNAL_ERROR, GetTimeNow(), "Psyclone", 0, true, false, "text/html", "Could not access HTML Basedir");
			channel->sendHTTPReply(reply, conid);
			delete(reply);
			delete(req);
			return true;
		}
	}

	HTTPFunc func = NULL;
	if (!strlen(reqStr))
		func = httpCalls["/"];
	else if (strstr(reqStr, "api/") == reqStr) {
		callAPI(reqStr+4, req, channel, conid);
		delete(req);
		return true;
	}
	else if (httpCalls.find(reqStr) != httpCalls.end())
		func = httpCalls[reqStr];
	if (func)
		return func(this, req, channel, conid);

	std::string html;
	std::string error;
	char* data = NULL;
	uint32 datasize = 0;
	char* exttype = new char[1024];
	utils::strcpyavail(exttype, "text/html", 1024, true);

	// LogPrint(0, 0, 0, "*** Requesting file '%s'...", reqStr);
	
	WebAlias* alias = checkForAlias(reqStr);
	if (alias) {
		std::string basedir = alias->location;
		if ((stricmp(reqStr, alias->name) == 0) || (stricmp(reqStr, alias->suburl) == 0)) {
			if (strlen(alias->defaultFile)) {
				if (!(html = utils::ReadAFileString(basedir + alias->defaultFile)).size())
					error = utils::StringFormat("Could read file default file '%s' in '%s'...", alias->defaultFile, basedir.c_str());
			}
			else {
				if (!(html = utils::ReadAFileString(basedir + "index.html")).size())
					error = utils::StringFormat("Could read file default 'index.html' in '%s'...", basedir.c_str());
			}
		}
		else {
			uint32 len = (uint32)strlen(alias->suburl);
			if (utils::TextEndsWith(reqStr, ".html", false) || utils::TextEndsWith(reqStr, ".txt", false)) {
				if (!(html = utils::ReadAFileString(basedir + (reqStr+len))).size())
					error = utils::StringFormat("Could read file '%s' in '%s'...", (reqStr + len), basedir.c_str());
			}
			else {
				if (!(data = utils::ReadAFile((basedir + (reqStr + len)).c_str(), datasize, true)))
					error = utils::StringFormat("Could read file '%s' in '%s'...", (reqStr + len), basedir.c_str());
				else {
					if (!html::GetMimeType(exttype, (reqStr + len), 1024))
						error = utils::StringFormat("Unsupported mime type for file '%s'...", (reqStr + len));
				}
			}
		}
	}
	else {
		if (utils::TextEndsWith(reqStr, ".html", false) || utils::TextEndsWith(reqStr, ".txt", false)) {
			if (!(html = utils::ReadAFileString(rootdir + reqStr)).size())
				error = utils::StringFormat("Could read file '%s' in root dir '%s'...", reqStr, rootdir.c_str());
		}
		else {
			if (!(data = utils::ReadAFile((rootdir + reqStr).c_str(), datasize, true)))
				error = utils::StringFormat("Could read file '%s' in root dir '%s'...", reqStr, rootdir.c_str());
			else {
				if (!html::GetMimeType(exttype, reqStr, 1024))
					error = utils::StringFormat("Unsupported mime type for file '%s'...", reqStr);
			}
		}
	}

	reply = new HTTPReply();
	if (error.size())
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, exttype, error.c_str());
	else if (data)
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, exttype, data, datasize);
	else
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, exttype, html.c_str());

	// printf("--- PsyProbe sending reply (%u)...\n", reply->contentLength);

	channel->sendHTTPReply(reply, conid);
	delete [] data;
	delete [] exttype;
	delete(reply);
	delete(req);
	return true;
}

bool PsyProbe::replyError(const char* text, HTTPRequest* req, NetworkChannel* channel, uint64 conid) {
	HTTPReply* reply = new HTTPReply();
	reply->createPage(HTTP_INTERNAL_ERROR, GetTimeNow(), "Psyclone", 0, true, false, "text/html", text);
	channel->sendHTTPReply(reply, conid);
	delete(reply);
	delete(req);
	return true;
}







bool PsyProbe::addCustomView(uint32 compID, const char* name, const char* templateURL) {
	if (!compID || !name || !strlen(name))
		return false;
	if (!templateURL || !strlen(templateURL)) {
		customViews.erase(compID);
		return true;
	}
	else {
		CustomView cv;
		cv.compID = compID;
		utils::strcpyavail(cv.name, name, MAXKEYNAMELEN, true);
		utils::strcpyavail(cv.templateURL, templateURL, MAXKEYNAMELEN, true);
		customViews[compID] = cv;
		return true;
	}
}









bool PsyProbe::ShutdownPage(PsyProbe* probe, HTTPRequest* req, NetworkChannel* channel, uint64 conid) {
	probe->node->shutdown(3000);
	HTTPReply* reply = new HTTPReply();
	reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/html", "<html><body>Shutdown in progres...</body></html>");
	channel->sendHTTPReply(reply, conid);
	delete(reply);
	delete(req);
	return true;
}

bool PsyProbe::MainPage(PsyProbe* probe, HTTPRequest* req, NetworkChannel* channel, uint64 conid) {
	if (!probe->node->nodeID)
		return probe->replyError("Psyclone network not yet ready...", req, channel, conid);

	std::string html = probe->getTemplate("MainPage");
	if (!html.size())
		return probe->replyError("Could not load SimplePage template", req, channel, conid);

	HTTPReply* reply = new HTTPReply();
	reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/html", html.c_str());
	channel->sendHTTPReply(reply, conid);
	delete(reply);
	delete(req);
	return true;
}

bool PsyProbe::SimplePage(PsyProbe* probe, HTTPRequest* req, NetworkChannel* channel, uint64 conid) {
	if (!probe->node->nodeID)
		return probe->replyError("Psyclone network not yet ready...", req, channel, conid);

	std::string html = probe->getTemplate("SimplePage");
	if (!html.size())
		return probe->replyError("Could not load SimplePage template", req, channel, conid);

	std::list< std::map<std::string, std::string> > hierarchyList;
	std::map<std::string, std::string> hierarchyMap;

	std::list< std::map<std::string, std::string> > nodeList;
	std::map<std::string, std::string> nodeMap;

	std::list< std::map<std::string, std::string> > spaceList;
	std::map<std::string, std::string> spaceMap;

	std::list< std::map<std::string, std::string> > catList;
	std::map<std::string, std::string> catMap;

	std::list< std::map<std::string, std::string> > modList;
	std::map<std::string, std::string> modMap;

	NodeInfoPack* infopack;
	std::map<uint16, NodeInfoPack*> infopacks;
	if (!probe->node->getAllInfoPacks(infopacks))
		return probe->replyError("Could not get information about Node network...", req, channel, conid);

	uint32 n;
	const DataMessageHeader* msgHeader;
	const ProcessInfoStruct* procInfo;
	std::map<uint16, std::pair<std::map<uint32, ComponentInfoStruct*>, std::map<uint32, ComponentInfoStruct*> > >::iterator iSpace, eSpace;
	std::map<uint32, ComponentInfoStruct*>::iterator iCat, eCat, iMod, eMod;
	std::map<uint16, std::pair<std::map<uint32, ComponentInfoStruct*>, std::map<uint32, ComponentInfoStruct*> > > hierarchy;
	std::map<uint16, NodeInfoPack*>::iterator iNode = infopacks.begin(), eNode = infopacks.end();
	while (iNode != eNode) {
		if (!(infopack = iNode->second))
			continue;
		hierarchyMap["%nodename%"] = infopack->data->name;
		hierarchyMap["%nodeid%"] = utils::StringFormat("%u", infopack->data->nodeID);

		nodeMap["%nodename%"] = iNode->second->data->name;
		nodeMap["%nodeid%"] = utils::StringFormat("%u", iNode->second->data->nodeID);
		nodeMap["%nodeaddress%"] = iNode->second->data->addr;
		nodeMap["%nodeport%"] = utils::StringFormat("%u", iNode->second->data->port);
		nodeList.push_back(nodeMap);

		if (!(hierarchy = infopack->getHierarcy()).size())
			continue;
		iSpace = hierarchy.begin();
		eSpace = hierarchy.end();

		while (iSpace != eSpace) {

			if (!(procInfo = infopack->getProcInfo(iSpace->first))) {
				iSpace++;
				continue;
			}

			hierarchyMap["%spacename%"] = procInfo->name;
			hierarchyMap["%spaceid%"] = utils::StringFormat("%u", procInfo->id);

			spaceMap["%nodename%"] = infopack->data->name;
			spaceMap["%spacename%"] = procInfo->name;
			spaceMap["%spaceid%"] = utils::StringFormat("%u", procInfo->id);
			spaceMap["%spacecommandline%"] = procInfo->commandline;
			spaceMap["%spacecreated%"] = PrintTimeString(procInfo->createdTime);
			spaceMap["%spacelastseen%"] = PrintTimeString(procInfo->lastseen);
			spaceMap["%spacelastseendif%"] = PrintTimeDifString(GetTimeAge(procInfo->lastseen));
			spaceMap["%spaceosid%"] = utils::StringFormat("%u", procInfo->osID);
			spaceMap["%spacememuse%"] = utils::BytifySize((double)procInfo->stats.procMemUsage);
			spaceMap["%spacestatmsgin%"] = utils::StringFormat("%llu", procInfo->stats.msgInCount);
			spaceMap["%spacestatmsginbytes%"] = utils::BytifySize((double)procInfo->stats.msgInBytes);
			spaceMap["%spacestatmsgout%"] = utils::StringFormat("%llu", procInfo->stats.msgOutCount);
			spaceMap["%spacestatmsgoutbytes%"] = utils::BytifySize((double)procInfo->stats.msgOutBytes);
			for (n=0; n<10; n++) {
				msgHeader = (DataMessageHeader*)(procInfo->stats.recentInMsg + (n * DRAFTMSGSIZE));
				if (msgHeader->cid == DATAMESSAGEID) {
					spaceMap[utils::StringFormat("%%spacelastreceived%u%%", n).c_str()] =
						utils::StringFormat("%s: %s", PrintTimeString(msgHeader->time).c_str(), probe->node->manager->typeToText(msgHeader->type).c_str());
				}
				else {
					spaceMap[utils::StringFormat("%%spacelastreceived%u%%", n).c_str()] = "";
				}
				msgHeader = (DataMessageHeader*)(procInfo->stats.recentOutMsg + (n * DRAFTMSGSIZE));
				if (msgHeader->cid == DATAMESSAGEID) {
					spaceMap[utils::StringFormat("%%spacelastsent%u%%", n).c_str()] =
						utils::StringFormat("%s: %s", PrintTimeString(msgHeader->time).c_str(), probe->node->manager->typeToText(msgHeader->type).c_str());
				}
				else {
					spaceMap[utils::StringFormat("%%spacelastsent%u%%", n).c_str()] = "";
				}
			}
			switch(procInfo->status) {
			case PSYPROC_ERROR:
				spaceMap["%spacestatus%"] = "Error";
				break;
			case PSYPROC_INIT:
				spaceMap["%spacestatus%"] = "Initialising";
				break;
			case PSYPROC_IDLE:
				spaceMap["%spacestatus%"] = "Idle";
				break;
			case PSYPROC_READY:
				spaceMap["%spacestatus%"] = "Ready";
				break;
			case PSYPROC_ACTIVE:
				spaceMap["%spacestatus%"] = "Running";
				break;
			default:
				spaceMap["%spacestatus%"] = "Unknown";
				break;
			}
			spaceList.push_back(spaceMap);


			iCat = iSpace->second.first.begin();
			eCat = iSpace->second.first.end();
			iMod = iSpace->second.second.begin();
			eMod = iSpace->second.second.end();

			while ( (iCat != eCat) || (iMod != eMod)) {
				if (iCat != eCat) {
					hierarchyMap["%catalogname%"] = iCat->second->name;
					hierarchyMap["%catalogid%"] = utils::StringFormat("%u", iCat->second->id);

					catMap["%nodename%"] = infopack->data->name;
					catMap["%spacename%"] = procInfo->name;
					catMap["%compname%"] = iCat->second->name;
					catMap["%compid%"] = utils::StringFormat("%u", iCat->second->id);
					catMap["%compcreated%"] = PrintTimeString(iCat->second->createdTime);
					catMap["%complastchanged%"] = PrintTimeString(iCat->second->lastUpdateTime);
					catMap["%compparamcount%"] = utils::StringFormat("%u", iCat->second->paramCount);
					catMap["%compprivatecount%"] = utils::StringFormat("%u", iCat->second->dataCount);
					catMap["%compstatmsgin%"] = utils::StringFormat("%llu", iCat->second->stats.msgInCount);
					catMap["%compstatmsginbytes%"] = utils::BytifySize((double)iCat->second->stats.msgInBytes);
					catMap["%compstatmsgout%"] = utils::StringFormat("%llu", iCat->second->stats.msgOutCount);
					catMap["%compstatmsgoutbytes%"] = utils::BytifySize((double)iCat->second->stats.msgOutBytes);
					for (n=0; n<10; n++) {
						msgHeader = (DataMessageHeader*)(iCat->second->stats.recentInMsg + (n * DRAFTMSGSIZE));
						if (msgHeader->cid == DATAMESSAGEID) {
							catMap[utils::StringFormat("%%complastreceived%u%%", n).c_str()] =
								utils::StringFormat("%s: %s", PrintTimeString(msgHeader->time).c_str(), probe->node->manager->typeToText(msgHeader->type).c_str());
						}
						else {
							catMap[utils::StringFormat("%%complastreceived%u%%", n).c_str()] = "";
						}
						msgHeader = (DataMessageHeader*)(iCat->second->stats.recentOutMsg + (n * DRAFTMSGSIZE));
						if (msgHeader->cid == DATAMESSAGEID) {
							catMap[utils::StringFormat("%%complastsent%u%%", n).c_str()] =
								utils::StringFormat("%s: %s", PrintTimeString(msgHeader->time).c_str(), probe->node->manager->typeToText(msgHeader->type).c_str());
						}
						else {
							catMap[utils::StringFormat("%%complastsent%u%%", n).c_str()] = "";
						}
					}

					catList.push_back(catMap);
					iCat++;
				}
				else {
					hierarchyMap["%catalogname%"] = "";
					hierarchyMap["%catalogid%"] = "";
				}

				if (iMod != eMod) {
					hierarchyMap["%modulename%"] = iMod->second->name;
					hierarchyMap["%moduleid%"] = utils::StringFormat("%u", iMod->second->id);

					modMap["%nodename%"] = infopack->data->name;
					modMap["%spacename%"] = procInfo->name;
					modMap["%compname%"] = iMod->second->name;
					modMap["%compid%"] = utils::StringFormat("%u", iMod->second->id);
					modMap["%compcreated%"] = PrintTimeString(iMod->second->createdTime);
					modMap["%complastchanged%"] = PrintTimeString(iMod->second->lastUpdateTime);
					modMap["%compparamcount%"] = utils::StringFormat("%u", iMod->second->paramCount);
					modMap["%compprivatecount%"] = utils::StringFormat("%u", iMod->second->dataCount);
					modMap["%compstatmsgin%"] = utils::StringFormat("%llu", iMod->second->stats.msgInCount);
					modMap["%compstatmsginbytes%"] = utils::BytifySize((double)iMod->second->stats.msgInBytes);
					modMap["%compstatmsgout%"] = utils::StringFormat("%llu", iMod->second->stats.msgOutCount);
					modMap["%compstatmsgoutbytes%"] = utils::BytifySize((double)iMod->second->stats.msgOutBytes);
					for (n=0; n<10; n++) {
						msgHeader = (DataMessageHeader*)(iMod->second->stats.recentInMsg + (n * DRAFTMSGSIZE));
						if (msgHeader->cid == DATAMESSAGEID) {
							modMap[utils::StringFormat("%%complastreceived%u%%", n).c_str()] =
								utils::StringFormat("%s: %s", PrintTimeString(msgHeader->time).c_str(), probe->node->manager->typeToText(msgHeader->type).c_str());
						}
						else {
							modMap[utils::StringFormat("%%complastreceived%u%%", n).c_str()] = "";
						}
						msgHeader = (DataMessageHeader*)(iMod->second->stats.recentOutMsg + (n * DRAFTMSGSIZE));
						if (msgHeader->cid == DATAMESSAGEID) {
							modMap[utils::StringFormat("%%complastsent%u%%", n).c_str()] =
								utils::StringFormat("%s: %s", PrintTimeString(msgHeader->time).c_str(), probe->node->manager->typeToText(msgHeader->type).c_str());
						}
						else {
							modMap[utils::StringFormat("%%complastsent%u%%", n).c_str()] = "";
						}
					}

					modList.push_back(modMap);
					iMod++;
				}
				else {
					hierarchyMap["%modulename%"] = "";
					hierarchyMap["%moduleid%"] = "";
				}

				if ( (iCat != eCat) || (iMod != eMod) ) {
					hierarchyList.push_back(hierarchyMap);
					hierarchyMap["%nodename%"] = "";
					hierarchyMap["%nodeid%"] = "";
					hierarchyMap["%spacename%"] = "";
					hierarchyMap["%spaceid%"] = "";
				}
			}

			iSpace++;
			if (iSpace != eSpace) {
				hierarchyList.push_back(hierarchyMap);
				hierarchyMap["%nodename%"] = "";
				hierarchyMap["%nodeid%"] = "";
			}
		}
		delete(infopack);
		iNode++;
		hierarchyList.push_back(hierarchyMap);
	}
	utils::StringScriptReplace(html, "list", hierarchyList);
	utils::StringScriptReplace(html, "nodes", nodeList);
	utils::StringScriptReplace(html, "spaces", spaceList);
	utils::StringScriptReplace(html, "catalogs", catList);
	utils::StringScriptReplace(html, "modules", modList);

	utils::StringSingleReplace(html, "%timenow%", PrintTimeNowString(), true);
	utils::StringSingleReplace(html, "%softwareversion%", __SOFTWARE_VERSION_STRING__, true);
	utils::StringSingleReplace(html, "%compiletimedate%", utils::StringFormat("%s %s", __TIME__, __DATE__).c_str(), true);
	utils::StringSingleReplace(html, "%generatetime%", PrintTimeDifString(GetTimeAge(req->time)).c_str(), true);
	
	infopacks.clear();

	HTTPReply* reply = new HTTPReply();
	reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/html", html.c_str());
	channel->sendHTTPReply(reply, conid);
	delete(reply);
	delete(req);
	return true;
}

bool PsyProbe::SubscriptionPage(PsyProbe* probe, HTTPRequest* req, NetworkChannel* channel, uint64 conid) {
	HTTPReply* reply = new HTTPReply();
	std::string html = "<html><body>";
	html += probe->node->subscriptionEngine->printFriendlyHTML();
	html += "</body></html>\n";
	reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/html", html.c_str());
	channel->sendHTTPReply(reply, conid);
	delete(reply);
	delete(req);
	return true;
}

bool PsyProbe::IDPage(PsyProbe* probe, HTTPRequest* req, NetworkChannel* channel, uint64 conid) {
	HTTPReply* reply = new HTTPReply();
	std::string html = "<html><body>";
	html += probe->node->manager->dataMapsMemory->printFriendlyHTML();
	html += "</body></html>\n";
	reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/html", html.c_str());
	channel->sendHTTPReply(reply, conid);
	delete(reply);
	delete(req);
	return true;
}

bool PsyProbe::LoadElement(PsyProbe* probe, HTTPRequest* req, NetworkChannel* channel, uint64 conid) {
	if (!probe->node->nodeID)
		return probe->replyError("Psyclone network not yet ready...", req, channel, conid);

	const char* url = req->getParameter("url");
	if (!url || !strlen(url))
		return probe->replyError("No url parameter provided", req, channel, conid);

	uint32 nodeID = utils::Ascii2Uint32(req->getParameter("nodeid"));
	uint32 compID = utils::Ascii2Uint32(req->getParameter("compid"));
	const char* nodeName = req->getParameter("node");
	const char* compName = req->getParameter("comp");

	std::string html = probe->getTemplate("LoadElement");
	if (!html.size())
		return probe->replyError("Could not load LoadElement template", req, channel, conid);

	std::string name;
	std::string type;

	if (nodeID) {
		name = probe->node->networking->getNodeName(nodeID);
		type = "Node";
	}
	else if (nodeName && strlen(nodeName)) {
		name = nodeName;
		type = "Node";
	}
	else if (compID) {
		name = probe->node->manager->getComponentName(compID);
		type = "Component";
	}
	else if (compName && strlen(compName)) {
		name = compName;
		type = "Component";
	}
	else {
		name = "System";
		type = "System";
	}

	utils::StringSingleReplace(html, "TemplateURLPlaceholder", url, false);
	utils::StringSingleReplace(html, "TemplateCompNamePlaceholder", name.c_str(), false);
	utils::StringSingleReplace(html, "TemplateCompTypePlaceholder", type.c_str(), false);

	HTTPReply* reply = new HTTPReply();
	reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/html", html.c_str());
	channel->sendHTTPReply(reply, conid);
	delete(reply);
	delete(req);
	return true;
}








bool PsyProbe::XMLMain(PsyProbe* probe, HTTPRequest* req, NetworkChannel* channel, uint64 conid) {
	HTTPReply* reply = new HTTPReply();
	std::string xml = probe->getMainXML(); // 
	if (!xml.length())
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", "<error code=\"-1\" text=\"Couldn't generate XML\" />");
	else
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", xml.c_str());
	channel->sendHTTPReply(reply, conid);
	delete(reply);
	delete(req);
	return true;
}

bool PsyProbe::XMLSystem(PsyProbe* probe, HTTPRequest* req, NetworkChannel* channel, uint64 conid) {
	HTTPReply* reply = new HTTPReply();
	std::string xml = probe->getSystemXML(); // 
	if (!xml.length())
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", "<error code=\"-1\" text=\"Couldn't generate XML\" />");
	else
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", xml.c_str());
	channel->sendHTTPReply(reply, conid);
	delete(reply);
	delete(req);
	return true;
}

bool PsyProbe::XMLSystemTime(PsyProbe* probe, HTTPRequest* req, NetworkChannel* channel, uint64 conid) {
	HTTPReply* reply = new HTTPReply();
	std::string xml = probe->getSystemTimeXML(); // 
	if (!xml.length())
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", "<error code=\"-1\" text=\"Couldn't generate XML\" />");
	else
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", xml.c_str());
	channel->sendHTTPReply(reply, conid);
	delete(reply);
	delete(req);
	return true;
}

bool PsyProbe::XMLPerformanceMatrix(PsyProbe* probe, HTTPRequest* req, NetworkChannel* channel, uint64 conid) {
	HTTPReply* reply = new HTTPReply();
	std::string xml = probe->getPerformanceMatrixXML(); // 
	if (!xml.length())
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", "<error code=\"-1\" text=\"Couldn't generate XML\" />");
	else
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", xml.c_str());
	channel->sendHTTPReply(reply, conid);
	delete(reply);
	delete(req);
	return true;
}

bool PsyProbe::XMLSubscriptions(PsyProbe* probe, HTTPRequest* req, NetworkChannel* channel, uint64 conid) {
	HTTPReply* reply = new HTTPReply();
	std::string xml = probe->node->subscriptionEngine->toXML();
	if (!xml.length())
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", "<error code=\"-1\" text=\"Couldn't generate XML\" />");
	else
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", xml.c_str());
	channel->sendHTTPReply(reply, conid);
	delete(reply);
	delete(req);
	return true;
}

bool PsyProbe::XMLIDs(PsyProbe* probe, HTTPRequest* req, NetworkChannel* channel, uint64 conid) {
	HTTPReply* reply = new HTTPReply();
	std::string xml = probe->node->manager->dataMapsMemory->toXML();
	if (!xml.length())
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", "<error code=\"-1\" text=\"Couldn't generate XML\" />");
	else
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", xml.c_str());
	channel->sendHTTPReply(reply, conid);
	delete(reply);
	delete(req);
	return true;
}

bool PsyProbe::XMLComponents(PsyProbe* probe, HTTPRequest* req, NetworkChannel* channel, uint64 conid) {
	HTTPReply* reply = new HTTPReply();
	std::string xml = probe->node->manager->componentMemory->toXML();
	if (!xml.length())
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", "<error code=\"-1\" text=\"Couldn't generate XML\" />");
	else
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", xml.c_str());
	channel->sendHTTPReply(reply, conid);
	delete(reply);
	delete(req);
	return true;
}


std::string PsyProbe::getMainXML() {
	std::string systemXML = getSystemXML();
//	std::string perfXML = node->monitoring->getPerformanceXML();

	std::string customXML;

	std::map<uint32, CustomView>::iterator i = customViews.begin(), e = customViews.end();
	while (i != e) {
		customXML += utils::StringFormat("<customview compid=\"%u\" name=\"%s\" template=\"%s\" />\n",
			i->second.compID, i->second.name, i->second.templateURL);
		i++;
	}

	std::string xml = utils::StringFormat("\
	<customviews>\n\
	%s</customviews>\n\
	<groups>\n\
		<group name=\"Default\" />\n\
		<group name=\"Indoor\" />\n\
		<group name=\"Outdoor\" />\n\
	</groups>\n\
	<systeminfo>\n\
		<text name=\"0\" text=\"System uptime: %s\" />\n\
		<text name=\"1\" text=\"More information\" />\n\
		<text name=\"2\" text=\"to follow...\" />\n\
	</systeminfo>\n", customXML.c_str(), PrintTimeDifString(GetTimeAge(node->createTime)).c_str());

	return utils::StringFormat("<psyprobe>\n%s%s</psyprobe>\n",
		xml.c_str(), systemXML.c_str());

}

std::string PsyProbe::getPerformanceMatrixXML() {
	return node->monitoring->getPerformanceMatrixXML();
}

std::string PsyProbe::getSystemXML() {
	std::map<uint16, NodeInfoPack*> infopacks;
	if (!node->getAllInfoPacks(infopacks))
		return "";

	std::string xml;

	xml += node->subscriptionEngine->toXML();
	xml += node->subscriptionEngine->activeContextsXML();
	xml += node->manager->dataMapsMemory->toXML();

	std::map<uint16, NodeInfoPack*>::iterator iNode = infopacks.begin(), eNode = infopacks.end();
	while (iNode != eNode) {
		if (iNode->second)
			xml += iNode->second->toXML();
		delete(iNode->second);
		iNode++;
	}
	infopacks.clear();

	// xml += utils::ReadAFileString(rootdir+"test.xml");

	return utils::StringFormat("<system time=\"%llu\" uptime=\"%llu\" id=\"%llu\" config=\"%s\" cmdline=\"%s\">\n%s</system>\n",
		GetTimeNow(), (uint64)GetTimeAge(node->createTime), node->createTime,
		node->configfile.c_str(), utils::GetCommandLine().c_str(), xml.c_str());
}

std::string PsyProbe::getSystemTimeXML() {
	return utils::StringFormat("<system time=\"%llu\" uptime=\"%llu\" id=\"%llu\" config=\"%s\" cmdline=\"%s\" />\n",
		GetTimeNow(), (uint64)GetTimeAge(node->createTime), node->createTime,
		node->configfile.c_str(), utils::GetCommandLine().c_str());
}


















bool PsyProbe::callAPI(const char* apiName, HTTPRequest* req, NetworkChannel* channel, uint64 conid) {
	HTTPReply* reply = new HTTPReply();
	APIFunc func;
	if ((apiCalls.find(apiName) == apiCalls.end()) || (!(func = apiCalls[apiName])) )
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", utils::StringFormat("<error code=\"-1\" text=\"API does not exist: %s\" />", apiName).c_str());
	else {
		if (!func(this, req, reply))
			reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", utils::StringFormat("<error code=\"-1\" text=\"API call failed: %s\" />", apiName).c_str());
	}
	channel->sendHTTPReply(reply, conid);
	delete(reply);
	return true;
}




bool PsyProbe::API_TriggerActivate(PsyProbe* probe, HTTPRequest* req, HTTPReply* reply) {
	uint32 compID = utils::Ascii2Uint32(req->getParameter("compid"));
	const char* name = req->getParameter("name");

	if (!compID || !name || !strlen(name)) {
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", "<error code=\"-1\" text=\"Invalid parameters\" />");
		return true;
	}

	TriggerSpec* trigger = probe->node->subscriptionEngine->findTrigger(compID, name);
	if (!trigger) {
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", "<error code=\"-1\" text=\"Couldn't find trigger\" />");
		return true;
	}

	DataMessage* msg = new DataMessage(trigger->type, compID);
	if (!probe->node->deliverTriggerMessage(trigger, 0, msg, GetTimeNow())) {
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", "<error code=\"-1\" text=\"Couldn't deliver trigger message\" />");
		delete(msg);
		return true;
	}
	delete(msg);

	reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", "<error code=\"0\" text=\"Operation success, one trigger delivered\" />");
	return true;
}

bool PsyProbe::API_PostActivate(PsyProbe* probe, HTTPRequest* req, HTTPReply* reply) {
	uint32 compID = utils::Ascii2Uint32(req->getParameter("compid"));
	const char* name = req->getParameter("name");

	if (!compID || !name || !strlen(name)) {
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", "<error code=\"-1\" text=\"Invalid parameters\" />");
		return true;
	}

	PostSpec* postSpec;
	std::list<PostSpec*>* postSpecs = probe->node->subscriptionEngine->findPosts(compID, name);
	if (!postSpecs || !postSpecs->size()) {
		delete(postSpecs);
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", "<error code=\"-1\" text=\"Couldn't find post\" />");
		return true;
	}

	uint32 count = 0;
	DataMessage* msg;
	std::list<PostSpec*>::iterator it = postSpecs->begin(), itEnd = postSpecs->end();
	while (it != itEnd) {
		if (postSpec = *it) {
			msg = new DataMessage();
			if (postSpec->contextchange.isValid()) {
				msg->setType(PsyAPI::CTRL_CONTEXT_CHANGE);
				msg->setContextChange(postSpec->contextchange);
			}
			else {
				msg->setType(postSpec->type);
			}
			postSpec->addContentToMsg(msg);
			//if (strlen(postSpec->contentKey))
			//	msg->setString(postSpec->contentKey, postSpec->content);
			msg->setMultiple(compID, postSpec->to, postSpec->tag, postSpec->ttl, postSpec->policy, GetTimeNow());
			if (probe->node->processMessage(msg)) {
				count++;
			}
			delete(msg);
		}
		it++;
	}

	if (count == 0) {
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", "<error code=\"-1\" text=\"Couldn't process post messages\" />");
		return true;
	}

	reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", utils::StringFormat("<error code=\"0\" text=\"Operation success, %u posts processed\" />", count).c_str());
	return true;
}


bool PsyProbe::API_GetComponentKeys(PsyProbe* probe, HTTPRequest* req, HTTPReply* reply) {
	uint32 compID = utils::Ascii2Uint32(req->getParameter("compid"));
	const char* format = req->getParameter("format");

	if (!compID) {
		reply->createPage(HTTP_MALFORMED_URL, GetTimeNow(), "Psyclone", 0, true, false, "text/plain", "");
		return true;
	}

	std::map<std::string, std::string> map = probe->node->getComponentPrivateDataKeysAndTypes(compID);
	//if (!map.size()) {
	//	reply->createPage(HTTP_INTERNAL_ERROR, GetTimeNow(), "Psyclone", 0, true, false, "text/plain", "");
	//	return true;
	//}

	std::string str;
	if (stricmp(format, "xml") == 0) {
		str = utils::TextJoinXML(map, "key", "data");
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", str.c_str(), (uint32)str.length());
	}
	else {
		str = utils::TextJoinJSON(map);
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "application/json", str.c_str(), (uint32)str.length());
	}
	return true;
}

bool PsyProbe::API_GetLog(PsyProbe* probe, HTTPRequest* req, HTTPReply* reply) {
	uint32 compID = utils::Ascii2Uint32(req->getParameter("compid"));
	const char* format = req->getParameter("format");
	uint32 maxCount = utils::Ascii2Uint32(req->getParameter("maxcount"));
	if (!maxCount)
		maxCount = 30;
	uint8 maxLevel = (uint8)utils::Ascii2Uint32(req->getParameter("maxlevel"));
	uint32 maxAgeMS = utils::Ascii2Uint32(req->getParameter("maxagems"));

	uint8 subject = compID ? LOG_COMPONENT : 0;

	std::list<LogEntry*> entries = probe->node->logging->getLogEntries(compID, subject, maxCount, maxLevel, maxAgeMS);

	LogEntry* entry;
	std::string str;
	if (!format || stricmp(format, "json") == 0) {
		while (entries.size()) {
			entry = entries.front();
			if (!str.length())
				str += entry->toJSON();
			else
				str += utils::StringFormat(",%s", entry->toJSON().c_str());
			entries.pop_front();
		}
		str = utils::StringFormat("{ \"log\": [\n%s]}", str.c_str());
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "application/json", str.c_str(), (uint32)str.length());
	}
	else {
		while (entries.size()) {
			entry = entries.front();
			str += entry->toXML();
			entries.pop_front();
		}
		str = utils::StringFormat("<log>\n%s</log>", str.c_str());
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", str.c_str(), (uint32)str.length());
	}
	return true;
}


bool PsyProbe::API_GetComponentData(PsyProbe* probe, HTTPRequest* req, HTTPReply* reply) {
	uint32 compID = utils::Ascii2Uint32(req->getParameter("compid"));
	const char* name = req->getParameter("name");
	const char* format = req->getParameter("format");

	if (!compID || !name || !strlen(name)) {
		reply->createPage(HTTP_MALFORMED_URL, GetTimeNow(), "Psyclone", 0, true, false, "text/plain", "");
		return true;
	}

	uint64 size = 0;
	std::string mimetype;

	char* data = probe->node->getComponentPrivateData(compID, name, size, mimetype);
	if (!data || !size) {
		reply->createPage(HTTP_INTERNAL_ERROR, GetTimeNow(), "Psyclone", 0, true, false, "text/plain", "");
		return true;
	}

	if (mimetype.length())
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, mimetype.c_str(), data, (uint32)size);
	else if (format && strlen(format)) {
		if (stricmp(format, "xml") == 0)
			reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", data, (uint32)size);
		else if (stricmp(format, "json") == 0)
			reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "application/json", data, (uint32)size);
		else if (stricmp(format, "html") == 0)
			reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/html", data, (uint32)size);
		else if (stricmp(format, "binary") == 0)
			reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "application/octet-stream", data, (uint32)size);
		else // if (stricmp(format, "text") == 0)
			reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/plain", data, (uint32)size);
	}
	else {
		mimetype = utils::StringFormat("Data of unknown format (%s)", utils::BytifySize((int32)size).c_str());
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/plain", mimetype.c_str(), (uint32)mimetype.length());
	}
	delete[] data;
	// In future: check utils::GetMimeType()
	return true;
}

bool PsyProbe::API_GetMessage(PsyProbe* probe, HTTPRequest* req, HTTPReply* reply) {
	uint64 memID = utils::Ascii2Uint64(req->getParameter("memid"));
	const char* format = req->getParameter("format");

	DataMessage* msg = probe->node->getCopyOfMessage(memID);

	if (!msg) {
		reply->createPage(HTTP_INTERNAL_ERROR, GetTimeNow(), "Psyclone", 0, true, false, "text/plain", "");
		return true;
	}

	if (stricmp(format, "xml") == 0)
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", msg->toXML().c_str());
	else if (stricmp(format, "json") == 0)
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "application/json", msg->toJSON().c_str());
	//else if (stricmp(format, "html") == 0)
	//	reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/html", data, size);
	else if (stricmp(format, "binary") == 0)
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "application/octet-stream", (char*)msg->data, msg->data->size);
	else // if (stricmp(format, "text") == 0)
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/plain", msg->toXML().c_str());
	// In future: check utils::GetMimeType()
	return true;
}


bool PsyProbe::API_PostMessage(PsyProbe* probe, HTTPRequest* req, HTTPReply* reply) {

	// URL: /api/post?name=postname&key1=val1...

	const char* name = req->getParameter("name");
	PostSpec* postSpec;
	if (!name || !strlen(name)) {
		//reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", "<error code=\"-1\" text=\"No post name provided\" />");
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "application/json", "{ \"errorcode\":-1, \"text\":\"No post name provided\" }");
		return true;
	}
	else if (!(postSpec = probe->posts[name])) {
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "application/json", utils::StringFormat("{ \"errorcode\":-1, \"text\":\"Post name '%s' not found\" }", name).c_str());
		return true;
	}

	DataMessage* msg = req->convertToMessage();
	if (postSpec->contextchange.isValid()) {
		msg->setType(PsyAPI::CTRL_CONTEXT_CHANGE);
		msg->setContextChange(postSpec->contextchange);
	}
	else {
		msg->setType(postSpec->type);
	}
	postSpec->addContentToMsg(msg);
	//if (strlen(postSpec->contentKey))
	//	msg->setString(postSpec->contentKey, postSpec->content);
	msg->setMultiple(0, postSpec->to, postSpec->tag, postSpec->ttl, postSpec->policy, GetTimeNow());
	if (!probe->node->processMessage(msg))
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "application/json", "{ \"errorcode\":-1, \"text\":\"Couldn't send post message\" }");
	else
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "application/json", utils::StringFormat("{ \"errorcode\":0, \"text\":\"Operation success, post '%s' message sent\" }", name).c_str());
	delete(msg);
	return true;
}


bool PsyProbe::API_PerformQuery(PsyProbe* probe, HTTPRequest* req, HTTPReply* reply) {

	// URL: /api/query?from=compname&query=myquery

	const char* comp = req->getParameter("from");
	const char* queryString = req->getParameter("query");
	const char* listString = req->getParameter("list");
	uint32 timeout = 5000 * 1000;

	uint32 to = probe->node->getComponentID(comp, false);
	if (!to) {
		reply->createPage(HTTP_FILE_NOT_FOUND, GetTimeNow(), "Psyclone", 0, true, false, "text/plain", "");
		return true;
	}

	DataMessage* msg = req->convertToMessage();
	msg->setType(PsyAPI::CTRL_QUERY);
	msg->setMultiple(0, to, 0, timeout, 0, GetTimeNow());
	//if (queryString)
	//	msg->setString("Query", queryString);
	//if (listString)
	//	msg->setString("List", listString);

	DataMessage* resultMsg = NULL;
	uint8 status = probe->node->query(msg, &resultMsg, timeout);
	delete(msg);

	if (!resultMsg) {
		if (status == QUERY_SUCCESS)
			reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/plain", "");
		else if (status == QUERY_TIMEOUT)
			reply->createPage(HTTP_GATEWAY_TIMEOUT, GetTimeNow(), "Psyclone", 0, true, false, "text/plain", "");
		else if ((status == QUERY_NAME_UNKNOWN) || (status == QUERY_COMPONENT_UNKNOWN))
			reply->createPage(HTTP_FILE_NOT_FOUND, GetTimeNow(), "Psyclone", 0, true, false, "text/plain", "");
		else
			reply->createPage(HTTP_ACCESS_DENIED, GetTimeNow(), "Psyclone", 0, true, false, "text/plain", "");
		return true;
	}

	const char* xml, *html, *data, *mime, *json, *text, *cache, *options, *origin;
	int64 resultCode = 0;
	resultMsg->getInt("ResultCode", resultCode);
	if (!resultCode) resultCode = HTTP_OK;
	cache = resultMsg->getString("CACHE");
	bool cacheable = (cache && (utils::stristr(cache, "yes") == cache));
	uint32 size;
	if (xml = resultMsg->getString("XML"))
		reply->createPage((uint8)resultCode, GetTimeNow(), "Psyclone", 0, true, cacheable, "text/xml", xml);
	else if (json = resultMsg->getString("JSON"))
		reply->createPage((uint8)resultCode, GetTimeNow(), "Psyclone", 0, true, cacheable, "application/json", json);
	else if (html = resultMsg->getString("HTML"))
		reply->createPage((uint8)resultCode, GetTimeNow(), "Psyclone", 0, true, cacheable, "text/html", html);
	else if (text = resultMsg->getString("TEXT"))
		reply->createPage((uint8)resultCode, GetTimeNow(), "Psyclone", 0, true, cacheable, "text/text", text);
	else if ((data = resultMsg->getData("BINARY", size)) && size && (mime = resultMsg->getString("MIMETYPE")))
		reply->createPage((uint8)resultCode, GetTimeNow(), "Psyclone", 0, true, cacheable, mime, data, size);
	else if ((options = resultMsg->getString("OPTIONS")) && (origin = resultMsg->getString("ORIGIN")))
		reply->createOptionsResponse((uint8)resultCode, GetTimeNow(), "Psyclone", 0, origin, options);
	else
		reply->createErrorPage(HTTP_SERVER_NOREPLY, GetTimeNow(), "Psyclone", true);

	delete(resultMsg);
	return true;
}

bool PsyProbe::API_RetrieveMessages(PsyProbe* probe, HTTPRequest* req, HTTPReply* reply) {

	const char* format = req->getParameter("format");
	std::list<DataMessage*> retrievedMsgs;
	std::list<DataMessage*>::iterator i, e;
	uint8 status;

	const char* str;

	// Built RetrieveSpec
	RetrieveSpec* spec = new RetrieveSpec();
	memset(spec, 0, sizeof(RetrieveSpec));
	spec->keytype = INDEX_TIME;
	spec->startInt = spec->endInt = INT64_NOVALUE;
	spec->startFloat = spec->endFloat = FLOAT64_NOVALUE;
	if (str = req->getParameter("source"))
		spec->source = probe->node->getComponentID(str);
	if (str = req->getParameter("maxage"))
		spec->maxage = utils::Ascii2Uint64(str) * 1000;
	if (str = req->getParameter("maxcount"))
		spec->maxcount = utils::Ascii2Uint32(str);
	if (str = req->getParameter("type"))
		spec->type = probe->node->getPsyType(str);
	if (str = req->getParameter("key"))
		utils::strcpyavail(spec->key, str, MAXKEYNAMELEN, true);
	else
		utils::strcpyavail(spec->key, "time", MAXKEYNAMELEN, true);
	if (str = req->getParameter("keytype")) {
		if (stricmp(str, "string") == 0)
			spec->keytype = INDEX_STRING;
		else if (stricmp(str, "integer") == 0)
			spec->keytype = INDEX_INTEGER;
		else if (stricmp(str, "float") == 0)
			spec->keytype = INDEX_FLOAT;
		else if (stricmp(str, "double") == 0)
			spec->keytype = INDEX_FLOAT;
		else if (stricmp(str, "time") == 0)
			spec->keytype = INDEX_TIME;
		else
			spec->keytype = 0;
	}
	if (str = req->getParameter("tag"))
		spec->tag = utils::Ascii2Uint32(str);
	if (str = req->getParameter("from"))
		spec->from = probe->node->getComponentID(str);
	if (str = req->getParameter("to"))
		spec->from = probe->node->getComponentID(str);
	if (str = req->getParameter("starttime"))
		spec->startTime = utils::Ascii2Uint64(str);
	if (str = req->getParameter("endtime"))
		spec->endTime = utils::Ascii2Uint64(str);
	if (str = req->getParameter("startint"))
		spec->startInt = utils::Ascii2Int64(str);
	if (str = req->getParameter("endint"))
		spec->endInt = utils::Ascii2Int64(str);
	if (str = req->getParameter("startfloat"))
		spec->startFloat = utils::Ascii2Float64(str);
	if (str = req->getParameter("endfloat"))
		spec->endFloat = utils::Ascii2Float64(str);
	if (str = req->getParameter("startstring"))
		utils::strcpyavail(spec->startString, str, MAXKEYNAMELEN, true);
	if (str = req->getParameter("endstring"))
		utils::strcpyavail(spec->endString, str, MAXKEYNAMELEN, true);

	status = probe->node->retrieve(retrievedMsgs, spec, 5000);

	if (status != QUERY_SUCCESS) {
		reply->createPage(HTTP_INTERNAL_ERROR, GetTimeNow(), "Psyclone", 0, true, false, "text/plain", "");
		return true;
	}

	bool xmlFormat = ( !format || (stricmp(format, "json") != 0));

	uint32 count = 0;
	std::string output;
	for (i = retrievedMsgs.begin(), e = retrievedMsgs.end(); i != e; i++) {
		if (xmlFormat)
			output += (*i)->toXML();
		else {
			if (!output.length())
				output += (*i)->toJSON();
			else
				output += "," + (*i)->toJSON();
		}
		count++;
		delete(*i);
	}
	retrievedMsgs.clear();

	if (xmlFormat)
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "text/xml", utils::StringFormat("<messages count=\"%u\">\n%s</messages>", count, output.c_str()).c_str());
	else
		reply->createPage(HTTP_OK, GetTimeNow(), "Psyclone", 0, true, false, "application/json", utils::StringFormat("{ \"messages\": [\n%s] }\n", output.c_str()).c_str());
	return true;
}


}
