

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  This file is part of CMSDK 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/
//_/_/
//_/_/  and is released under the following
//_/_/  
//_/_/  EXTENDED BSD License
//_/_/
//_/_/  Redistribution and use in source and binary forms, with or without
//_/_/  modification, is permitted provided that the following conditions 
//_/_/  are met:
//_/_/
//_/_/  - Redistributions of source code must retain the above copyright 
//_/_/    and collaboration notice, this list of conditions and the following 
//_/_/    disclaimer.
//_/_/
//_/_/  - Redistributions in binary form must reproduce the above copyright 
//_/_/    notice, this list of conditions and the following disclaimer in the 
//_/_/    documentation and/or other materials provided with the distribution.
//_/_/
//_/_/  - Neither the name of its copyright holders nor the names of its 
//_/_/    contributors may be used to endorse or promote products derived from 
//_/_/    this software without specific prior written permission.
//_/_/
//_/_/  - CADIA Clause: The license granted in and to the software under this 
//_/_/    agreement is a limited-use license. The software may not be used in 
//_/_/    furtherance of: (i) intentionally causing bodily injury or severe 
//_/_/    emotional harm to any person; (ii) invading the personal privacy or 
//_/_/    violating the human rights of any person; or (iii) committing or 
//_/_/    preparing for any act of war. 
//_/_/
//_/_/  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
//_/_/  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
//_/_/  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
//_/_/  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 
//_/_/  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
//_/_/  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
//_/_/  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
//_/_/  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
//_/_/  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
//_/_/  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
//_/_/  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//_/_/
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
#if !defined(_DATAMAPSMEMORY_H_)
#define _DATAMAPSMEMORY_H_

#include "MemoryManager.h"
#include "PsyTime.h"

