

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  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.
//_/_/
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
// Replicoder.cpp : Defines the entry point for the console application.
//

#include "ExternalModules.h"


int RunTestCrank(uint16 sysid, std::string crankName);
int RunRetrieveTestCrank(uint16 sysid, std::string crankName);

void PrintUsage(const char* progname) {
	printf("Usage: %s <sysid>   (sysid: port number of local Psyclone Node)\n\n", progname);
}

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

	uint16 sysid = (uint16) utils::Ascii2Uint32(argv[1]);

	std::string crankName = "Test";
	if (argc > 2)
		crankName = argv[2];

	if (sysid)
		LogPrint(0, LOG_SPACE, 1, "Connecting to Psyclone ID %u to run crank '%s'...", sysid, crankName.c_str());
	else {
		sysid = 10000;
		LogPrint(0, LOG_SPACE, 1, "Connecting to Psyclone on default ID 10000 to run crank '%s'...", crankName.c_str());
	}

	if (crankName.compare("Test") == 0)
		return RunTestCrank(sysid, crankName);
	else if (crankName.compare("RetrieveTest") == 0)
		return RunRetrieveTestCrank(sysid, crankName);

	return 0;

}

int RunTestCrank(uint16 sysid, std::string crankName) {
	PsyAPI* api = NULL;
	PsySpace* space = new PsySpace("MySpace");
	int64 counter = 0;
	DataMessage* msg;
	const char* triggerName;

	// Keep checking if Psyclone node is still available
	while (true) {
		if (api && space->isConnected()) {
			if (!space->hasShutdown()) {
				if (msg = api->waitForNewMessage(100, triggerName)) {
					api->getParameter("Counter", counter);
					LogPrint(0, LOG_SPACE, 1, "Got trigger message %u...", (uint32)counter);
					api->tweakParameter("Counter", 1);
					api->postOutputMessage("Ball");
				}
			}
		}
		else {
			if (api) {
				delete(space);
				space = new PsySpace("MySpace");
				api = NULL;
			}
			// try to connect
			if (!space->connect(sysid)) {
				LogPrint(0, LOG_SPACE, 0, "Waiting to connect to Psyclone ID %u...", sysid);
				utils::Sleep(1000);
			}
			else {
				if (!space->start()) {
					LogPrint(0, LOG_SPACE, 0, "Couldn't start Psyclone ID %u...", sysid);
					delete(space);
					exit(0);
				}
				// Get PsyAPI for Crank Test2
				if (!(api = space->getCrankAPI(crankName.c_str()))) {
					LogPrint(0, LOG_SPACE, 0, "Couldn't get Crank '%s' API...", crankName.c_str());
					delete(space);
					exit(0);
				}
				LogPrint(0, LOG_SPACE, 1, "(Re)connected to Psyclone ID %u...", sysid);
			}
		}
	}

	return 0;
}



int RunRetrieveTestCrank(uint16 sysid, std::string crankName) {

	PsyAPI* api = NULL;
	PsySpace* space = new PsySpace("MySpace");
	int64 counter = 0;
	DataMessage* msg;
	const char* triggerName;

	uint32 count = 0;
	uint32 datasize = 0;
	char* data = NULL;
	int64 val;
	std::list<DataMessage*> retrievedMsgs;
	std::list<DataMessage*>::iterator i, e;

	// Keep checking if Psyclone node is still available
	while (true) {
		if (api && space->isConnected()) {
			if (!space->hasShutdown()) {
				if (msg = api->waitForNewMessage(100, triggerName)) {
					api->logPrint(0, "Got trigger message '%s'...", triggerName);
					if (++count < 10)
						api->postOutputMessage("Ball");
					else {
						// Execute retrieve
						uint8 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(0, "Retrieve 'r1' timed out...");
						else
							api->logPrint(0, "Retrieve 'r1' failed (%u)...", status);

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

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

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

						data = utils::StringFormat(datasize, "Hello World");
						status = api->queryCatalog(&data, 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(0, "Query 'write' timed out for file catalog...");
						else
							api->logPrint(0, "Query 'write' failed (%u) for file catalog...", status);
						delete[] data;

						data = utils::StringFormat(datasize, "Hello World");
						status = api->queryCatalog(&data, 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(0, "Query 'write' timed out to data catalog...");
						else
							api->logPrint(0, "Query 'write' failed to data catalog (%u)...", status);
						delete[] data;

						api->postOutputMessage("Test");
					}
				}
			}
		}
		else {
			if (api) {
				delete(space);
				space = new PsySpace("MySpace");
				api = NULL;
			}
			// try to connect
			if (!space->connect(sysid)) {
				LogPrint(0, LOG_SPACE, 0, "Waiting to connect to Psyclone ID %u...", sysid);
				utils::Sleep(1000);
			}
			else {
				// Register callback
				if (!space->registerCrankCallback("RetrieveTest2", Crank2)) {
					LogPrint(0, LOG_SPACE, 0, "Couldn't register Crank 'RetrieveTest2'...");
					utils::Sleep(1000);
					delete(space);
					space = new PsySpace("MySpace");
					continue;
				}
				if (!space->start()) {
					LogPrint(0, LOG_SPACE, 0, "Couldn't start Psyclone ID %u...", sysid);
					utils::Sleep(1000);
					delete(space);
					space = new PsySpace("MySpace");
					continue;
				}
				// Get PsyAPI for Crank Test2
				if (!(api = space->getCrankAPI(crankName.c_str()))) {
					LogPrint(0, LOG_SPACE, 0, "Couldn't get Crank '%s' API...", crankName.c_str());
					utils::Sleep(1000);
					delete(space);
					space = new PsySpace("MySpace");
					continue;
				}
				LogPrint(0, LOG_SPACE, 1, "(Re)connected to Psyclone ID %u...", sysid);
				// post ball to set things going...
				utils::Sleep(1000);
				//api->postOutputMessage("Ball");
			}
		}
	}

	return 0;
}





int8 Crank2(PsyAPI* api) {
	char* myName = new char[256];
	api->getModuleName(myName, 256);

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

	DataMessage* inMsg;
	const char* triggerName;
	uint32 datasize = 0;
	char* data = NULL;
	int64 val;
	std::list<DataMessage*> retrievedMsgs;
	std::list<DataMessage*>::iterator i, e;
	while (api->shouldContinue()) {
		if ( inMsg = api->waitForNewMessage(100, triggerName)) {
			api->logPrint(1,"got trigger message '%s'...", triggerName);
			if (stricmp(triggerName, "Ball") == 0)
				api->postOutputMessage("Ball");
			else if (stricmp(triggerName, "Test") == 0) {
				// Execute retrieve

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

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

				status = api->queryCatalog(&data, 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(0,"Query 'read' timed out for file catalog...");
				else
					api->logPrint(0,"Query 'read' failed (%u) for file catalog...", status);
				delete (data);

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

				// Shutdown
				utils::Sleep(1000);
				api->postOutputMessage("Done");
			}
		}
	}
	return 0;
}
