

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  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(_SUBSCRIPTIONS_H_)
#define _SUBSCRIPTIONS_H_

#include "PsyTime.h"
#include "Utils.h"
#include "HTML.h"
#include "ObjectIDs.h"
#include "xml_parser.h"
#include "DataMessage.h"

// Component types
#define COMP_WHITEBOARD		1
#define COMP_CATALOG		2
#define COMP_STREAM			3
#define COMP_FEED			4
#define COMP_SERVICE		5
#define COMP_MODULE			6
#define COMP_INTERNAL		6
#define COMP_EXTERNAL		7

// Migration types
#define COMP_MIGRATE_NO		0
#define COMP_MIGRATE_ALLOW	1


namespace cmlabs {

#define TRIGGERFILTER_HASKEY			1
#define TRIGGERFILTER_EQUALS			2
#define TRIGGERFILTER_NOTEQUALS			3
#define TRIGGERFILTER_EQUALSNUMERIC		4
#define TRIGGERFILTER_NOTEQUALSNUMERIC	5
#define TRIGGERFILTER_GREATERTHAN		6
#define TRIGGERFILTER_LESSTHAN			7
#define TRIGGERFILTER_MAXAGE			8

#define INDEX_TIME		1
#define INDEX_STRING	2
#define INDEX_INTEGER	3
#define INDEX_FLOAT		4

struct FilterSpec {
	uint8 filter;
	uint64 time;
	char key[MAXKEYNAMELEN+1];
	char value[MAXVALUENAMELEN+1];
	std::string toXML() {
		return utils::StringFormat("\t<filter filter=\"%u\" time=\"%llu\" key=\"%s\" value=\"%s\" />\n",
			filter, time, key, value);
	}
	bool fromXML(const char* xml) {
		if (!xml) return false;
		XMLResults xmlResults;
		XMLNode node = XMLNode::parseString(xml, "filter", &xmlResults);
		if (xmlResults.error != eXMLErrorNone)
			return false;
		if (node.isEmpty()) return false;
		return fromXML(node);
	}
	bool fromXML(XMLNode &node) {
		const char* str;
		if (str = node.getAttribute("filter")) filter = (uint8)utils::Ascii2Uint32(str); else return false;
		if (str = node.getAttribute("time")) time = utils::Ascii2Uint64(str); else return false;
		if (str = node.getAttribute("key")) utils::strcpyavail(key, str, MAXKEYNAMELEN, true); else return false;
		if (str = node.getAttribute("value")) utils::strcpyavail(value, str, MAXVALUENAMELEN, true); else return false;
		return true;
	}
};

struct QuerySpec {
	uint32 source;
	uint32 origin;
	uint64 maxage;
	uint32 maxcount;
	PsyContext context;
	bool binary;
	uint32 ipHost;
	uint16 ipPort;
	char sourceName[MAXKEYNAMELEN+1];
	char hostName[MAXKEYNAMELEN + 1];
	char name[MAXKEYNAMELEN+1];
	char key[MAXKEYNAMELEN+1];
	char value[MAXVALUENAMELEN+1];
	char type[MAXKEYNAMELEN+1];
	char subdir[MAXKEYNAMELEN+1];
	char ext[MAXKEYNAMELEN+1];
	char operation[MAXKEYNAMELEN+1];
	std::string toXML() {
		return utils::StringFormat("\t<query source=\"%u\" origin=\"%u\" maxage=\"%llu\" maxcount=\"%u\" binary=\"%s\" name=\"%s\" key=\"%s\" value=\"%s\" type=\"%s\" subdir=\"%s\" ext=\"%s\" operation=\"%s\" sourcename=\"%s\" hostname=\"%s\" iphost=\"%u\" port=\"%u\" />\n",
			source, origin, maxage, maxcount, (binary ? "yes" : "no"), name, key, value, type, subdir, ext, operation,
			sourceName, hostName, ipHost, ipPort);
	}
	bool fromXML(const char* xml) {
		if (!xml) return false;
		XMLResults xmlResults;
		XMLNode node = XMLNode::parseString(xml, "query", &xmlResults);
		if (xmlResults.error != eXMLErrorNone)
			return false;
		if (node.isEmpty()) return false;
		return fromXML(node);
	}
	bool fromXML(XMLNode &node) {
		const char* str;
		if (str = node.getAttribute("source")) source = utils::Ascii2Uint32(str); else return false;
		if (str = node.getAttribute("origin")) origin = utils::Ascii2Uint32(str); else return false;
		if (str = node.getAttribute("maxage")) maxage = utils::Ascii2Uint64(str); else return false;
		if (str = node.getAttribute("maxcount")) maxcount = utils::Ascii2Uint32(str); else return false;
		if (str = node.getAttribute("context")) { if (!context.fromString(str)) return false; } else return false;
		if (str = node.getAttribute("binary")) binary = (stricmp(str, "yes") == 0); else return false;
		if (str = node.getAttribute("name")) utils::strcpyavail(name, str, MAXKEYNAMELEN, true); else return false;
		if (str = node.getAttribute("key")) utils::strcpyavail(key, str, MAXKEYNAMELEN, true); else return false;
		if (str = node.getAttribute("value")) utils::strcpyavail(value, str, MAXVALUENAMELEN, true); else return false;
		if (str = node.getAttribute("type")) utils::strcpyavail(type, str, MAXKEYNAMELEN, true); else return false;
		if (str = node.getAttribute("subdir")) utils::strcpyavail(subdir, str, MAXKEYNAMELEN, true); else return false;
		if (str = node.getAttribute("ext")) utils::strcpyavail(ext, str, MAXKEYNAMELEN, true); else return false;
		if (str = node.getAttribute("operation")) utils::strcpyavail(operation, str, MAXKEYNAMELEN, true); else return false;
		if (str = node.getAttribute("sourcename")) utils::strcpyavail(sourceName, str, MAXKEYNAMELEN, true); else return false;
		if (str = node.getAttribute("hostname")) utils::strcpyavail(hostName, str, MAXKEYNAMELEN, true); else return false;
		if (str = node.getAttribute("iphost")) ipHost = utils::Ascii2Uint32(str); else return false;
		if (str = node.getAttribute("ipport")) ipPort = utils::Ascii2Uint32(str); else return false;
		return true;
	}
};

struct RetrieveSpec {
	uint32 source;
	uint32 origin;
	uint64 maxage;
	uint32 maxcount;
	PsyType type;
	PsyContext context;
	uint32 tag;
	char name[MAXKEYNAMELEN+1];
	char key[MAXKEYNAMELEN+1];
	uint8 keytype;
	uint32 from;
	uint32 to;
	uint64 startTime;
	uint64 endTime;
	int64 startInt;
	int64 endInt;
	float64 startFloat;
	float64 endFloat;
	char startString[MAXVALUENAMELEN+1];
	char endString[MAXVALUENAMELEN+1];
	std::string toXML() {
		return utils::StringFormat("\t<retrieve source=\"%u\" origin=\"%u\" maxage=\"%llu\" maxcount=\"%u\" type=\"%s\" context=\"%s\" tag=\"%u\" name=\"%s\" key=\"%s\" keytype=\"%u\" from=\"%u\" to=\"%u\" starttime=\"%llu\" endtime=\"%llu\" startint=\"%lld\" endint=\"%lld\" startfloat=\"%f\" endfloat=\"%f\" startstring=\"%s\" endstring=\"%s\" />\n",
			source, origin, maxage, maxcount, type.toString().c_str(), context.toString().c_str(),
			tag, name, key, keytype, from, to,
			startTime, endTime, startInt, endInt, startFloat, endFloat,	startString, endString);
	}
	bool fromXML(const char* xml) {
		if (!xml) return false;
		XMLResults xmlResults;
		XMLNode node = XMLNode::parseString(xml, "retrieve", &xmlResults);
		if (xmlResults.error != eXMLErrorNone)
			return false;
		if (node.isEmpty()) return false;
		return fromXML(node);
	}
	bool fromXML(XMLNode &node) {
		const char* str;
		if (str = node.getAttribute("source")) source = utils::Ascii2Uint32(str); else return false;
		if (str = node.getAttribute("origin")) origin = utils::Ascii2Uint32(str); else return false;
		if (str = node.getAttribute("maxage")) maxage = utils::Ascii2Uint64(str); else return false;
		if (str = node.getAttribute("maxcount")) maxcount = utils::Ascii2Uint32(str); else return false;
		if (str = node.getAttribute("type")) { if (!type.fromString(str)) return false; } else return false;
		if (str = node.getAttribute("context")) { if (!context.fromString(str)) return false; } else return false;
		if (str = node.getAttribute("tag")) tag = utils::Ascii2Uint32(str); else return false;
		if (str = node.getAttribute("name")) utils::strcpyavail(name, str, MAXKEYNAMELEN, true); else return false;
		if (str = node.getAttribute("key")) utils::strcpyavail(key, str, MAXKEYNAMELEN, true); else return false;
		if (str = node.getAttribute("keytype")) keytype = (uint8)utils::Ascii2Uint32(str); else return false;
		if (str = node.getAttribute("from")) from = utils::Ascii2Uint32(str); else return false;
		if (str = node.getAttribute("to")) to = utils::Ascii2Uint32(str); else return false;
		if (str = node.getAttribute("starttime")) startTime = utils::Ascii2Uint64(str); else return false;
		if (str = node.getAttribute("endtime")) endTime = utils::Ascii2Uint64(str); else return false;
		if (str = node.getAttribute("startint")) startInt = utils::Ascii2Int64(str); else return false;
		if (str = node.getAttribute("endint")) endInt = utils::Ascii2Int64(str); else return false;
		if (str = node.getAttribute("startfloat")) startFloat = utils::Ascii2Float64(str); else return false;
		if (str = node.getAttribute("endfloat")) endFloat = utils::Ascii2Float64(str); else return false;
		if (str = node.getAttribute("startstring")) utils::strcpyavail(startString, str, MAXVALUENAMELEN, true); else return false;
		if (str = node.getAttribute("endstring")) utils::strcpyavail(endString, str, MAXVALUENAMELEN, true); else return false;
		return true;
	}
};

#define MESSAGE_NON_GUARANTEED	1

struct PostSpec {
	uint32 to;
	PsyType type;
	PsyContext context;
	PsyContext contextchange;
	uint32 tag;
	uint64 ttl;
	uint8 policy;
	uint32 contentCount;
	char name[MAXKEYNAMELEN+1];
//	char contentKey[MAXKEYNAMELEN + 1];
	char content[MAXCONTENTLEN + 1];
	std::string toXML() {
		if (contentCount) {
			// ||S||key||val||I||key||val||S||key||val
			const char* contentType, *key, *value;
			std::string strContent;
			std::vector<std::string> entries = utils::TextListSplit(content, "||", false, true);
			uint32 slotCount = 3 * contentCount;
			uint32 n = 0;
			if (entries.size() == slotCount) {
				while (n < slotCount) {
					contentType = entries[n++].c_str();
					key = entries[n++].c_str();
					value = entries[n++].c_str();
					if (strcmp(contentType, "S") == 0) {
						strContent += utils::StringFormat("\t\t<content type=\"string\" key=\"%s\" value=\"%s\" />\n",
							html::EncodeHTML(key).c_str(), html::EncodeHTML(value).c_str());
					}
					else if (strcmp(contentType, "I") == 0) {
						strContent += utils::StringFormat("\t\t<content type=\"integer\" key=\"%s\" value=\"%s\" />\n",
							html::EncodeHTML(key).c_str(), html::EncodeHTML(value).c_str());
					}
					else if (strcmp(contentType, "F") == 0) {
						strContent += utils::StringFormat("\t\t<content type=\"float\" key=\"%s\" value=\"%s\" />\n",
							html::EncodeHTML(key).c_str(), html::EncodeHTML(value).c_str());
					}
				}
			}
			return utils::StringFormat("\t<post to=\"%u\" type=\"%s\" context=\"%s\" contextchange=\"%s\" tag=\"%u\" ttl=\"%llu\" policy=\"%u\" name=\"%s\">\n%s</post>\n",
				to, type.toString().c_str(), context.toString().c_str(), contextchange.toString().c_str(), tag, ttl, policy, name, strContent.c_str());
		}
		else {
			return utils::StringFormat("\t<post to=\"%u\" type=\"%s\" context=\"%s\" contextchange=\"%s\" tag=\"%u\" ttl=\"%llu\" policy=\"%u\" name=\"%s\" />\n",
				to, type.toString().c_str(), context.toString().c_str(), contextchange.toString().c_str(), tag, ttl, policy, name);
		}
	}
	bool fromXML(const char* xml) {
		if (!xml) return false;
		XMLResults xmlResults;
		XMLNode node = XMLNode::parseString(xml, "post", &xmlResults);
		if (xmlResults.error != eXMLErrorNone)
			return false;
		if (node.isEmpty()) return false;
		return fromXML(node);
	}
	bool fromXML(XMLNode &node) {
		const char* str;
		if (str = node.getAttribute("to")) to = utils::Ascii2Uint32(str); else return false;
		if (str = node.getAttribute("type")) { if (!type.fromString(str)) return false; }
		else return false;
		if (str = node.getAttribute("context")) { if (!context.fromString(str)) return false; }
		else return false;
		if (str = node.getAttribute("contextchange")) { if (!contextchange.fromString(str)) return false; }
		else return false;
		if (str = node.getAttribute("tag")) tag = utils::Ascii2Uint32(str); else return false;
		if (str = node.getAttribute("ttl")) ttl = utils::Ascii2Uint64(str); else return false;
		if (str = node.getAttribute("policy")) policy = (uint8)utils::Ascii2Uint32(str); else return false;
		if (str = node.getAttribute("name")) utils::strcpyavail(name, str, MAXKEYNAMELEN, true); else return false;
		contentFromXML(node);
		return true;
	}
	bool contentFromXML(XMLNode &node) {
		std::string strContent;
		const char* str, *key, *value;
		if ((key = node.getAttribute("contentkey")) && (value = node.getAttribute("content"))) {
			strContent = utils::StringFormat("%s||S||%s||%s", content, html::DecodeHTML(key).c_str(), html::DecodeHTML(value).c_str());
			utils::strcpyavail(content, strContent.c_str(), MAXCONTENTLEN, false);
			contentCount++;
		}

		XMLNode subNode;
		uint32 i, count = node.nChildNode("content");
		for (i = 0; i < count; i++) {
			if (!(subNode = node.getChildNode("content", i)).isEmpty()) {
				if (!(key = subNode.getAttribute("key")) || !strlen(key))
					continue;
				if ( (!(value = subNode.getAttribute("content")) || !strlen(value)) &&
					(!(value = subNode.getAttribute("value")) || !strlen(value)) )
					continue;
				str = subNode.getAttribute("type");
				if (str && stricmp(str, "integer") == 0)
					strContent = utils::StringFormat("%s||I||%s||%s", content, html::DecodeHTML(key).c_str(), html::DecodeHTML(value).c_str());
				else if (str && ((stricmp(str, "double") == 0) || (stricmp(str, "float") == 0)))
					strContent = utils::StringFormat("%s||F||%s||%s", content, html::DecodeHTML(key).c_str(), html::DecodeHTML(value).c_str());
				else
					strContent = utils::StringFormat("%s||S||%s||%s", content, html::DecodeHTML(key).c_str(), html::DecodeHTML(value).c_str());
				utils::strcpyavail(content, strContent.c_str(), MAXCONTENTLEN, false);
				contentCount++;
			}
		}
		return true;
	}
	uint32 addContentToMsg(DataMessage* msg) {
		if (!contentCount)
			return 0;
		uint32 count = 0;
		const char* contentType, *key, *value;
		std::string strContent;
		std::vector<std::string> entries = utils::TextListSplit(content, "||", false, true);
		uint32 slotCount = 3 * contentCount, n = 0;
		if (entries.size() == slotCount) {
			while (n < slotCount) {
				contentType = entries[n++].c_str();
				key = entries[n++].c_str();
				value = entries[n++].c_str();
				if (strcmp(contentType, "S") == 0)
					msg->setString(key, value);
				else if (strcmp(contentType, "I") == 0)
					msg->setInt(key, utils::Ascii2Int64(value));
				else if (strcmp(contentType, "F") == 0)
					msg->setFloat(key, utils::Ascii2Float64(value));
				count++;
			}
		}
		return count;
	}
};

struct SignalSpec {
	PsyType type;
	char name[MAXKEYNAMELEN+1];
	std::string toXML() {
		return utils::StringFormat("\t<signal type=\"%s\" name=\"%s\" />\n", type.toString().c_str(), name);
	}
	bool fromXML(const char* xml) {
		if (!xml) return false;
		XMLResults xmlResults;
		XMLNode node = XMLNode::parseString(xml, "signal", &xmlResults);
		if (xmlResults.error != eXMLErrorNone)
			return false;
		if (node.isEmpty()) return false;
		return fromXML(node);
	}
	bool fromXML(XMLNode &node) {
		const char* str;
		if (str = node.getAttribute("type")) { if (!type.fromString(str)) return false; } else return false;
		if (str = node.getAttribute("name")) utils::strcpyavail(name, str, MAXKEYNAMELEN, true); else return false;
		return true;
	}
};

struct TriggerSpec {
	bool reset(uint32 s = 0) {
		memset(this, 0, sizeof(TriggerSpec));
		size = (s) ? s : sizeof(TriggerSpec);
		cid = TRIGGERDATA;
		return true;
	}
	FilterSpec* getFilterSpec(uint32 n) {
		if (n >= filterCount) return NULL;
		return (FilterSpec*)
			(((char*)this) + sizeof(TriggerSpec) + (n*sizeof(FilterSpec)));
	}
	RetrieveSpec* getRetrieveSpec(uint32 n) {
		if (n >= retrieveCount) return NULL;
		return (RetrieveSpec*)
			(((char*)this) + sizeof(TriggerSpec) + (filterCount*sizeof(FilterSpec)) + (n*sizeof(RetrieveSpec)));
	}
	RetrieveSpec* getRetrieveSpec(const char* name);
	QuerySpec* getQuerySpec(uint32 n) {
		if (n >= queryCount) return NULL;
		return (QuerySpec*)
			(((char*)this) + sizeof(TriggerSpec) + (filterCount*sizeof(FilterSpec)) +
			(retrieveCount*sizeof(RetrieveSpec)) + (n*sizeof(QuerySpec)));
	}
	QuerySpec* getQuerySpec(const char* name);
	std::list<PostSpec*>* getPostSpecs(const char* name);
	std::list<PostSpec*>* getPostSpecs(PsyType type);
	PostSpec* getPostSpec(const char* name);
	PostSpec* getPostSpec(PsyType type);
	PostSpec* getPostSpec(uint32 n) {
		if (n >= postCount) return NULL;
		return (PostSpec*)
			(((char*)this) + sizeof(TriggerSpec) + (filterCount*sizeof(FilterSpec)) +
			(retrieveCount*sizeof(RetrieveSpec)) + (queryCount*sizeof(QuerySpec)) + 
			(n * sizeof(PostSpec)));
	}
	SignalSpec* getSignalSpec(const char* name);
	SignalSpec* getSignalSpec(PsyType type);
	SignalSpec* getSignalSpec(uint32 n) {
		if (n >= signalCount) return NULL;
		return (SignalSpec*)
			(((char*)this) + sizeof(TriggerSpec) + (filterCount*sizeof(FilterSpec)) +
			(retrieveCount*sizeof(RetrieveSpec)) + (queryCount*sizeof(QuerySpec)) +
			(postCount*sizeof(PostSpec)) + (n * sizeof(SignalSpec)));
	}
	std::string toXML();

