

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

#include "DataMessage.h"

namespace cmlabs {

struct ObsEntry {
	std::string name;
	std::string type;
	std::string trigger;
};

struct cmpObsEntry {
	bool operator()(const ObsEntry& a, const ObsEntry& b) const {
		return (a.name == b.name);
	}
};

int64 ExtractIntFromMessage(const char* name, DataMessage* msg, std::map<std::string, std::string>& mappings, bool &ok);
float64 ExtractFloatFromMessage(const char* name, DataMessage* msg, std::map<std::string, std::string>& mappings, bool &ok);
uint64 ExtractTimeFromMessage(const char* name, DataMessage* msg, std::map<std::string, std::string>& mappings, bool &ok);
std::string ExtractStringFromMessage(const char* name, DataMessage* msg, std::map<std::string, std::string>& mappings, bool &ok);

struct ObsHeader {
	uint32 cid;
	uint32 size;
	uint64 timestamp;
	uint64 id;
	uint64 creatorID;
	uint64 objectID;
	uint64 topicID;
	uint32 type;
	char name[MAXVALUENAMELEN + 1];
	float64 confidence;
	float64 consensus;
};

#define OBSID_LOC 1
struct ObsLocation {
	float64 x, y, size;
	uint64 mapID;
	static uint32 getType() { return OBSID_LOC; }
	static std::string getTypeName() { return "Location";  }
	void init() { memset(this, 0, sizeof(*this)); }
	std::string print() { return utils::StringFormat("(%.3f,%.3f) [%.2f]", x, y, size); }
	std::string toJSON() { return utils::StringFormat(" \"x\": %.3f, \"y\": %.3f, \"size\": %.2f", x, y, size); }
	bool extractRangeFrom(DataMessage* msg) {
		bool ok;
		x = msg->getFloat("FromX", ok); if (!ok) return false;
		y = msg->getFloat("FromY", ok);
		return ok;
	}
	bool extractRangeTo(DataMessage* msg) {
		bool ok;
		x = msg->getFloat("ToX", ok);
		y = msg->getFloat("ToY", ok);
		return ok;
	}
	bool isBetween(ObsLocation& a, ObsLocation& b) {
		if (a.x <= b.x) {
			if ((x < a.x) || (x > b.x))
				return false;
			if (a.y <= b.y) {
				if ((y < a.y) || (y > b.y))
					return false;
			}
			else {
				if ((y < b.y) || (y > a.y))
					return false;
			}
		}
		else {
			if ((x < b.x) || (x > a.x))
				return false;
			if (a.y <= b.y) {
				if ((y < a.y) || (y > b.y))
					return false;
			}
			else {
				if ((y < b.y) || (y > a.y))
					return false;
			}
		}
		return true;
	}
	bool extractDataFromMessage(DataMessage* msg, std::map<std::string, std::string>& mappings) {
		bool ok;
		x = ExtractFloatFromMessage("x", msg, mappings, ok);
		if (!ok) return false;
		y = ExtractFloatFromMessage("y", msg, mappings, ok);
		if (!ok) return false;
		size = ExtractFloatFromMessage("size", msg, mappings, ok);
		mapID = (uint64)ExtractIntFromMessage("mapid", msg, mappings, ok);
		return true;
	}
};


#define OBSID_DIM 2
struct ObsDimension {
	float64 w, h, d;
	uint32 getType() { return OBSID_DIM; }
	static std::string getTypeName() { return "Dimension"; }
	void init() { memset(this, 0, sizeof(*this)); }
	std::string print() { return utils::StringFormat("(%.3f,%.3f,%.3f)", w, h, d); }
	std::string toJSON() { return utils::StringFormat(" \"w\": %.3f, \"h\": %.3f, \"d\": %.3f ", w, h, d); }
	bool extractRangeFrom(DataMessage* msg) {
		bool ok;
		w = msg->getFloat("FromW", ok); if (!ok) return false;
		h = msg->getFloat("FromH", ok); if (!ok) return false;
		d = msg->getFloat("FromD");
		return true;
	}
	bool extractRangeTo(DataMessage* msg) {
		bool ok;
		w = msg->getFloat("ToW", ok); if (!ok) return false;
		h = msg->getFloat("ToH", ok); if (!ok) return false;
		d = msg->getFloat("ToD");
		return ok;
	}
	bool isBetween(ObsDimension& a, ObsDimension& b) {
		float64 vol, volA, volB;
		if (this->d) {
			// consider depth
			vol = w * h * d;
			volA = a.w * a.h * a.d;
			volB = b.w * b.h * b.d;
		}
		else {
			// ignore depth
			vol = w * h;
			volA = a.w * a.h;
			volB = b.w * b.h;
		}
		if (volA <= volB) {
			if ((vol < volA) || (vol > volB))
				return false;
		}
		else {
			if ((vol < volB) || (vol > volA))
				return false;
		}
		return true;
	}
	bool extractDataFromMessage(DataMessage* msg, std::map<std::string, std::string>& mappings) {
		bool ok;
		w = ExtractFloatFromMessage("w", msg, mappings, ok);
		if (!ok) return false;
		h = ExtractFloatFromMessage("h", msg, mappings, ok);
		if (!ok) return false;
		d = ExtractFloatFromMessage("d", msg, mappings, ok);
		return true;
	}
};


#define OBSID_INT 3
struct ObsInteger {
	int64 val;
	uint32 getType() { return OBSID_INT; }
	static std::string getTypeName() { return "Integer"; }
	void init() { memset(this, 0, sizeof(*this)); }
	std::string print() { return utils::StringFormat("%lld", val); }
	std::string toJSON() { return utils::StringFormat(" \"value\": %lld ", val); }
	bool extractRangeFrom(DataMessage* msg) {
		bool ok;
		val = msg->getInt("FromVal", ok);
		return ok;
	}
	bool extractRangeTo(DataMessage* msg) {
		bool ok;
		val = msg->getInt("ToVal", ok);
		return ok;
	}
	bool isBetween(ObsInteger& a, ObsInteger& b) {
		if (a.val <= b.val)
			return ((val < a.val) || (val > b.val));
		else
			return ((val < b.val) || (val > a.val));
	}
	bool extractDataFromMessage(DataMessage* msg, std::map<std::string, std::string>& mappings) {
		bool ok;
		val = ExtractIntFromMessage("val", msg, mappings, ok);
		return ok;
	}
};

#define OBSID_FLOAT 4
struct ObsFloat {
	float64 val;
	uint32 getType() { return OBSID_FLOAT; }
	static std::string getTypeName() { return "Float"; }
	void init() { memset(this, 0, sizeof(*this)); }
	std::string print() { return utils::StringFormat("%.3f", val); }
	std::string toJSON() { return utils::StringFormat(" \"value\": %.3f ", val); }
	bool extractRangeFrom(DataMessage* msg) {
		bool ok;
		val = msg->getFloat("FromVal", ok);
		return ok;
	}
	bool extractRangeTo(DataMessage* msg) {
		bool ok;
		val = msg->getFloat("ToVal", ok);
		return ok;
	}
	bool isBetween(ObsFloat& a, ObsFloat& b) {
		if (a.val <= b.val)
			return ((val < a.val) || (val > b.val));
		else
			return ((val < b.val) || (val > a.val));
	}
	bool extractDataFromMessage(DataMessage* msg, std::map<std::string, std::string>& mappings) {
		bool ok;
		val = ExtractFloatFromMessage("val", msg, mappings, ok);
		return ok;
	}
};

#define OBSID_TIME 5
struct ObsTime {
	uint64 val;
	uint32 getType() { return OBSID_TIME; }
	static std::string getTypeName() { return "Time"; }
	void init() { memset(this, 0, sizeof(*this)); }
	std::string print() { return utils::StringFormat("%s", PrintTime(val)); }
	std::string toJSON() { return utils::StringFormat(" \"time\": %llu, \"timetext\": \"%s\" ", val, PrintTimeString(val).c_str()); }
	bool extractRangeFrom(DataMessage* msg) {
		bool ok;
		val = msg->getTime("FromVal", ok);
		return ok;
	}
	bool extractRangeTo(DataMessage* msg) {
		bool ok;
		val = msg->getTime("ToVal", ok);
		return ok;
	}
	bool isBetween(ObsTime& a, ObsTime& b) {
		if (a.val <= b.val)
			return ((val < a.val) || (val > b.val));
		else
			return ((val < b.val) || (val > a.val));
	}
	bool extractDataFromMessage(DataMessage* msg, std::map<std::string, std::string>& mappings) {
		bool ok;
		val = ExtractTimeFromMessage("val", msg, mappings, ok);
		return ok;
	}
};

#define OBSID_STRING 6
struct ObsString {
	char val[1024];
	uint32 getType() { return OBSID_STRING; }
	static std::string getTypeName() { return "String"; }
	void init() { memset(this, 0, sizeof(*this)); }
	std::string print() { return utils::StringFormat("%s", val); }
	std::string toJSON() { return utils::StringFormat(" \"value\": \"%s\" ", val); }
	bool extractRangeFrom(DataMessage* msg) {
		bool ok;
		const char* str = msg->getString("FromVal", ok);
		if (!ok) return false;
		utils::strcpyavail(val, str, 1023, true);
		return true;
	}
	bool extractRangeTo(DataMessage* msg) {
		bool ok;
		const char* str = msg->getString("ToVal", ok);
		if (!ok) return false;
		utils::strcpyavail(val, str, 1023, true);
		return true;
	}

