

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  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 "Examples.h"

namespace cmlabs {

int8 Simple(PsyAPI* api) {
	bool print = false;
	if (api->hasParameter("Print"))
		print = (api->getParameterString("Print").compare("yes") == 0);

	int32 c;
	DataMessage* inMsg;
	const char* triggerName;
	if (api->shouldContinue()) {
		if (inMsg = api->waitForNewMessage(0, triggerName)) {
			c = api->postOutputMessage();
			if (c > 1) {
				if (print)
					api->logPrint(1, "Posted %d output messages", c);
			}
			else if (c == 1) {
				if (print)
					api->logPrint(1, "Posted one output message", c);
			}
			else if (c < 0)
				api->logPrint(1, "Posted no messages (%d)", c);
			else
				api->logPrint(1, "Posted no messages");
		}
	}
	return 0;
}

int8 Ping(PsyAPI* api) {
	api->logPrint(1, "Started running (internal)...");

	DataMessage* inMsg, *outMsg;
	int64 counter = 0, start, end;
	int64 t = 0;
	const char* triggerName;
	bool print = false;
	int64 count = 10000;
	int64 cycles = 10;
	int64 modules = 2;
	double current, last = 0;

	int64 val;
	if (api->hasParameter("Cycles"))
		if (api->getParameter("Cycles", val))
			cycles = val;
	if (api->hasParameter("Count"))
		if (api->getParameter("Count", val))
			count = val;
	if (api->hasParameter("Modules"))
		if (api->getParameter("Modules", val))
			modules = val;

	int64 totalMessageCount = cycles*count;

	while (api->shouldContinue()) {
		if (inMsg = api->waitForNewMessage(100, triggerName)) {
			outMsg = new DataMessage();
			if ((stricmp(triggerName, "Ready") == 0) || (stricmp(triggerName, "Start") == 0)) {
				api->logPrint(1, "Starting %u cycles test...", cycles);
				start = GetTimeNow();
				print = true;
				t = 0;
				counter = 0;
			}
			else if (stricmp(triggerName, "Ball") == 0) {
				t += GetTimeAge(inMsg->getCreatedTime());
				//api->logPrint(1,"Got ball message type '%s'...", api->typeToText(inMsg->getType()).c_str());
				if (!counter++)
					start = GetTimeNow();
				else if (counter % totalMessageCount == 0) {
					end = GetTimeNow();
					current = ((double)(end - start)) / (modules*count);
					if (!last)
						api->logPrint(1, "Got msg %llu, avg msg time: %.1fus, avg msg age: %.1fus...",
							counter, current, ((double)t / count));
					else if (last > current)
						api->logPrint(1, "Got msg %llu, avg msg time: %.1fus, avg msg age: %.1fus (%.1fus faster)...",
							counter, current, ((double)t / count), last - current);
					else
						api->logPrint(1, "Got msg %llu, avg msg time: %.1fus, avg msg age: %.1fus (%.1fus slower)...",
							counter, current, ((double)t / count), current - last);
					api->postOutputMessage("Done", outMsg);
					outMsg = NULL;
				}
				else if (counter % count == 0) {
					end = GetTimeNow();
					current = ((double)(end - start)) / (modules*count);
					if (print) {
						if (!last)
							api->logPrint(1, "Got msg %llu, avg msg time: %.1fus, avg msg age: %.1fus...",
								counter, current, ((double)t / count));
						else if (last > current)
							api->logPrint(1, "Got msg %llu, avg msg time: %.1fus, avg msg age: %.1fus (%.1fus faster)...",
								counter, current, ((double)t / count), last - current);
						else
							api->logPrint(1, "Got msg %llu, avg msg time: %.1fus, avg msg age: %.1fus (%.1fus slower)...",
								counter, current, ((double)t / count), current - last);
					}
					last = current;
					t = 0;
					start = GetTimeNow();
				}
			}
			else {
				api->logPrint(1, "Got other message type '%s'...", api->typeToText(inMsg->getType()).c_str());
				delete(outMsg);
				outMsg = NULL;
			}
			if (outMsg) {
				api->postOutputMessage("Ball", outMsg);
				// utils::Sleep(5);
			}
		}
	}

	return 0;
}

int8 Pong(PsyAPI* api) {
	api->logPrint(1, "Started running...");

	const char* triggerName;
	DataMessage* inMsg;
	while (api->shouldContinue()) {
		if (inMsg = api->waitForNewMessage(100, triggerName)) {
			//api->logPrint(1, "Got ball message type '%s'...", api->typeToText(inMsg->getType()).c_str());
			api->postOutputMessage("Ball");
		}
	}

	return 0;
}


int8 Shutdown(PsyAPI* api) {
	api->logPrint(1, "Started running...");

	DataMessage* inMsg, *outMsg;
	const char* triggerName;

	while (api->shouldContinue()) {
		if (inMsg = api->waitForNewMessage(100, triggerName)) {
			outMsg = new DataMessage();
			api->postOutputMessage("Done", outMsg);
		}
	}

	return 0;
}

int8 Print(PsyAPI* api) {
	api->logPrint(1, "Started running...");

	DataMessage* inMsg;
	const char* triggerName;
	std::string userdata;

	uint32 count = 0;
	uint64 lastTime = 0;
	while (api->shouldContinue()) {
		if (inMsg = api->waitForNewMessage(100, triggerName)) {
			userdata = inMsg->getUserEntriesAsString();
			if (userdata.length()) {
				if (lastTime)
					api->logPrint(1, "Trigger '%s' type '%s' from '%s' [%u] (%s):\n%s", triggerName, api->typeToText(inMsg->getType()).c_str(), api->getOtherModuleName(inMsg->getFrom()).c_str(), count, PrintTimeDifString(GetTimeAge(lastTime)).c_str(), userdata.c_str());
				else
					api->logPrint(1, "Trigger '%s' type '%s' from '%s' [%u]:\n%s\n", triggerName, api->typeToText(inMsg->getType()).c_str(), api->getOtherModuleName(inMsg->getFrom()).c_str(), count, userdata.c_str());
			}
			else {
				if (lastTime)
					api->logPrint(1, "Trigger '%s' type '%s' from '%s' [%u] (%s)", triggerName, api->typeToText(inMsg->getType()).c_str(), api->getOtherModuleName(inMsg->getFrom()).c_str(), count, PrintTimeDifString(GetTimeAge(lastTime)).c_str());
				else
					api->logPrint(1, "Trigger '%s' type '%s' from '%s' [%u]", triggerName, api->typeToText(inMsg->getType()).c_str(), api->getOtherModuleName(inMsg->getFrom()).c_str(), count);
			}
			lastTime = inMsg->getCreatedTime();
			//api->postOutputMessage();
			count++;
			//if (++count == 1) {
			//	api->postOutputMessage();
			//	count = 0;
			//}
		}
	}

	return 0;
}


int8 SignalPing(PsyAPI* api) {
	DataMessage* inMsg;
	const char* triggerName;

	api->logPrint(1, "Started running...");

	int64 steps = 10;
	int64 cycles;
	if (api->hasParameter("Cycles"))
		if (api->getParameter("Cycles", cycles))
			steps = cycles;

	uint64 lastTime = GetTimeNow() - 1000000;
	uint64 totalTime = 0;
	uint32 count = 0;
	while (api->shouldContinue()) {
		if (inMsg = api->waitForNewMessage(0, triggerName)) {
			if (stricmp(triggerName, "Start") == 0) {
				api->logPrint(1, "Emitting signal [%u]...", count);
				api->emitSignal("Output");
				utils::Sleep(10);
			}
		}
		else if (inMsg = api->waitForSignal("Input", 100, lastTime)) {
			totalTime += GetTimeAge(lastTime = inMsg->getCreatedTime());
			count++;
			// api->logPrint(1,"[%s] got signal, %u [%u]...", totalTime, count);
			if (count % 10 == 0)
				api->logPrint(1, "Got signal, average %.3fus [%u]...", ((double)totalTime / count), count);
			utils::Sleep(10);
			if (count < steps)
				api->emitSignal("Output");
			else
				api->postOutputMessage();
		}
	}
	return 0;
}

int8 SignalPong(PsyAPI* api) {
	DataMessage* inMsg;
	const char* triggerName;

	api->logPrint(1, "Started running...");

	utils::Sleep(100);

	uint64 lastTime = GetTimeNow() - 1000000;
	while (api->shouldContinue()) {
		if (inMsg = api->waitForNewMessage(0, triggerName)) {
		}
		else if (inMsg = api->waitForSignal("Input", 100, lastTime)) {
			lastTime = inMsg->getCreatedTime();
			// api->postOutputMessage();
			api->emitSignal("Output");
			// utils::Sleep(10);
		}
	}
	return 0;
}


int8 RetrieveTest(PsyAPI* api) {
	int64 steps = 10;
	int64 cycles;
	if (api->hasParameter("Cycles"))
		if (api->getParameter("Cycles", cycles))
			steps = cycles;

	api->logPrint(1, "Started running, %u steps...", steps);

	int64 val;
	DataMessage* inMsg, *outMsg;
	const char* triggerName;
	uint64 start = GetTimeNow();

	std::list<DataMessage*> retrievedMsgs;
	std::list<DataMessage*>::iterator i, e;

	while (api->shouldContinue()) {
		if (inMsg = api->waitForNewMessage(100, triggerName)) {
			if ((stricmp(triggerName, "Ready") == 0) || (stricmp(triggerName, "Start") == 0)) {
				api->logPrint(1, "Got start message type '%s'...", api->typeToText(inMsg->getType()).c_str());
				for (val = 0; val < 10; val++) {
					outMsg = new DataMessage();
					outMsg->setInt("count", val);
					// api->logPrint(1,"[%s] Posting msg %llu   %s...", val, PrintTimeString(outMsg->getCreatedTime()).c_str());
					api->postOutputMessage("Ball", outMsg);
				}
				start = GetTimeNow();
			}
		}

		uint8 status;
		if (GetTimeAgeMS(start) > 1000) {
			for (uint32 nn = 0; nn<steps; nn++) {
				status = api->retrieve(retrievedMsgs, "r1");
				if (status == QUERY_SUCCESS)
					api->logPrint(1, "Successfully retrieved %u messages...", retrievedMsgs.size());
				else if (status == QUERY_TIMEOUT)
					api->logPrint(1, "Retrieve 'r1' timed out...");
				else
					api->logPrint(1, "Retrieve 'r1' failed (%u)...", status);

				for (i = retrievedMsgs.begin(), e = retrievedMsgs.end(); i != e; i++) {
					//if ((*i)->getInt("count", val))
					//	api->logPrint(1,"Count: %u", val);
					delete(*i);
				}
				retrievedMsgs.clear();

				status = api->retrieveIntegerParam(retrievedMsgs, "r2", 2, 8, 4);
				if (status == QUERY_SUCCESS)
					api->logPrint(1, "Successfully retrieved %u messages...", retrievedMsgs.size());
				else if (status == QUERY_TIMEOUT)
					api->logPrint(1, "Retrieve 'r2' timed out...");
				else
					api->logPrint(1, "Retrieve 'r2' failed (%u)...", status);

				for (i = retrievedMsgs.begin(), e = retrievedMsgs.end(); i != e; i++) {
					//if ((*i)->getInt("count", val))
					//	api->logPrint(1,"Count: %u", val);
					delete(*i);
				}
				retrievedMsgs.clear();
				utils::Sleep(100);

				status = api->retrieveIntegerParam(retrievedMsgs, "r3", 0, 100, 100);
				if (status == QUERY_SUCCESS)
					api->logPrint(1, "Successfully retrieved %u messages...", retrievedMsgs.size());
				else if (status == QUERY_TIMEOUT)
					api->logPrint(1, "Retrieve 'r3' timed out...");
				else
					api->logPrint(1, "Retrieve 'r3' failed (%u)...", status);

				for (i = retrievedMsgs.begin(), e = retrievedMsgs.end(); i != e; i++) {
					//if ((*i)->getInt("count", val))
					//	api->logPrint(1,"Count: %u", val);
					delete(*i);
				}
				retrievedMsgs.clear();

				uint32 datasize = 0;
				char* data = NULL;
				char* result = NULL;
				status = api->queryCatalog(&result, datasize, "MyFiles", "test", "read");
				if (status == QUERY_SUCCESS)
					api->logPrint(1, "Successfully retrieved %u bytes from file catalog...", datasize);
				else if (status == QUERY_TIMEOUT)
					api->logPrint(1, "Retrieve 'read' timed out for file catalog...");
				else
					api->logPrint(1, "Retrieve 'read' failed (%u) for file catalog...", status);
				delete[] result;

				data = utils::StringFormat(datasize, "Hello1 World");
				status = api->queryCatalog(&result, datasize, "MyFiles", "test", "write", data, datasize);
				if (status == QUERY_SUCCESS)
					api->logPrint(1, "Successfully wrote %u bytes to file catalog...", datasize);
				else if (status == QUERY_TIMEOUT)
					api->logPrint(1, "Retrieve 'write' timed out for file catalog...");
				else
					api->logPrint(1, "Retrieve 'write' failed (%u) for file catalog...", status);
				delete[] data;
				delete[] result;

				status = api->queryCatalog(&result, datasize, "MyData", "test", "read");
				if (status == QUERY_SUCCESS)
					api->logPrint(1, "Successfully read %u bytes from data catalog...", datasize);
				else if (status == QUERY_TIMEOUT)
					api->logPrint(1, "Retrieve 'read' timed out from data catalog...");
				else
					api->logPrint(1, "Retrieve 'read' failed (%u) from data catalog, ok if starting with a fresh catalog ...", status);
				delete[] result;

				data = utils::StringFormat(datasize, "Hello2 World");
				status = api->queryCatalog(&result, datasize, "MyData", "test", "write", data, datasize);
				if (status == QUERY_SUCCESS)
					api->logPrint(1, "Successfully wrote %u bytes to data catalog...", datasize);
				else if (status == QUERY_TIMEOUT)
					api->logPrint(1, "Retrieve 'write' timed out to data catalog...");
				else
					api->logPrint(1, "Retrieve 'write' failed to data catalog (%u)...", status);
				delete[] data;
				delete[] result;

				status = api->queryCatalog(&result, datasize, "MyData", "test", "read");
				if (status == QUERY_SUCCESS)
					api->logPrint(1, "Successfully read %u bytes from data catalog...", datasize);
				else if (status == QUERY_TIMEOUT)
					api->logPrint(1, "Retrieve 'read' timed out from data catalog...");
				else
					api->logPrint(1, "Retrieve 'read' failed (%u) from data catalog...", status);
				delete[] result;
			}

			api->postOutputMessage("Done");

			start = 0;
		}
	}

	return 0;
}

} // namespace cmlabs
