

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  This file is part of PSYCLONE version 2.0.0.170915_Beta4 (15 September 2017)
//_/_/  
//_/_/  developed by
//_/_/  
//_/_/  Communicative Machines Ltd. 
//_/_/    http://cmlabs.com/
//_/_/  Copyright (c) 2017
//_/_/  
//_/_/  in collaboration with 
//_/_/  
//_/_/  Icelandic Institute for Intelligent Machines ses
//_/_/    Menntavegur 1
//_/_/    101 Reykjavik
//_/_/    ICELAND
//_/_/    http://www.iiim.is/
//_/_/
//_/_/  PSYCLONE 2.0 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.0 is also available to 
//_/_/  any interested party from the copyright holder.
//_/_/  
//_/_/  You can redistribute this copy of PSYCLONE 2.0 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.0.  
//_/_/  If not, see <http://www.gnu.org/licenses/>.
//_/_/  
//_/_/  PSYCLONE 2.0 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 "Psyclone.h"

using namespace cmlabs;

bool ConvertAnnotationToMessageRecording(std::string annotationFile, std::string outputDir);

Node* node = NULL;

void PrintUsage(const char* progname) {
	printf("Usage: %s [] []\n\n", progname);
}

bool HasParam(int argc, char* argv[], const char* key) {
	char* str;
	char c;
	for (int n=0; n<argc; n++) {
		if ( (str = strstr(argv[n], key)) == argv[n] ) {
			c = str[strlen(key)];
			if ( (c=='=') || (c==0))
				return true;
		}
	}
	return false;
}

const char* GetParam(int argc, char* argv[], const char* key) {
	char* str;
	for (int n=0; n<argc; n++) {
		if ( (str = strstr(argv[n], key)) == argv[n] ) {
			str += strlen(key);
			if (*str=='=')
				return str+1;
		}
	}
	return NULL;
}

int GetParamInt(int argc, char* argv[], const char* key) {
	const char* str = GetParam(argc, argv, key);
	if (str == NULL)
		return -1;
	else
		return atoi(str);
}

//bool testbeat(uint32 id, uint32 count) {
//	if (count == 10) {
//		printf("%s: Timer %u, count %u, exiting...\n", PrintTimeNowString().c_str(), id, count);
//		return false;
//	}
//	else
//		printf("%s: Timer %u, count %u...\n", PrintTimeNowString().c_str(), id, count);
//	return true;
//}

THREAD_RET THREAD_FUNCTION_CALL test(THREAD_ARG arg) {
	if (arg == NULL) thread_ret_val(1);
	utils::Event* event = (utils::Event*) arg;

	while (true) {
		if (event->waitNext(50))
			printf("  Got event: %s\n", PrintTimeNowString().c_str());
	}

	thread_ret_val(0);
}

THREAD_RET THREAD_FUNCTION_CALL TestThread(THREAD_ARG arg) {
	thread_ret_val(0);
}

#include "Bitmap.h"
#include "MessagePlayer.h"