namespace cmlabs {

struct DataMapsMemoryStruct {
	uint64 size;
	uint32 cid;
	uint64 createdTime;		// time of creation
	uint32 typesMaxCount;
	uint32 typesBitFieldSize;
	uint64 typesSize;
	uint32 contextsMaxCount;
	uint32 contextsBitFieldSize;
	uint64 contextsSize;
	uint32 tagsMaxCount;
	uint32 tagsBitFieldSize;
	uint64 tagsSize;
	uint32 cranksMaxCount;
	uint32 cranksBitFieldSize;
	uint64 cranksSize;
//	uint32 componentsMaxCount;
//	uint32 componentsBitFieldSize;
//	uint32 componentsSize;
	uint32 requestsMaxCount;
	uint32 requestsBitFieldSize;
	uint64 requestsSize;
};

struct GenericMapHeader {
	uint64 size; // total size of map structure in bytes
	uint32 maxID; // maximum number of entries allocated
	uint32 count; // number of entries
	uint32 bitFieldSize; // number of bytes in the bitfield
	// Bitfield
	// Entries
};

struct TypeMapEntry {
	uint16 id; // id, corresponds to the index in map
	uint8 status; // status = 0: not there, 1: in-sync, 2: ready
	uint64 time; // timestamp when entered
	char name[MAXKEYNAMELEN+1]; // name in text
	std::string toXML() {
		return utils::StringFormat("\t<type id=\"%u\" name=\"%s\" status=\"%u\" time=\"%llu\" />\n", id, name, status, time);
	}
	static std::string GetOuterXMLName() { return "types"; }
	bool writeToMsg(uint32 n, DataMessage* msg) {
		if (!msg) return false;
		msg->setInt((int64)n, "ID", id);
		msg->setInt((int64)n, "Status", status);
		msg->setString((int64)n, "Name", name);
		msg->setTime((int64)n, "CreatedTime", time);
		return true;
	}
};

struct ContextMapEntry {
	uint16 id; // id, corresponds to the index in map
	uint8 status; // status = 0: not there, 1: in-sync, 2: ready
	uint64 time; // timestamp when entered
	char name[MAXKEYNAMELEN+1]; // name in text
	std::string toXML() {
		return utils::StringFormat("\t<context id=\"%u\" name=\"%s\" status=\"%u\" time=\"%llu\" />\n", id, name, status, time);
	}
	static std::string GetOuterXMLName() { return "contexts"; }
	bool writeToMsg(uint32 n, DataMessage* msg) {
		if (!msg) return false;
		msg->setInt((int64)n, "ID", id);
		msg->setInt((int64)n, "Status", status);
		msg->setString((int64)n, "Name", name);
		msg->setTime((int64)n, "CreatedTime", time);
		return true;
	}
};

struct TagMapEntry {
	uint32 id; // id, corresponds to the index in map
	uint8 status; // status = 0: not there, 1: in-sync, 2: ready
	uint64 time; // timestamp when entered
	char name[MAXKEYNAMELEN+1]; // name in text
	std::string toXML() {
		return utils::StringFormat("\t<tag id=\"%u\" name=\"%s\" status=\"%u\" time=\"%llu\" />\n", id, name, status, time);
	}
	static std::string GetOuterXMLName() { return "tags"; }
	bool writeToMsg(uint32 n, DataMessage* msg) {
		if (!msg) return false;
		msg->setInt((int64)n, "ID", id);
		msg->setInt((int64)n, "Status", status);
		msg->setString((int64)n, "Name", name);
		msg->setTime((int64)n, "CreatedTime", time);
		return true;
	}
};

struct CrankMapEntry {
	uint16 id; // id, corresponds to the index in map
	uint8 status; // status = 0: not there, 1: in-sync, 2: ready
	uint64 time; // timestamp when entered
	uint32 compID; // id of parent component
	char name[MAXKEYNAMELEN + 1]; // name in text
	char language[MAXKEYNAMELEN + 1]; // name in text
	char function[MAXKEYNAMELEN+1]; // name in text
	char libraryFilename[MAXKEYNAMELEN+1]; // name in text
	char script[MAXSCRIPTLEN+1]; // script in text
	std::string toXML() {
		if (strlen(script))
			return utils::StringFormat("\t<crank id=\"%u\" name=\"%s\" status=\"%u\" time=\"%llu\" compid=\"%u\" function=\"%s\" library=\"%s\">\n\t\t%s\n\t</crank>\n",
				id, name, status, time, compID, function, libraryFilename, script);
		else
			return utils::StringFormat("\t<crank id=\"%u\" name=\"%s\" status=\"%u\" time=\"%llu\" compid=\"%u\" function=\"%s\" library=\"%s\" />\n",
				id, name, status, time, compID, function, libraryFilename);
	}
	static std::string GetOuterXMLName() { return "cranks"; }
	bool writeToMsg(uint32 n, DataMessage* msg) {
		if (!msg) return false;
		msg->setInt((int64)n, "ID", id);
		msg->setInt((int64)n, "CompID", compID);
		msg->setInt((int64)n, "Status", status);
		msg->setString((int64)n, "Name", name);
		msg->setString((int64)n, "Language", language);
		msg->setString((int64)n, "Function", function);
		msg->setString((int64)n, "LibraryFilename", libraryFilename);
		msg->setString((int64)n, "Script", script);
		msg->setTime((int64)n, "CreatedTime", time);
		return true;
	}
};

#define REQ_CREATED				1
#define REQ_RECEIVED_LOCAL		2
#define REQ_RECEIVED_REMOTE		3
#define REQ_PROCESSING_LOCAL	4
#define REQ_PROCESSING_REMOTE	5
#define REQ_REPLY_READY			6
#define REQ_FAILED				7
#define REQ_FAILED_DATA			8
#define REQ_FAILED_NODATA		9
#define REQ_FAILED_DATA_EOL		10
#define REQ_SUCCESS				11
#define REQ_SUCCESS_DATA		12
#define REQ_SUCCESS_NODATA		13
#define REQ_SUCCESS_DATA_EOL	14

struct RequestMapEntry {
	uint32 id; // id, corresponds to the index in map
	uint64 time; // timestamp when entered
	char name[MAXKEYNAMELEN+1]; // Internal use only for named event
	uint64 lastUpdate; // timestamp when entered
	uint8 status; // status of the process
	uint32 from; // id of component making the request
	uint32 to; // id of component processing the request
	uint32 remoteID; // id of request at origin node, if any
	uint64 dataMessageID; // id of Data Message
	uint64 dataMessageEOL; // eol of Data Message
	std::string toXML() {
		return utils::StringFormat("\t<request id=\"%u\" name=\"%s\" status=\"%u\" time=\"%llu\" lastupdate=\"%llu\" from=\"%u\" to=\"%u\" remoteid=\"%u\" msgid=\"%llu\" msgeol=\"%llu\" />\n",
			id, name, status, time, lastUpdate, from, to, remoteID, dataMessageID, dataMessageEOL);
	}
	static std::string GetOuterXMLName() { return "requests"; }
};

#define CHECKDATAMAPSMEMORYSERIAL 	if (serial != master->getDataMapsShmemSerial()) {if (!open()) {mutex->leave();return 0;}}

class MasterMemory;
class DataMapsMemory {
public:
	DataMapsMemory(MasterMemory* master);
	~DataMapsMemory();

