

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  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.
//_/_/
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
#if !defined(_CCMCATALOG_H_)
#define _CCMCATALOG_H_

#include "PsyAPI.h"
#include "Observations.h"
#include "MathClasses.h"
#include "Bitmap.h"
#include "VantagePoints.h"

namespace	cmlabs {

extern "C" {
	DllExport int8 CCMCatalog(PsyAPI* api);
	DllExport int8 CCMProxyCatalog(PsyAPI* api);
	DllExport int8 CCMCollector(PsyAPI* api);
	DllExport int8 CCMTester(PsyAPI* api);
	DllExport int8 CCMRoleNegotiator(PsyAPI* api);
	DllExport int8 CCMTaskNegotiator(PsyAPI* api);
	DllExport int8 RoleNegotiator(PsyAPI* api);
	DllExport int8 TaskNegotiator(PsyAPI* api);
	DllExport int8 TaskExecutor(PsyAPI* api);
}

class CCMDataStorage;
//uint8 PostToSystem(uint32 systemID, const char* postName, const char* myName, DataMessage* msg, PsyAPI* api, CCMDataStorage* dataStorage);
bool ProcessObservationTrigger(DataMessage* outMsg, DataMessage* inMsg, ObsEntry *obsEntry, uint64 id);

class CCMBaseTypedStorage {
public:
	virtual ~CCMBaseTypedStorage() { }	// virtual destructor to avoid compiler warnings
	virtual bool query(DataMessage* msg, DataMessage* resultMsg, std::list<std::string>* jsonList, uint32 lastObjectID) = 0;
	virtual bool addObservation(uint64 id, const char* cdata, uint32 size) = 0;
};

template <typename T>
class CCMTypedStorage : public CCMBaseTypedStorage {
public:
	CCMTypedStorage(uint32 maxKeep = 1000);
	~CCMTypedStorage();

	bool query(DataMessage* msg, DataMessage* resultMsg, std::list<std::string>* jsonList, uint32 lastObjectID);
	bool addObservation(uint64 id, const char* cdata, uint32 size);

protected:
	utils::Mutex mutex;
	std::multimap<uint64, GenericObservation<T>* > data;
	uint32 maxCount;

	bool getRange(uint64 fromTime, uint64 toTime, T& from, T& to, uint64 creatorID, uint64 objectID, uint64 topicID, const char* name, DataMessage* resultMsg, std::list<std::string>* jsonList);
	bool getPeriod(uint64 fromTime, uint64 toTime, uint64 creatorID, uint64 objectID, uint64 topicID, const char* name, DataMessage* resultMsg, std::list<std::string>* jsonList);
	bool getLast(uint32 count, uint64 creatorID, uint64 objectID, uint64 topicID, const char* name, DataMessage* resultMsg, std::list<std::string>* jsonList);
	bool getLastAllObjects(uint32 count, uint32 maxObjID, uint64 creatorID, uint64 topicID, const char* name, DataMessage* resultMsg, std::list<std::string>* jsonList);
};

class CCMMap {
public:
	CCMMap() {
		id = 0;
		view = 0;
		scale = 0;
		// invertX = invertY = false;
		image = NULL;
		parent = 0;
	}
	~CCMMap() {
		delete image;
		image = NULL;
	}

	std::string toJSON() {
		return utils::StringFormat("{ \"id\": %u, \"name\": \"%s\", \"originx\": %.3f, \"originy\": %.3f, \"offsetx\": %.3f, \"offsety\": %.3f, \"view\": %.3f, \"scale\": %.3f }\n",
			id, name.c_str(), origin.x, origin.y, offset.x, offset.y, view, scale);
	}

	uint32 id;
	std::string name;
	uint32 parent;
	PointFloat origin;
	PointFloat offset;
	double scale, view;
	Bitmap* image;
	//bool invertX, invertY;
};

class CCMIdentity {
public:
	CCMIdentity() {
		image = NULL;
		for (int n = 0; n < 10; n++) {
			faces[n] = NULL;
		}
	}
	~CCMIdentity() {
		delete image;
		image = NULL;
		for (int n = 0; n < 10; n++) {
			delete faces[n];
			faces[n] = NULL;
		}
	}

	std::string toJSON() {
		return utils::StringFormat("{ \"id\": %u, \"name\": \"%s\", \"type\": \"%s\", \"nickname\": \"%s\" }\n",
			id, name.c_str(), type.c_str(), nickname.c_str());
	}

	uint32 id;
	std::string name;
	std::string type;
	std::string nickname;
	Bitmap* image;
	Bitmap* faces[10];
};

struct CCMObjectMediaSource {
	std::string name;
	uint32 systemID;
	std::string componentName;
	std::string privateDataName;
	std::string mimetype;
};

class CCMObject {
public:
	CCMObject() {
		id = 0;
		systemID = 0;
		identity = 0;
		firstSeen = lastSeen = 0;
		presentNow = false;
		lastMediaSourceID = 0;
	}
	~CCMObject() {
	}