int main(int argc, char* argv[]) {

	//std::string teststr = "He becomes the third American to be detained by the North; one has been sentenced for spying, the other for trying to steal a sign from a hotel.\n\n The latest detention comes amid high tension on the peninsula, with the US warning its 'strategic patience' on the North's nuclear.\nA US naval battle group headed by an aircraft carrier, the USS Carl Vinson, and described by President Donald Trump as an 'armada', is expected to reach the Korean peninsula.\n";
	//std::string str = utils::TextListBreakLines(teststr.c_str(), 40, "<br>\n");
	//printf("%s\n", str.c_str());
	//exit(0);

	//uint32 size = 100000;
	//char* data = new char[size];
	//char* out = new char[2048];
	//memset(data, 0, size);
	//DataMessage* testMsg, *draft;
	//int64 serial = 0;

	//for (int nnn = 0; nnn < 10000000; nnn++) {
	//	testMsg = new DataMessage();
	//	testMsg->setSerial(++serial);
	//	testMsg->setData("AudioDataFrame", data, size);
	//	testMsg->setInt("AudioChannels", 1);
	//	testMsg->setInt("AudioRate", 16000);
	//	testMsg->setInt("AudioSamples", 1);
	//	testMsg->setInt("AudioBitsPerSample", 2);
	//	testMsg->setInt("TimeMS", 0);
	//	testMsg->setDouble("FilePosition", 0);
	//	testMsg->setDouble("VideoFPS", 0);
	//	draft = new DataMessage(*testMsg, 2048);
	//	memcpy(out, draft->data, draft->data->size);
	//	delete(draft);
	//	delete(testMsg);
	//}



	//std::string str = utils::ReadRegistryString("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\FA6", "ImagePath");
	//uint32 v32 = utils::ReadRegistryDWORD("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\FA6", "Start");

	//utils::WriteRegistryDWORD("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\FA62", "Test", 20);
	//utils::WriteRegistryString("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\FA62", "Test2", "Hello");

	//utils::DeleteRegistryEntry("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\FA62", "Test2");
	//utils::WriteRegistryString("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\FA62\\TestKey", "Test2", "Hello");
	//utils::CopyRegistryTree("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\FA6", "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\FA63");
	//utils::DeleteRegistryTree("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services", "FA62");

	//exit(0);

	//printf("Sizeof ReplayIndexEntry: %u\n", (uint32)sizeof(ReplayIndexEntry));

	//DataMessage* msg;
	//MessagePlayer* msgPlayer = new MessagePlayer();
	//msgPlayer->initRead("d:/c/CoCoMaps/Data/RobotStatusReplay", 0, true);
	//while (true) {
	//	msg = msgPlayer->waitForNextMessage(1000);
	//	if (msg) {
	//		printf("Trigger name: %s size: %u at %s\n",
	//			msgPlayer->getCurrentTriggerName().c_str(), msg->getSize(), PrintTimeString(msg->getCreatedTime()).c_str());
	//	}
	//	else
	//		printf("---\n");
	//}


	//uint32 size, length;
	//char* data = utils::ReadAFile("d:/32FC1.bin", size, true);

	//Bitmap* bitmap = new Bitmap(data, size, 640, 480, "32FC1", size / 480);
	//bitmap->saveToFile("d:/32FC1x.bmp");

	//char* bmpfile = Bitmap::DataToBitmapFileFormat(data, size, 640, 480, "32FC1", size/480, length);
	//utils::WriteAFile("d:/32FC1.bmp", bmpfile, length, true);

	//data = utils::ReadAFile("d:/16UC1.bin", size, true);
	//bmpfile = Bitmap::DataToBitmapFileFormat(data, size, 640, 480, "16UC1", size / 480, length);
	//utils::WriteAFile("d:/16UC1.bmp", bmpfile, length, true);

	//bitmap = new Bitmap(data, size, 640, 480, "16UC1", size / 480);
	//bitmap->saveToFile("d:/16UC1x.bmp");

	////Bitmap* bitmap = new Bitmap(data, 640, 480, size / 480);
	////bitmap->saveToFile("d:/2.bmp");

	//exit(0);


	//DataMessage* msg = new DataMessage();
	//msg->setTime("TestTime", GetTimeNow());
	//msg->setInt("TestInt", 1321);
	//msg->setInt(1, "TestIntArray", 1321);
	//msg->setInt(2, "TestIntArray", 1321);
	//msg->setInt(3, "TestIntArray", 1321);
	//msg->setString("TestString", "asdfasdf");
	//msg->setString("1", "TestString", "asdfasdf");
	//msg->setString("2", "TestString", "asdfasdf");
	//msg->setString("3", "TestString", "asdfasdf");
	//uint32 size = 231214;
	//char* data = new char[size];
	//msg->setData("TestData", data, size);

	//printf("Original message:\n%s\n\n", msg->toJSON().c_str());

	//DataMessage* msgDraft = new DataMessage(*msg, 1000);

	//printf("Draft message:\n%s\n\n", msgDraft->toJSON().c_str());
	//printf("Draft message:\n%s\n\n", msgDraft->toXML().c_str());

	//utils::Sleep(1000000);

	//DataMessage* msg = new DataMessage();
	//msg->setTime("1asdf", "Test", 1);
	//msg->setTime("2asdf", "Test", 2);
	//msg->setTime("3asdf", "Test", 3);
	//msg->setTime("5asdf", "Test", 5);
	//msg->setTime("6asdf", "Test", 6);

	//msg->setTime("Test2", GetTimeNow());
	//msg->setString("Test3", "Hello World");


	//int64 t;
	//t = msg->getTime("1asdf", "Test");
	//t = msg->getTime("3asdf", "Test");
	//t = msg->getTime("6asdf", "Test");

	//std::map<std::string, uint64> a = msg->getTimeMap("Test");

	//DataMessage* msg2 = new DataMessage();
	//msg2->setTimeMap("Test", a);

	//std::string str;
	//str = msg->toJSON();
	//printf(str.c_str());
	//str = msg2->toJSON();
	//printf(str.c_str());
	//str = msg->toXML();
	//printf(str.c_str());
	//str = msg2->toXML();
	//printf(str.c_str());
	//return 0;

//	MemoryRequestServer::UnitTest();
//	return 0;
//	MovingAverage::UnitTest();
//	return 0;

	//uint32 newID;
	//for (int ii = 0; ii < 10000; ii++) {
	//	if (!ThreadManager::CreateThread(TestThread, NULL, newID)) {
	//		return 0;
	//	}
	//}
	//newID = 0;

	#ifdef WINDOWS
		//_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
	#else
		//	SIGABRT Abnormal termination 
		signal(SIGABRT, SIG_IGN);
		signal(SIGPIPE, SIG_IGN);
		signal(SIGBUS, SIG_IGN);
		signal(SIGHUP, signal_handler);
		//	SIGTERM Termination request 
		signal(SIGTERM, signal_handler);
		//	SIGINT CTRL+C signal 
		signal(SIGINT, signal_handler);
		signal(SIGABRT, signal_handler);
		//	SIGFPE Floating-point error 
		signal(SIGFPE, signal_handler);
		//	SIGILL Illegal instruction 
		signal(SIGILL, signal_handler);
		//	SIGSEGV Illegal storage access 
		signal(SIGSEGV, signal_handler);
	#endif

	//ThreadHandle tid;
	//uint32 osid;
	//utils::Event event;
	//utils::CreateThread(test, &event, tid, osid);
	//utils::CreateThread(test, &event, tid, osid);
	//utils::CreateThread(test, &event, tid, osid);
	//utils::CreateThread(test, &event, tid, osid);
	//utils::CreateThread(test, &event, tid, osid);

	//while (true) {
	//	printf("Signal event: %s\n", PrintTimeNowString().c_str());
	//	event.signal();
	//	utils::Sleep(0);
	//}

	//if (!utils::CreateDrumBeat(1, 50, testbeat, true)) {
	//	printf("Couldn't create beat 1...\n");
	//	return 0;
	//}
	//utils::Sleep(10);
	//if (!utils::CreateDrumBeat(2, 50, testbeat, true)) {
	//	printf("Couldn't create beat 1...\n");
	//	return 0;
	//}
	//utils::Sleep(10);
	//if (!utils::CreateDrumBeat(3, 50, testbeat, true)) {
	//	printf("Couldn't create beat 1...\n");
	//	return 0;
	//}

	//utils::Sleep(1000000000);


	//uint32* bittest = new uint32[8];
	//uint32 size = sizeof(uint32)*8;
	//memset(bittest, 255, size);

	//uint32 loc = 242;
	//bit check;
	//if (!utils::GetBit(loc, bittest, size, check)) {
	//	fprintf(stderr, "Bit location %u already in use\n", loc);
	//	return 0;
	//}
	//if ((check == BITOCCUPIED))
	//	fprintf(stderr, "Bit location %u occupied\n", loc);
	//else
	//	fprintf(stderr, "Bit location %u free\n", loc);

	//// Reserve it
	//utils::SetBit(loc, BITOCCUPIED, bittest, size);

	//if (!utils::GetBit(loc, bittest, size, check)) {
	//	fprintf(stderr, "Bit location %u already in use\n", loc);
	//	return 0;
	//}
	//if ((check == BITOCCUPIED))
	//	fprintf(stderr, "Bit location %u occupied\n", loc);
	//else
	//	fprintf(stderr, "Bit location %u free\n", loc);

	//return 0;



//	uint8 a1 = 1, a2 = 2, a3 = 3, a4 = 4, a5 = 5, a6 = 6, a7 = 7, a8 = 8;
//	uint8 b1 = 111, b2 = 234, b3 = 124, b4 = 246, b5 = 24, b6 = 19, b7 = 20, b8 = 21;
//	uint8 x = 255;
//
//	uint64 conA = TYPEGEN(a1, a2, a3, a4, a5, a6, a7, a8);
////	uint64 conB = CONTEXT(b1, b2, b3, b4, b5, b6, b7, b8);
//	uint64 conB = CONTEXT(a1, a2, a3, a4, a5, x, x, x);
//
//	if (MATCH(conA, conB)) {
//		printf("Works\n");
//		return 0;
//	}
//	else {
//		printf("****** Error ******\n");
//		return 0;
//	}

//	uint8 type = TYPELEVEL(conA, 3);
//	printf("Level: %u\n", type);

	//ReplyCheck* replyCheck = new ReplyCheck();
	//uint32 newID = replyCheck->newEntry();
	//replyCheck->addReplier(newID, 1);
	//replyCheck->addReplier(newID, 2);
	//replyCheck->addReplier(newID, 3);
	//replyCheck->addReplier(newID, 4);

	//ReplyCheckEntry* entry;
	//uint8 res;
	//if ((res = replyCheck->waitForReplies(newID, 1000, &entry, false)) != SYNC_TIMEOUT)
	//	return 0;
	//replyCheck->addReply(newID, 1, true);
	//replyCheck->addReply(newID, 2, true);
	//replyCheck->addReply(newID, 3, true);
	//if ((res = replyCheck->waitForReplies(newID, 1000, &entry, false)) != SYNC_TIMEOUT)
	//	return 0;
	//replyCheck->addReply(newID, 4, false);
	//if ((res = replyCheck->waitForReplies(newID, 1000, &entry)) != SYNC_SUCCESS)
	//	return 0;

	utils::SetCommandLine(argc, argv);

	const char* procname = argv[0];
	int spaceid = GetParamInt(argc, argv, "space");
	const char* name = GetParam(argc, argv, "name");

	int port = GetParamInt(argc, argv, "port");
	if (port <= 0)
		port = 10000;

	const char* logdir = GetParam(argc, argv, "logdir");
	const char* configfile = GetParam(argc, argv, "spec");
	const char* htmldir = GetParam(argc, argv, "html");

	uint32 len;
	char* configXML = NULL;
	if (configfile && strlen(configfile)) {
		configXML = utils::ReadAFile(configfile, len);
		if (configXML == NULL) {
			LogPrint(0, LOG_SYSTEM, 0, "Error reading PsySpec file '%s'...", configfile);
			exit(0);
		}
	}

	std::string verboseCmdline;
	int val;
	for (int n=0; n<SUBJECT_COUNT; n++) {
		if ( (val = GetParamInt(argc, argv, VerboseSubjects[n])) >= 0) {
			if (!n)
				LogSystem::SetLogLevelVerbose(val);
			else
				LogSystem::SetLogLevelVerbose(n, val);
			verboseCmdline += utils::StringFormat("%s=%u ", VerboseSubjects[n], (uint32)val);
		}
		if ( (val = GetParamInt(argc, argv, DebugSubjects[n])) >= 0) {
			if (!n)
				LogSystem::SetLogLevelDebug(val);
			else
				LogSystem::SetLogLevelDebug(n, val);
			verboseCmdline += utils::StringFormat("%s=%u ", DebugSubjects[n], (uint32)val);
		}
	}

	const char* test;
	if (HasParam(argc, argv, "test")) {
		test = GetParam(argc, argv, "test");
		if (test == NULL) {
			UnitTest_CMSDK();
			exit(0);
		}
		else if (stricmp(test, "sdk") == 0) {
			UnitTest_CMSDK();
			exit(0);
		}
		else if (stricmp(test, "perf") == 0) {
			PerfTest_CMSDK();
			exit(0);
		}
		else if (stricmp(test, "timer") == 0) {
			utils::UnitTest_Timer();
			exit(0);
		}
		else if (stricmp(test, "utils") == 0) {
			utils::UtilsTest();
			exit(0);
		}
		else if (stricmp(test, "server") == 0) {
			NetworkTest_TCPServer((uint16)GetParamInt(argc, argv, "port"));
			exit(0);
		}
		else if (stricmp(test, "client") == 0) {
			NetworkTest_TCPClient(GetParam(argc, argv, "server"), (uint16)GetParamInt(argc, argv, "port"));
			exit(0);
		}
		else if (stricmp(test, "network") == 0) {
			NetworkManager::UnitTest();
			exit(0);
		}
		else if (stricmp(test, "rest") == 0) {
			RESTParser::UnitTest();
			exit(0);
		}
		else if (stricmp(test, "requestgateway") == 0) {
			RequestGateway::UnitTest();
			exit(0);
		}
		else if (stricmp(test, "http") == 0) {
			NetworkManager::UnitTestHTTP();
			exit(0);
		}
		else if (stricmp(test, "httpserver") == 0) {
			std::vector<std::string> urls;
			urls.push_back("/main.css");
			urls.push_back("/main.html");
			urls.push_back("/main.js");
			urls.push_back("/main_template.xml");
			urls.push_back("/psyutils.js");
			urls.push_back("/readme.html");
			urls.push_back("/simplepage_template.html");
			urls.push_back("/system.css");
			urls.push_back("/system.html");
			urls.push_back("/system.js");
			urls.push_back("/test.xml");
			NetworkManager::TestHTTP("localhost", 10000, urls);
			//utils::Sleep(100000000);
			exit(0);
		}
		else if (stricmp(test, "https") == 0) {
			NetworkManager* manager = new NetworkManager();
			HTTPReply* reply = manager->makeHTTPRequest("https://www.ssllabs.com/ssltest/", 10000);
			utils::Sleep(100000000);
			exit(0);
		}
		else if (stricmp(test, "queues") == 0) {
			ProcessMemory::UnitTest();
		//	MemoryQueues::UnitTestQueues();
			exit(0);
		}
		else {
			LogPrint(0, LOG_SYSTEM, 0, "Test '%s' is not recognised\n", test);
			exit(0);
		}
	}	

	if (HasParam(argc, argv, "annotate")) {
		const char* annotateFile, *outputDir;
		annotateFile = GetParam(argc, argv, "annotate");
		outputDir = GetParam(argc, argv, "outputdir");
		if (!annotateFile || !outputDir) {
			LogPrint(0, LOG_SYSTEM, 0, "Usage: Psyclone annotate=file outputdir=dir\n");
			exit(0);
		}
		ConvertAnnotationToMessageRecording(annotateFile, outputDir);
		exit(0);
	}

	if (HasParam(argc, argv, "listmsg")) {
		MessagePlayer* msgPlayer = new MessagePlayer();
		if (!msgPlayer->initRead(GetParam(argc, argv, "listmsg"))) {
			LogPrint(0, LOG_SYSTEM, 0, "Dir '%s' doesn't contain any message recordings\n", GetParam(argc, argv, "listmsg"));
			exit(0);
		}
		printf("\n%s\n", msgPlayer->printAllString(GetParam(argc, argv, "format")).c_str());
		exit(0);
	}

	//Node* node2;
	// psyclone psytest
	//    run single node on local computer
	// psyclone psytest=2
	//    run two nodes on local computer, other port auto-detect
	// psyclone psytest=N
	//    run N nodes on local computer, other ports auto-detect
	// psyclone psytest=N nodes=192.168.1.12:5000,192.168.1.13:8000
	//    nodes: other nodes already running
	//    N: will start N - count(nodes) local nodes, at least one
	

	std::string cmdline;
	std::list<uint32> autoStartedNodeProcIDs;
	uint32 procID;
	int32 nodeCount = 0;
	uint32 nodeIndex = 2;
	uint32 port2 = 0;
	std::string nodesXML;

	while (true) {
		cmdline.clear();
		autoStartedNodeProcIDs.clear();
		procID = 0;
		nodeCount = 0;
		nodeIndex = 2;
		port2 = 0;

		if (HasParam(argc, argv, "psytest")) {
			nodeCount = GetParamInt(argc, argv, "psytest");
			if (nodeCount <= 0)
				nodeCount = 1;
			nodesXML = utils::StringFormat("	<node name=\"Main\" address=\"localhost\" port=\"%u\" />\n", port);
			if (nodeCount > 1) {
				const char* nodes = GetParam(argc, argv, "nodes");
				// Build up nodes list
				if (nodes) {
					std::multimap<std::string, std::string> nodeMap = utils::TextMultiMapSplit(nodes, ",", ":");
					std::multimap<std::string, std::string>::iterator iN = nodeMap.begin(), eN = nodeMap.end();
					while (iN != eN) {
						nodesXML += utils::StringFormat("	<node name=\"Node%u\" address=\"%s\" port=\"%s\" />\n",
							nodeIndex++, iN->first.c_str(), iN->second.c_str());
						iN++;
					}
				}
				uint16 lastPort = port, nodePort = 0;
				if (nodeCount - nodeIndex + 1 > 20) {
					LogPrint(0, LOG_SYSTEM, 0, "Too many nodes (%d) being auto started", nodeCount - nodeIndex);
					exit(0);
				}

				uint32 rows = 3;
				uint32 cols = (uint32)ceilf((float)((nodeCount + 1.0) / rows));
				uint32 dWidth, dHeight;
				uint16 w = 400, h = 300;
				if (utils::GetDesktopSize(dWidth, dHeight)) {
					w = dWidth / cols;
					h = dHeight / rows;
				}
				utils::RenameConsoleWindow("Psyclone Node 1", true);
				utils::Sleep(100);
				utils::MoveConsoleWindow(0, 0, w, dHeight);
				while (nodeCount - nodeIndex + 1 > 0) {
					if (utils::GetNextAvailableLocalPort(lastPort, nodePort)) {
						nodesXML += utils::StringFormat("	<node name=\"Node%u\" address=\"localhost\" port=\"%u\" />\n",
							nodeIndex, nodePort);
						// Auto start local node
						cmdline = utils::StringFormat("%s port=%u %s",
							utils::GetCommandLineExecutable().c_str(),
							nodePort,
							verboseCmdline.c_str());
						utils::Sleep(500);
						if (procID = utils::NewProcess(cmdline.c_str(), NULL,
							utils::StringFormat("Psyclone Node %u", nodeIndex).c_str(), w * ((int)((nodeIndex - 2) / rows) + 1), h * ((nodeIndex - 2) % rows), w, (int)(h * 0.9))) {
							autoStartedNodeProcIDs.push_back(procID);
							LogPrint(0, LOG_SYSTEM, 0, "Started Node %u [%u]: %s", nodeIndex, nodePort, cmdline.c_str());
						}
						else {
							LogPrint(0, LOG_SYSTEM, 0, "Couldn't autostart local node %u...", nodeIndex);
							delete[] configXML;
							exit(0);
						}

						lastPort = nodePort;
						nodeIndex++;
					}
				}
			}

			uint8 mode = 0;
			if (HasParam(argc, argv, "stay"))
				mode |= STAYAFTERFINISH;
			if (HasParam(argc, argv, "long"))
				mode |= LONGPINGTEST | NOTIMETEST;
			if (HasParam(argc, argv, "short"))
				mode |= SHORTTEST;
			if (HasParam(argc, argv, "cont"))
				mode |= CONTINUOUSTEST;

			if (!configXML)
				configXML = buildPsyTestSpec(len, nodeCount, nodesXML, mode);
			LogPrint(0, LOG_SYSTEM, 3, "Using PsySpec:\n%s\n", configXML);
		}

		if (spaceid > 0) {
			if (name)
				LogPrint(0, LOG_SYSTEM, 0, "Starting space '%s' ID: %u...", name, spaceid);
			else
				LogPrint(0, LOG_SYSTEM, 0, "Starting space ID %u...", spaceid);

			PsySpace* space = new PsySpace(name, false, spaceid, false);
			if (!space->connect(port, true)) {
				LogPrint(0, LOG_SYSTEM, 0, "Space ID %u couldn't initialise...", spaceid);
				delete[] configXML;
				exit(0);
			}
			if (!space->start()) {
				LogPrint(0, LOG_SYSTEM, 0, "Space ID %u couldn't start...", spaceid);
				delete[] configXML;
				exit(0);
			}
			LogPrint(0, LOG_SYSTEM, 0, "Space ID %u has started successfully...", spaceid);

			while (!space->hasShutdown())
				utils::Sleep(100);
			LogPrint(0, LOG_SYSTEM, 0, "Space ID %u has shutdown...", spaceid);
			delete(space);
			delete[] configXML;
			exit(0);
		}

		node = new Node(logdir, procname);
		if (!node->init(port, configXML, htmldir)) {
			LogPrint(0, LOG_SYSTEM, 0, "Could not initialise local Node...\n");
			utils::Sleep(1000);
			delete(node);
			delete[] configXML;
			exit(0);
		}

		if (configfile && strlen(configfile))
			node->setConfigFile(configfile);
		else if (HasParam(argc, argv, "psytest"))
			node->setConfigFile("PsyTest (auto)");

		int8 status = node->run();

		if (!node->terminate()) {
			LogPrint(0, LOG_SYSTEM, 0, "Could not terminate local Node...\n");
			delete(node);
			delete[] configXML;
			exit(0);
		}
		//printf("-9-"); fflush(stdout);

		// Shutdown autostarted nodes
		std::list<uint32>::iterator i = autoStartedNodeProcIDs.begin(), e = autoStartedNodeProcIDs.end();
		while (i != e) {
			utils::EndProcess(*i);
			i++;
		}
		autoStartedNodeProcIDs.clear();

		delete(node);

		if (status != NODE_EXIT_RESTART)
			break;

		utils::Sleep(1000);
	}
	delete[] configXML;

	//printf("-10-"); fflush(stdout);
	exit(0);
}