	bool open();
	bool create(uint32 typesMaxCount, uint32 contextsMaxCount, uint32 tagsMaxCount, uint32 cranksMaxCount, uint32 requestsMaxCount);

	bool getMemoryUsage(uint64& alloc, uint64& usage);
	bool maintenance();

	std::string printFriendlyHTML();
	std::string toXML();

	// TypeLevelMap
	bool writeTypeLevelsToMsg(DataMessage* msg);
	bool confirmTypeLevelID(uint16 id);
	bool cancelTypeLevelID(uint16 id);
	bool createNewTypeLevel(uint16 id, const char* name, uint64 time, uint16& existingID);
	bool syncTypeLevels(DataMessage* msg);
	bool getTypeLevelName(uint16 id, char* name, uint32 maxSize);
	bool getTypeLevelID(const char* name, uint16 &id);
	uint8 lookupTypeLevelID(const char* name, uint16 &id); // status = 0: not there, 1: in-sync, 2: ready
	bool deleteTypeLevel(const char* name);
	bool deleteTypeLevel(uint16 id);
	uint64 getTypeLevelCreateTime(uint16 id);
	std::string printAllTypes();

	// ContextLevelMap
	bool writeContextLevelsToMsg(DataMessage* msg);
	bool confirmContextLevelID(uint16 id);
	bool cancelContextLevelID(uint16 id);
	bool createNewContextLevel(uint16 id, const char* name, uint64 time, uint16& existingID);
	bool syncContextLevels(DataMessage* msg);
	bool getContextLevelName(uint16 id, char* name, uint32 maxSize);
	bool getContextLevelID(const char* name, uint16 &id);
	uint8 lookupContextLevelID(const char* name, uint16 &id); // status = 0: not there, 1: in-sync, 2: ready
	bool deleteContextLevel(const char* name);
	bool deleteContextLevel(uint16 id);
	uint64 getContextLevelCreateTime(uint16 id);
	std::string printAllContexts();

	// TagMap
	bool writeTagsToMsg(DataMessage* msg);
	bool confirmTagID(uint32 id);
	bool cancelTagID(uint32 id);
	bool createNewTag(uint32 id, const char* name, uint64 time, uint32& existingID);
	bool syncTags(DataMessage* msg);
	bool getTagName(uint32 id, char* name, uint32 maxSize);
	bool getTagID(const char* name, uint32 &id);
	uint8 lookupTagID(const char* name, uint32 &id); // status = 0: not there, 1: in-sync, 2: ready
	bool deleteTag(const char* name);
	bool deleteTag(uint32 id);
	uint64 getTagCreateTime(uint32 id);