	uint32 size;
	uint32 cid;
	uint32 id;
	uint32 componentID;
	uint32 interval;
	uint32 from;
	uint32 to;
	uint32 tag;
	uint32 delay;
	uint8 grouppolicy;
	uint32 groupparam;
	uint16 crankID;
	uint32 group;
	PsyType type;
	PsyContext context;
	PsyContext triggerContext;
	char name[MAXKEYNAMELEN+1];
	uint16 filterCount;
	uint16 retrieveCount;
	uint16 queryCount;
	uint16 postCount;
	uint16 signalCount;
	// l x FilterSpec
	// n x RetrieveSpec
	// m x PostSpec
};

struct ComponentSetup {
	bool reset() {
		memset(this, 0, sizeof(ComponentSetup));
		size = sizeof(ComponentSetup);
		cid = COMPONENTSETUPDATA;
		return true;
	}
	uint32 size;
	uint32 cid;
	uint32 componentID;
	uint16 spaceID;
	uint32 tag;
	uint8 type;
	char name[MAXKEYNAMELEN+1];
	char logfile[MAXKEYNAMELEN+1];
	uint8 verbose;
	uint8 debug;
	uint8 migration;
	uint8 priority;
	char function[MAXKEYNAMELEN+1];
	char root[MAXKEYNAMELEN+1]; // dir or URL
	bool rotate;
	uint8 consoleOutput;
	uint8 autoRestart;