	bool isBetween(ObsString& a, ObsString& b) {
		int64 comp = stricmp(a.val, b.val);
		if (comp <= 0) {
			if ((stricmp(val, a.val) < 0) || (stricmp(val, b.val) > 0))
				return false;
		}
		else {
			if ((stricmp(val, b.val) < 0) || (stricmp(val, a.val) > 0))
				return false;
		}
		return true;
	}
	bool extractDataFromMessage(DataMessage* msg, std::map<std::string, std::string>& mappings) {
		bool ok;
		std::string str = ExtractStringFromMessage("val", msg, mappings, ok);
		if (!ok) return false;
		utils::strcpyavail(val, str.c_str(), 1023, true);
		return true;
	}
};

#define OBSID_COUNT 6

uint32 GetObsTypeFromRawData(const char* data, uint32 size);

template <typename T>
class GenericObservation {
public:
	static std::list<GenericObservation<T>*> ReadAllFromMessage(DataMessage* msg);
	static bool WriteAllToMessage(DataMessage* msg, std::list<GenericObservation<T>*>& list);

	GenericObservation();
	GenericObservation(const char* data, uint32 size, uint64 id = 0);
	~GenericObservation();

	ObsHeader header;
	T data;

	std::string print();
	std::string toJSON();

	bool extractDataFromMessage(DataMessage* msg, std::map<std::string, std::string>& mappings);

	bool writeToMessage(DataMessage* msg, uint64 id);
	bool readFromMessage(DataMessage* msg, uint64 id = 0);
	bool readFromData(const char* data, uint32 size, uint64 id = 0);

	//bool buildFromQuery(uint8 queryType, std::string& query);
	bool isBetween(GenericObservation<T>* a, GenericObservation<T>* b);
};


} // namespace cmlabs

#include "Observations.tpl.h"

#endif //_OBSERVATIONS_H_