	std::string toJSON() {
		return utils::StringFormat("{ \"id\": %u, \"type\": \"%s\", \"identity\": %u, \"firstseen\": %llu, \"lastseen\": %llu, \"present\": %u, \"data\": %s, \"streams\": %s }\n",
			id, type.c_str(), identity, firstSeen, lastSeen, presentNow ? 1 : 0, staticDataJSON().c_str(), mediaStreamsJSON().c_str());
	}

	std::string staticDataJSON() {
		return staticData.getUserEntriesAsJSON(true);
	}

	std::string mediaStreamsJSON() {
		std::string json;
		std::map<uint32, CCMObjectMediaSource>::iterator i=mediaSources.begin(), e=mediaSources.end();
		while (i != e) {
			json += utils::StringFormat("%s { \"id\": %u, \"name\": \"%s\", \"systemid\": %u, \"component\": \"%s\", \"dataname\": \"%s\", \"mimetype\": \"%s\" }\n",
				json.length() ? "," : "", i->first,
				i->second.name.c_str(), i->second.systemID, i->second.componentName.c_str(), i->second.privateDataName.c_str(), i->second.mimetype.c_str());
			i++;
		}

		return utils::StringFormat("[%s]", json.c_str());
	}

	bool addMediaStream(const char* name, uint32 systemID, const char* componentName, const char* privateDataName, const char* mimetype) {
		if (!name || !systemID || !componentName || !privateDataName || !mimetype ||
			!strlen(name) || !strlen(componentName) || !strlen(privateDataName) || !strlen(mimetype))
			return false;

		// check if this entry already exists - allow update of mimetype
		std::map<uint32, CCMObjectMediaSource>::iterator i = mediaSources.begin(), e = mediaSources.end();
		while (i != e) {
			if (
				(stricmp(i->second.name.c_str(), name) == 0) &&
				(stricmp(i->second.componentName.c_str(), componentName) == 0) &&
				(stricmp(i->second.privateDataName.c_str(), privateDataName) == 0) &&
				(i->second.systemID == systemID)) {
				i->second.mimetype = mimetype;
				return true;
			}
			i++;
		}

		CCMObjectMediaSource source;
		source.name = name;
		source.systemID = systemID;
		source.componentName = componentName;
		source.privateDataName = privateDataName;
		source.mimetype = mimetype;
		lastMediaSourceID++;
		mediaSources[lastMediaSourceID] = source;
		return true;
	}

	uint32 id;
	uint32 systemID;
	std::string type;
	uint32 identity;
	uint64 firstSeen;
	uint64 lastSeen;
	bool presentNow;

	DataMessage staticData;
	std::map<uint32, CCMObjectMediaSource> mediaSources;
	uint32 lastMediaSourceID;
};

class CCMImage {
public:
	CCMImage() {
		id = 0;
		createdTime = GetTimeNow();
		systemID = objectID = 0;
		image = NULL;
	}
	~CCMImage() {
		delete image;
		image = NULL;
	}

	std::string toJSON() {
		return utils::StringFormat("{ \"id\": %u, \"created\": %llu, \"system\": %u, \"object\": %u, \"size\": %u }\n",
			id, createdTime, systemID, objectID, image ? image->size : 0);
	}