	// Whiteboard, Stream, Catalog, Service, Feed
	char subdir[MAXKEYNAMELEN+1];
	char extension[MAXKEYNAMELEN+1];	// filetype
	char base[MAXKEYNAMELEN+1];		// filename base
	char keys[MAXVALUENAMELEN+1];
	char value[MAXVALUENAMELEN+1];
	char operation[MAXKEYNAMELEN+1];
	uint32 timeout;

	// Recording & Playback
	uint32 maxcount;
	uint64 maxsize;
	uint32 interval;

	// Executable
	char cmdline[MAXKEYNAMELEN+1];

	// Setup
	char setup[1024*1024]; // 1MB max
};

// Local only, not transmitted

struct CustomView {
	uint32 compID;
	char name[MAXKEYNAMELEN+1];
	char templateURL[MAXKEYNAMELEN+1];
};

class ComponentSpec {
public:
	ComponentSpec() {
		setup = new ComponentSetup;
		setup->reset();
	};
	~ComponentSpec() {
		delete(setup);
		setup = NULL;
		while (triggers.size()) {
			delete(triggers.front());
			triggers.pop_front();
		}
	};

	ComponentSetup* setup;
	std::list<TriggerSpec*> triggers;
	std::list<CustomView> customViews;
};

} // namespace cmlabs

#endif //_SUBSCRIPTIONS_H_