std::string addTimePsyTestSpec(uint16 nodeCount, std::string context, std::string nextContext) {

	// top and main node 
	std::string xml = utils::StringFormat(
"	<!-- ************** Time Test ************** -->\n\
	\n\
	<module name=\"TimeMaster\" node=\"Main\">\n\
		<parameter name=\"Count\" type=\"Integer\" value=\"%%TimeCount%%\" />\n\
		<parameter name=\"Nodes\" type=\"Integer\" value=\"%%Nodes%%\" />\n\
		<context name=\"%s\">\n\
			<trigger name=\"Start\" context=\"%s\" />\n\
			<trigger name=\"InputMsg\" type=\"time.1\" />\n\
			<crank name=\"TimeMaster\" function=\"Examples::PsyTest_TimeMaster\" />\n\
			<post name=\"OutputMsg\" type=\"time.2\" ttl=\"1000000\" />\n\
			<post name=\"Done\" context=\"%s\" />\n\
		</context>\n\
	</module>\n", context.c_str(), context.c_str(), nextContext.c_str());

	if (nodeCount == 1) {
		xml += utils::StringFormat("\n\
	<module name=\"TimeSlave2\" node=\"Main\">\n\
		<context name=\"%s\">\n\
			<trigger name=\"InputMsg\" type=\"time.2\" />\n\
			<crank name=\"TimeSlave2\" function=\"Examples::PsyTest_TimeSlave\" />\n\
			<post name=\"OutputMsg\" type=\"time.1\" ttl=\"1000000\" />\n\
		</context>\n\
	</module>\n\n", context.c_str());
	}
	else {
		for (uint16 n=2; n<=nodeCount; n++) {
			xml += utils::StringFormat("\n\
	<module name=\"TimeSlave%u\" node=\"Node%u\">\n\
		<context name=\"%s\">\n\
			<trigger name=\"InputMsg\" type=\"time.2\" />\n\
			<crank name=\"TimeSlave%u\" function=\"Examples::PsyTest_TimeSlave\" />\n\
			<post name=\"OutputMsg\" type=\"time.1\" ttl=\"1000000\" />\n\
		</context>\n\
	</module>\n\n", n, n, context.c_str(), n);
		}
	}

	return xml;
}