	uint32 id;
	uint64 createdTime;
	uint32 systemID;
	uint32 objectID;
	Bitmap* image;
};

class CCMRemoteSystemIdentity {
public:
	CCMRemoteSystemIdentity(uint32 sysID) {
		systemID = sysID;
		systemAddressCount = 0;
		memset(systemAddresses, 0, 64 * sizeof(uint32));
		systemPort = 0;
		chosenAddress = 0;
	}
	bool setFromString(const char* str) {
		std::vector<std::string> list = utils::TextListSplit(str, ":", false, true);
		if (list.size() != 3) return false;
		std::vector<std::string> alist = utils::TextListSplit(list.at(1).c_str(), ",", false, true);
		if (!alist.size()) return false;
		systemHostname = list.at(0);
		systemPort = (uint16) utils::Ascii2Uint32(list.at(2).c_str());
		systemAddressCount = (uint32)clmin(alist.size(), 64);
		uint32 addr;
		for (uint32 n = 0; n < systemAddressCount; n++) {
			if (!(addr = utils::Ascii2Uint32(alist.at(n).c_str())))
				return false;
			systemAddresses[n] = addr;
		}
		return true;
	}
	uint32 systemID;
	uint32 systemAddressCount;
	uint32 systemAddresses[64];
	uint16 systemPort;
	std::string systemHostname;
	uint32 chosenAddress;
};


struct CCMLog {
	uint64 timestamp;
	uint32 systemID;
	uint8 priorStatus;
	uint8 newStatus;
	std::string description;
	bool setData(uint64 timestamp, uint32 systemID, uint8 priorStatus, uint8 newStatus, const char* description) {
		this->timestamp = timestamp;
		this->systemID = systemID;
		this->priorStatus = priorStatus;
		this->newStatus = newStatus;
		this->description = description;
		return true;
	}
};

struct CCMRole {
	std::string name;
	enum roletype { EXCLUSIVE = 1, SHARED = 2 } type;
	// std::string complementaryRoleName;
	std::map<uint32, uint64> currentAssignments;
	std::map<uint64, CCMLog> historyLog;
	bool addCurrentData(DataMessage* msg) {
		if (!msg) return false;
		std::map<uint32, uint64>::iterator i = currentAssignments.begin(), e = currentAssignments.end();
		while (i != e) {
			msg->setTime(i->first, "Assignements", i->second);
			i++;
		}
		return true;
	}
	std::string toJSON() {
		std::string assignments;
		std::map<uint32, uint64>::iterator i = currentAssignments.begin(), e = currentAssignments.end();
		while (i != e) {
			if (assignments.length())
				assignments += utils::StringFormat(",{\"identity\": %u, \"time\": %llu}", i->first, i->second);
			else
				assignments += utils::StringFormat("{\"identity\": %u, \"time\": %llu}", i->first, i->second);
			i++;
		}
		return utils::StringFormat("{ \"name\": \"%s\", \"assignments\": [%s] }\n",
			name.c_str(), assignments.c_str());
	}
};

const std::map<uint8, std::string> TaskStatusMap = {
	{ 1,"Created"}, { 2,"Assigned" }, { 3,"Accepted" },
	{ 4,"Status" }, { 5,"Success" }, { 6,"Failed" }, { 7,"Timedout" },{ 8,"Cancelled" } };


struct CCMTask {
	uint64 id;
	int64 localRefID;
	std::string name;
	int32 timeoutMS;
	uint32 creatorID;
	uint64 createdTime;
	DataMessage* creationMsg;
	std::map<uint32, uint8> currentAssignments;
	enum tasktype { EXCLUSIVE = 1, SHARED = 2 } type;
	enum taskstatus { CREATED = 1, ASSIGNED = 2, ACCEPTED = 3, STATUS = 4, SUCCESS = 5, FAILED = 6, TIMEDOUT = 7, CANCELLED = 8} status;
	std::map<uint64, CCMLog> historyLog;
	bool addCurrentData(DataMessage* msg) {
		if (!msg) return false;
		if (creationMsg)
			msg->copyUserEntriesFromMessage(creationMsg);
		msg->setInt("TaskID", (int64)id);
		msg->setInt("ReferenceID", localRefID);
		msg->setString("TaskName", name.c_str());
		msg->setInt("CreatorID", id);
		std::map<uint32, uint8>::iterator i = currentAssignments.begin(), e = currentAssignments.end();
		while (i != e) {
			msg->setString(i->first, "Assignments", getStatusString((uint8)(i->second)).c_str());
			i++;
		}
		msg->setString("Status", getStatusString((uint8)status).c_str());
		return true;
	}
	std::string getStatusString(uint8 status) {
		std::map<uint8, std::string>::const_iterator i = TaskStatusMap.find(status);
		if (i == TaskStatusMap.end())
			return "Unknown";
		else
			return i->second;
	}
	std::string toJSON() {
		std::string assignments;
		std::map<uint32, uint8>::iterator i = currentAssignments.begin(), e = currentAssignments.end();
		while (i != e) {
			if (assignments.length())
				assignments += utils::StringFormat(",{\"identity\": %u, \"status\": \"%s\"}", i->first, getStatusString((uint8)(i->second)).c_str());
			else
				assignments += utils::StringFormat("{\"identity\": %u, \"status\": \"%s\"}", i->first, getStatusString((uint8)(i->second)).c_str());
			i++;
		}
		return utils::StringFormat("{ \"id\": %llu, \"name\": \"%s\", \"refid\": %lld, \"creator\": %u, \"created\": %llu, \"timeout\": %d, \"assignments\": [%s], \"parameters\": %s }\n",
			id, name.c_str(), localRefID, creatorID, createdTime, timeoutMS, assignments.c_str(), creationMsg ? creationMsg->getUserEntriesAsJSON(true).c_str() : "{}");
	}
};

struct TaskTracker {
	uint64 id;
	std::string name;
	std::string status;
	uint64 createdTime;
	uint64 completedTime;
	uint64 lastUpdatedTime;
	uint32 assignee;
};



struct RemoteSystemEntry {
	uint32 systemID;
	uint64 lastSeenTime;
	std::map<DataMessage*, std::string> queuedPosts;
	CCMRemoteSystemIdentity* identity;
	bool clear() {
		std::map<DataMessage*, std::string>::iterator i = queuedPosts.begin(), e = queuedPosts.end();
		while (i != e) {
			delete i->first;
			i++;
		}
		queuedPosts.clear();
		return true;
	}
};

class CCMDataStorage {
public:
	CCMDataStorage(PsyAPI* api);
	~CCMDataStorage();