	// CrankMap
	bool writeCranksToMsg(DataMessage* msg);
	bool confirmCrankID(uint16 id);
	bool cancelCrankID(uint16 id);
	bool createNewCrank(uint16 id, uint32 compID, const char* name, const char* function, const char* libraryFilename, const char* language, const char* script, uint64 time, uint16& existingID);
	bool syncCranks(DataMessage* msg);
	bool getCrankName(uint16 id, char* name, uint32 maxSize);
	bool getCrankFunction(uint16 id, char* function, uint32 maxSize);
	bool getCrankLanguage(uint16 id, char* language, uint32 maxSize);
	bool getCrankLibraryFilename(uint16 id, char* libraryFilename, uint32 maxSize);
	bool getCrankID(const char* name, uint16 &id);
	uint8 lookupCrankID(const char* name, uint16 &id); // status = 0: not there, 1: in-sync, 2: ready
	uint32 getCrankCompID(uint16 id);
	bool getCrankScript(uint16 id, char* script, uint32 maxSize);
	bool deleteCrank(uint16 id);
	bool deleteCrank(const char* name);
	uint64 getCrankCreateTime(uint16 id);

	// RequestMap
	bool createNewRequest(uint32 from, uint32 to, uint32 remoteID, uint32 &id);
	bool getRequestInfo(uint32 id, uint32& from, uint32& to, uint32& remoteID);
	bool getRequestStatus(uint32 id, uint8& status, uint64& time, uint64& msgID, uint64& msgEOL);
	bool setRequestStatus(uint32 id, uint8 status);
	bool setRequestStatus(uint32 id, uint8 status, uint64 msgID, uint64 msgEOL);
	bool waitForRequestReply(uint32 id, uint32 timeout, uint8& status, uint64& time, uint64& msgID, uint64& msgEOL);
	bool deleteRequest(uint32 id);
	uint32 getRequestCount();

private:
	bool resize(uint16 port, uint32 serial, uint32 typesMaxCount, uint32 contextsMaxCount, uint32 tagsMaxCount, uint32 cranksMaxCount, uint32 requestsMaxCount);

	utils::Mutex* mutex;
	MasterMemory* master;
	DataMapsMemoryStruct* header;
	uint64 memorySize;
	uint16 port;
	uint32 serial;

	// headers for each map type
	GenericMapHeader* typeMapHeader;
	GenericMapHeader* contextMapHeader;
	GenericMapHeader* tagMapHeader;
	GenericMapHeader* crankMapHeader;
//	GenericMapHeader* componentMapHeader;
	GenericMapHeader* requestMapHeader;
};

template <typename T, typename ID>
class GenericMemoryMap {
public:
	// Generic MemoryMap
	static T* CreateEntry(char* data, ID id, const char* name);
	static T* CreateFirstFreeEntry(char* data, ID &id, const char* name);
	static T* GetEntry(char* data, ID id);
	static bool ConfirmEntry(char* data, ID id);
	static bool CancelEntry(char* data, ID id);
	static bool DeleteEntry(char* data, ID id);
	static uint64 GetEntryTime(char* data, ID id);
	static bool GetEntryName(char* data, ID id, char* name, uint32 maxSize);
	static ID GetEntryID(char* data, const char* name, bool force = false);
	static uint8 LookupEntryID(char* data, const char* name, ID& id);
	static uint32 GetCount(char* data);
	static uint32 GetUsage(char* data);
	static std::string PrintAllEntries(char* data);
	static std::string PrintAllEntriesHTML(char* data);
	static std::string ToXML(char* data);
	static bool WriteAllEntriesToMsg(char* data, DataMessage* msg);
};

} // namespace cmlabs

#include "MemoryMaps.tpl.h"

#endif //_DATAMAPSMEMORY_H_