std::string addPingPsyTestSpec(uint16 nodeCount, std::string context, std::string nextContext) {

	// top and main node 
	std::string xml = utils::StringFormat(
"	<!-- ************** Ping Test ************** -->\n\
	\n\
	<module name=\"PingMaster\" node=\"Main\">\n\
		<customview name=\"MyStatus\" template=\"elements/mystatus.html\" />\n\
		<parameter name=\"Count\" type=\"Integer\" value=\"%%PingCount%%\" />\n\
		<parameter name=\"Cycles\" type=\"Integer\" value=\"%%PingCycles%%\" />\n\
		<parameter name=\"Nodes\" type=\"Integer\" value=\"%%Nodes%%\" />\n\
		<context name=\"%s\">\n\
			<trigger name=\"Start\" context=\"%s\" />\n\
			<trigger name=\"InputMsg\" type=\"ping.1\" />\n\
			<crank name=\"PingMaster\" function=\"Examples::PsyTest_PingMaster\" />\n\
			<post name=\"OutputMsg\" type=\"ping.2\" />\n\
			<post name=\"Done\" context=\"%s\" />\n\
		</context>\n\
	</module>\n", context.c_str(), context.c_str(), nextContext.c_str());

	if (nodeCount == 1) {
		xml += utils::StringFormat("\n\
	<module name=\"PingSlave2\" node=\"Main\">\n\
		<context name=\"%s\">\n\
			<trigger name=\"InputMsg\" type=\"ping.2\" />\n\
			<crank name=\"PingSlave2\" function=\"Examples::PsyTest_PingSlave\" />\n\
			<post name=\"OutputMsg\" type=\"ping.1\" />\n\
		</context>\n\
	</module>\n\n", context.c_str());
	}
	else {
		for (uint16 n=2; n<=nodeCount; n++) {
			xml += utils::StringFormat("\n\
	<module name=\"PingSlave%u\" node=\"Node%u\">\n\
		<context name=\"%s\">\n\
			<trigger name=\"InputMsg\" type=\"ping.%u\" />\n\
			<crank name=\"PingSlave%u\" function=\"Examples::PsyTest_PingSlave\" />\n\
			<post name=\"OutputMsg\" type=\"ping.%u\" />\n\
		</context>\n\
	</module>\n\n", n, n, context.c_str(), n, n, (n < nodeCount) ? n+1 : 1);
		}
	}

	return xml;
}