	std::string init(const char* ccmDir, uint32 maxKeep = 1000);

	std::string addMap(const char* name, const char* parent, const char* offset, const char* view, const char* image, const char* invertX, const char* invertY);
	std::string updateMap(uint32 id, const char* imageData, uint32 imageDataSize, uint32 imageWidth, uint32 imageHeight, uint32 imageStep, const char* imageEncoding, float64 scale, float64 originX, float64 originY);
	std::string addIdentity(uint32 id, const char* name, const char* type, const char* nickname, const char* image, std::list<std::string>& faces);
	uint32 createObject(const char* type, uint32 identity, uint64 firstSeen, uint64 lastSeen, bool presentNow, uint32 systemID);
	bool updateObject(uint32 id, const char* type, uint32 identity, uint64 firstSeen, uint64 lastSeen, bool presentNow, uint32 systemID);
	bool addDataToObject(uint32 id, DataMessage* msg);
	bool addStreamToObject(uint32 id, const char* name, uint32 systemID, const char* componentName, const char* privateDataName, const char* mimetype);
	uint32 getObjectSystemID(uint32 id);
	bool addImage(uint32 objectID, uint32 systemID, Bitmap* image);

	std::string getFullJSON();
	bool queryMap(DataMessage* msg, DataMessage* replyMsg);
	bool queryIdentity(DataMessage* msg, DataMessage* replyMsg);
	bool queryIdentities(DataMessage* msg, DataMessage* replyMsg);
	bool queryObject(DataMessage* msg, DataMessage* replyMsg);
	bool queryObjects(DataMessage* msg, DataMessage* replyMsg);
	CCMImage* getLastImage(uint32 objectID, uint32 systemID);

	bool query(DataMessage* qMsg, DataMessage* resultMsg, std::list<std::string>* jsonList = NULL);
	bool addObservation(const char* cdata, uint32 size);

	bool addRemoteSystemIdentity(uint32 systemID, DataMessage* inMsg);
	DataMessage* addHeartbeat(DataMessage* inMsg);
	bool addQueuedPost(uint32 sysID, const char* name, DataMessage* msg);
	bool addQueuedPostToOthers(uint32 sysID, const char* name, DataMessage* msg);

	// roles
	bool addRole(const char* name, const char* type);
	bool takeRole(DataMessage* msg, DataMessage* replyMsg);
	bool leaveRole(DataMessage* msg, DataMessage* replyMsg);
	bool giveRole(DataMessage* msg, DataMessage* replyMsg);
	CCMRole* getRole(const char* name);

	// tasks
	bool createTask(DataMessage* msg, DataMessage* replyMsg);
	bool cancelTask(DataMessage* msg, DataMessage* replyMsg);
	bool assignTask(DataMessage* msg, DataMessage* replyMsg);
	bool acceptTask(DataMessage* msg, DataMessage* replyMsg);
	bool updateTask(DataMessage* msg, DataMessage* replyMsg);
	bool completeTask(DataMessage* msg, DataMessage* replyMsg);
	bool assignTask(uint64 taskID, uint32 assigneeID, uint32 sysID, DataMessage* replyMsg);
	DataMessage* getNextTimedOutTask();
	uint32 localSystemID;
	//std::map<uint32, CCMRemoteSystemIdentity*> remoteSystemIdentities;

	VantagePoints vantagePoints;
	std::map<std::string, std::string> namedPoints;

protected:

	PsyAPI* api;
	uint32 lastObsID;
	std::map<uint32, CCMBaseTypedStorage*> obsMap;
	std::string ccmDir;
	uint32 lastMapID;
	std::map<uint32, CCMMap*> maps;
	uint32 lastIdentityID;
	std::map<uint32, CCMIdentity*> identities;
	uint32 lastObjectID;
	std::map<uint32, CCMObject*> objects;
	uint32 lastImageID;
	std::map<uint32, CCMImage*> images;

	std::map<std::string, CCMRole*> roles;
	std::map<uint64, CCMTask*> tasks;
	uint64 lastTaskID;

	std::map<uint32, RemoteSystemEntry*> remoteSystems;
};




} // namespace cmlabs

#include "CCMCatalog.tpl.h"

#endif // _CCMCATALOG_H_