std::string addPingUDPPsyTestSpec(uint16 nodeCount, std::string context, std::string nextContext) {

	// top and main node 
	std::string xml = utils::StringFormat(
"	<!-- ************** PingUDP Test ************** -->\n\
	\n\
	<module name=\"PingUDPMaster\" node=\"Main\">\n\
		<parameter name=\"Count\" type=\"Integer\" value=\"%%PingCount%%\" />\n\
		<parameter name=\"Cycles\" type=\"Integer\" value=\"%%PingCycles%%\" />\n\
		<parameter name=\"Nodes\" type=\"Integer\" value=\"%%Nodes%%\" />\n\
		<customview name=\"MyStatus\" template=\"elements/mystatus.html\" />\n\
		<context name=\"%s\">\n\
			<trigger name=\"Start\" context=\"%s\" />\n\
			<trigger name=\"InputMsg\" type=\"ping.1\" />\n\
			<crank name=\"PingUDPMaster\" function=\"Examples::PsyTest_PingMaster\" />\n\
			<post name=\"OutputMsg\" type=\"ping.2\" guaranteed=\"no\" />\n\
			<post name=\"Done\" context=\"%s\" />\n\
		</context>\n\
	</module>\n", context.c_str(), context.c_str(), nextContext.c_str());

	if (nodeCount == 1) {
		xml += utils::StringFormat("\n\
	<module name=\"PingUDPSlave2\" node=\"Main\">\n\
		<context name=\"%s\">\n\
			<trigger name=\"InputMsg\" type=\"ping.2\" />\n\
			<crank name=\"PingUDPSlave2\" function=\"Examples::PsyTest_PingSlave\" />\n\
			<post name=\"OutputMsg\" type=\"ping.1\" guaranteed=\"no\" />\n\
		</context>\n\
	</module>\n\n", context.c_str());
	}
	else {
		for (uint16 n=2; n<=nodeCount; n++) {
			xml += utils::StringFormat("\n\
	<module name=\"PingUDPSlave%u\" node=\"Node%u\">\n\
		<context name=\"%s\">\n\
			<trigger name=\"InputMsg\" type=\"ping.%u\" />\n\
			<crank name=\"PingUDPSlave%u\" function=\"Examples::PsyTest_PingSlave\" />\n\
			<post name=\"OutputMsg\" type=\"ping.%u\" guaranteed=\"no\" />\n\
		</context>\n\
	</module>\n\n", n, n, context.c_str(), n, n, (n < nodeCount) ? n+1 : 1);
		}
	}

	return xml;
}

std::string addSignalPsyTestSpec(uint16 nodeCount, std::string context, std::string nextContext) {

	// top and main node 
	std::string xml = utils::StringFormat(
"	<!-- ************** Signal Test ************** -->\n\
	\n\
	<module name=\"SignalMaster\" node=\"Main\">\n\
		<parameter name=\"Count\" type=\"Integer\" value=\"%%SignalCount%%\" />\n\
		<parameter name=\"Cycles\" type=\"Integer\" value=\"%%SignalCycles%%\" />\n\
		<parameter name=\"Nodes\" type=\"Integer\" value=\"%%Nodes%%\" />\n\
		<context name=\"%s\">\n\
			<trigger name=\"Start\" context=\"%s\" />\n\
			<signal name=\"Input\" type=\"signal.1\" />\n\
			<crank name=\"SignalMaster\" function=\"Examples::PsyTest_SignalMaster\" />\n\
			<signal name=\"Output\" type=\"signal.2\" />\n\
			<post name=\"Done\" context=\"%s\" />\n\
		</context>\n\
	</module>\n", context.c_str(), context.c_str(), nextContext.c_str());

	if (nodeCount == 1) {
		xml += utils::StringFormat("\n\
	<module name=\"SignalSlave2\" node=\"Main\">\n\
		<context name=\"%s\">\n\
			<trigger name=\"Start\" context=\"%s\" />\n\
			<signal name=\"Input\" type=\"signal.2\" />\n\
			<crank name=\"SignalSlave2\" function=\"Examples::PsyTest_SignalSlave\" />\n\
			<signal name=\"Output\" type=\"signal.1\" />\n\
		</context>\n\
	</module>\n\n", context.c_str(), context.c_str());
	}
	else {
		for (uint16 n=2; n<=nodeCount; n++) {
			xml += utils::StringFormat("\n\
	<module name=\"SignalSlave%u\" node=\"Node%u\">\n\
		<context name=\"%s\">\n\
			<trigger name=\"Start\" context=\"%s\" />\n\
			<signal name=\"Input\" type=\"signal.%u\" />\n\
			<crank name=\"SignalSlave%u\" function=\"Examples::PsyTest_SignalSlave\" />\n\
			<signal name=\"Output\" type=\"signal.%u\" />\n\
		</context>\n\
	</module>\n\n", n, n, context.c_str(), context.c_str(), n, n, (n < nodeCount) ? n+1 : 1);
		}
	}

	return xml;
}

std::string addRetrievePsyTestSpec(uint16 nodeCount, std::string context, std::string nextContext) {

	// top and main node 
	std::string xml = utils::StringFormat(
"	<!-- ************** Catalogs Test ************** -->\n\
	\n\
	<whiteboard name=\"WB1\" node=\"Main\" key=\"count\" keytype=\"integer\" />\n\
	\n\
	<whiteboard name=\"WB2\" node=\"Main\" key=\"count\" keytype=\"integer\">\n\
		<trigger name=\"Test1\" type=\"test.*\" />\n\
	</whiteboard>\n\
	\n\
	<catalog name=\"MyFiles\" node=\"Main\" type=\"FileCatalog\" root=\"./\">\n\
		<parameter name=\"ReadOnly\" type=\"String\" value=\"no\" />\n\
	</catalog>\n\
	\n\
	<catalog node=\"Main\" name=\"MyData\" type=\"DataCatalog\" interval=\"2000\" root=\"./mydata.dat\" />\n\
	\n\
	<module name=\"RetrieveTest1\" node=\"Main\">\n\
		<parameter name=\"Count\" type=\"Integer\" value=\"%%RequestCount%%\" />\n\
		<context name=\"%s\">\n\
			<trigger name=\"Start\" context=\"%s\" />\n\
			<retrieve name=\"r1\" source=\"WB1\" maxcount=\"100\" />\n\
			<retrieve name=\"r2\" source=\"WB1\" key=\"count\" keytype=\"integer\" />\n\
			<retrieve name=\"r3\" source=\"WB2\" key=\"count\" keytype=\"integer\" />\n\
			<query name=\"MyFiles\" source=\"MyFiles\" subdir=\"test\" ext=\"txt\" binary=\"yes\" />\n\
			<query name=\"MyData\" source=\"MyData\" />\n\
			<crank name=\"RetrieveTest1\" function=\"Examples::PsyTest_RetrieveTest\" />\n\
			<post name=\"OutputMsg\" to=\"WB1\" type=\"test.1\" ttl=\"2000000\" />\n\
			<post name=\"Done\" context=\"%s\" />\n\
			<setup>Hello world Multi</setup>\n\
		</context>\n\
	</module>\n", context.c_str(), context.c_str(), (nodeCount == 1) ? nextContext.c_str() : utils::StringFormat("%s.2", context.c_str()).c_str() );

	for (uint16 n=2; n<=nodeCount; n++) {
		xml += utils::StringFormat("\n\
	<module name=\"RetrieveTest%u\" node=\"Node%u\">\n\
		<parameter name=\"Count\" type=\"Integer\" value=\"%%RequestCount%%\" />\n\
		<context name=\"%s.%u\">\n\
			<trigger name=\"Start\" context=\"%s.%u\" />\n\
			<retrieve name=\"r1\" source=\"WB1\" maxcount=\"100\" />\n\
			<retrieve name=\"r2\" source=\"WB1\" key=\"count\" keytype=\"integer\" />\n\
			<retrieve name=\"r3\" source=\"WB2\" key=\"count\" keytype=\"integer\" />\n\
			<query name=\"MyFiles\" source=\"MyFiles\" subdir=\"test\" ext=\"txt\" binary=\"yes\" />\n\
			<query name=\"MyData\" source=\"MyData\" />\n\
			<crank name=\"RetrieveTest1\" function=\"Examples::PsyTest_RetrieveTest\" />\n\
			<post name=\"OutputMsg\" to=\"WB1\" type=\"test.%u\" ttl=\"2000000\" />\n\
			<post name=\"Done\" context=\"%s\" />\n\
			<setup>Hello world Multi</setup>\n\
		</context>\n\
	</module>\n", n, n, context.c_str(), n, context.c_str(), n, n,
			(n == nodeCount) ? nextContext.c_str() : utils::StringFormat("%s.%u", context.c_str(), n+1).c_str());
	}

	return xml;
}





char* buildPsyTestSpec(uint32& len, uint16 nodeCount, std::string nodesXML, uint8 mode) {

	std::string configXML;

	uint32 timeCount = (mode & NOTIMETEST) ? 1 : 5;
	uint32 pingCount = (mode & LONGPINGTEST) ? 100000 : 10;
	uint32 pingCycles = 10;
	uint32 signalCount = 10;
	uint32 signalCycles = 10;
	uint32 requestCount = 10;

	if (mode & SHORTTEST) {
		timeCount = 1;
		pingCount = 2;
		pingCycles = 2;
		signalCount = 2;
		signalCycles = 2;
		requestCount = 2;
	}

	std::string endGame = "type=\"Psyclone.Shutdown\"";
	if (mode & STAYAFTERFINISH)
		endGame = "type=\"Test.Finished\"";
	else if (mode & CONTINUOUSTEST)
		endGame = "context=\"Test.TimeTest\"";

	configXML = utils::StringFormat(
"<psySpec>\n%s\n\
	<library name=\"Examples\" library=\"Examples\" /> <!-- universal library on all nodes -->\n\
	\n\
	<variable name=\"Nodes\" value=\"%u\" />\n\
	<variable name=\"TimeCount\" value=\"%u\" />\n\
	<variable name=\"PingCount\" value=\"%u\" />\n\
	<variable name=\"PingCycles\" value=\"%u\" />\n\
	<variable name=\"SignalCount\" value=\"%u\" />\n\
	<variable name=\"SignalCycles\" value=\"%u\" />\n\
	<variable name=\"RequestCount\" value=\"%u\" />\n\
	\n\
	<!-- ************** Startup ************** -->\n\
	\n\
	<module name=\"Startup\">\n\
		<trigger name=\"Ready\" type=\"Psyclone.Ready\" />\n\
		<crank name=\"Start\" function=\"Simple\" />\n\
		<post name=\"Done\" context=\"Test.TimeTest\" />\n\
	</module>\n\
	\n%s%s%s%s%s\n\
	\n\
	<!-- ************** Shutdown ************** -->\n\
	\n\
	<module name=\"Shutdown\" node=\"Main\">\n\
		<context name=\"Test.Done\">\n\
			<trigger name=\"Start\" context=\"Test.Done\" />\n\
			<crank name=\"Pong\" function=\"Simple\" />\n\
		<post name=\"Done\" %s />\n\
		</context>\n\
	</module>\n\
	\n\
</psySpec>\n",
	nodesXML.c_str(), nodeCount,
	timeCount, pingCount, pingCycles, signalCount, signalCycles, requestCount,
	addTimePsyTestSpec(nodeCount, "Test.TimeTest", "Test.PingTest").c_str(),
	addPingPsyTestSpec(nodeCount, "Test.PingTest", "Test.PingUDPTest").c_str(),
	addPingUDPPsyTestSpec(nodeCount, "Test.PingUDPTest", "Test.SignalTest").c_str(),
	addSignalPsyTestSpec(nodeCount, "Test.SignalTest", "Test.CatalogTest").c_str(),
	addRetrievePsyTestSpec(nodeCount, "Test.CatalogTest", "Test.Done").c_str(),
	endGame.c_str());

	len = (uint32)(configXML.size() + 1);
	char* xml = new char[len];
	utils::strcpyavail(xml, configXML.c_str(), len, true);

	// printf("%s", xml);
	//exit(0);

	return xml;
}


int CRTLCCount = 0;
void signal_handler(int sig) {
	//	SIGABRT Abnormal termination 
	//	SIGFPE Floating-point error 
	//	SIGILL Illegal instruction 
	//	SIGINT CTRL+C signal 
	//	SIGSEGV Illegal storage access 
	//	SIGTERM Termination request 

	signal(sig, SIG_IGN);

	switch (sig) {
	case SIGABRT:
		printf("\n*******************************************************\n");
		printf("         Received Abnormal Termination Signal    \n");
		printf("*******************************************************\n");
		if (node)
			node->terminate();
		signal(sig, signal_handler);
		break;
	case SIGTERM:
		printf("\n*******************************************************\n");
		printf("            Received Termination Signal    \n");
		printf("*******************************************************\n");
		if (node)
			node->terminate();
		CRTLCCount++;
		if (CRTLCCount > 3)
			exit(0);
		signal(sig, signal_handler);
		break;
	case SIGINT:
		printf("\n*******************************************************\n");
		printf("           Received Shutdown Signal (CTRL+c)           \n");
		printf("*******************************************************\n");
		if (node)
			node->terminate();
		CRTLCCount++;
		if (CRTLCCount > 3)
			exit(0);
		signal(sig, signal_handler);
		break;
#ifdef WIN32
#else
	case SIGHUP:
		printf("\n*******************************************************\n");
		printf("              Received Reset Signal    \n");
		printf("*******************************************************\n");
		if (node)
			node->terminate();
		signal(sig, signal_handler);
		break;
#endif // WIN32
	case SIGFPE:
	case SIGILL:
	case SIGSEGV:
		// case SIG_ERR:
		printf("\n*******************************************************\n");
		printf("            Received Termination Signal    \n");
		printf("*******************************************************\n");
		printf("\n");
		printf("\
Psyclone or a module running inside Psyclone has encountered a problem and needs to close.\
We are sorry for the inconvenience. If this problem persists, please contact Communicative\
Machines at support@cmlabs.com.\n");
		printf("\n");
		exit(0);
		break;
	default:
		signal(sig, signal_handler);
		break;
	}
}





bool ConvertAnnotationToMessageRecording(std::string annotationFile, std::string outputDir) {
	std::string inputData = utils::ReadAFileString(annotationFile);
	if (!inputData.length()) {
		LogPrint(0, LOG_SYSTEM, 0, "Input file '%s' empty or not there\n", annotationFile.c_str());
		return false;
	}

	MessagePlayer* msgPlayer = new MessagePlayer();
	if (!msgPlayer->initWrite(outputDir.c_str(), 0, 1000000000)) {
		LogPrint(0, LOG_SYSTEM, 0, "Output dir '%s' couldn't be created\n", outputDir.c_str());
		return false;
	}

	std::vector<std::string> lines = utils::TextListSplitLines(inputData.c_str(), false, true);
	std::vector<std::string>::iterator i = lines.begin(), e = lines.end();
	std::vector<std::string> items;
	std::vector<std::string> times;

	DataMessage* msg = new DataMessage();
	uint64 startTime = GetTimeNow(), t;
	uint32 l = 0;
	uint32 hh, mm, ss, ms;
	while (i != e) {
		items = utils::TextListSplit((*i).c_str(), "\t", true, true);
		if (items.size() == 6) {
			// 0: Marker name    2: timestamp  'HH;MM;SS;mmm'
			times = utils::TextListSplit(items.at(2).c_str(), ":");
			if (times.size() < 3)
				times = utils::TextListSplit(items.at(2).c_str(), ";");
			if (times.size() < 3) {
				i++;
				continue;
			}
			hh = utils::Ascii2Uint32(times.at(0).c_str());
			mm = utils::Ascii2Uint32(times.at(1).c_str());
			ss = utils::Ascii2Uint32(times.at(2).c_str());
			if (times.size() > 3)
				ms = utils::Ascii2Uint32(times.at(3).c_str());
			else ms = 0;
			t = startTime + ((uint64)((hh * 3600) + (mm * 60) + ss) * 1000000) + (ms * 1000);
			msg->setCreatedTime(t);
			msg->setSendTime(t);
			msg->setRecvTime(t);
			msgPlayer->addMessage(items.at(0).c_str(), msg);
		}
		else {
			LogPrint(0, LOG_SYSTEM, 0, "Line %u in file '%s' ignored, has %u entries...\n", l+1, annotationFile.c_str(), items.size());
		}
		i++;
	}

	msgPlayer->flushToDisk();

	printf("%s\n", msgPlayer->printAllString("text").c_str());

	return true;
}


