

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

#include "NetworkProtocols.h"
#include "xml_parser.h"
#include "jsmn.h"

namespace cmlabs {

struct RESTParam {
	enum Type { INTEGER, FLOAT, TEXT, ARRAY, IMAGE, VIDEO, BINARY } type;
	bool optional;
	bool allowEmpty;
	std::string name;
	std::string objects;
};

class RESTParser;

class RESTRequest {
	friend class RESTParser;
public:
	enum Status {FAILED_EMPTY = 0, SUCCESS = 1, FAILED_DESCRIPTION = 2, FAILED_UNKNOWN_REQUEST = 3, FAILED_FORMAT = 4, FAILED_PARAM = 5, FAILED_AUTH = 6, SUCCESS_OPTIONS = 7} status;

protected:
	std::string origin;
	std::string signature;
	uint64 createTime;
	DataMessage* storage;
	bool requireAuthentication;
	std::map<std::string, std::string> filterMap;
	std::string errorString;

	bool setStatus(Status status) {this->status = status; return true;}
	bool setOrigin(const char* origin) {this->origin = origin; return true;}
	bool setCreateTime(uint64 created) {this->createTime = created; return true;}
	bool setRequestSignature(const char* signature) {this->signature = signature; return true; }
	bool setRequireAuthentication(bool auth) {this->requireAuthentication = auth; return true; }

	bool setTime(const char* key, uint64 val) {return storage->setTime(key, val);}
	bool setInt(const char* key, int64 val) {return storage->setInt(key, val);}
	bool setDouble(const char* key, double val) {return storage->setDouble(key, val);}
	bool setString(const char* key, const char* val) {return storage->setString(key, val);}
	bool setBinary(const char* key, const char* data, uint32 size, const char* contentType) {
		if (!storage->setData(key, data, size))
			return false;
		return storage->setString(utils::StringFormat("%s_content_type_", key).c_str(), contentType);
	}

	bool addToErrorString(const char* errorLine) {errorString.length() ? errorString = utils::StringFormat("%s - %s", errorString.c_str(), errorLine) : errorString = errorLine; return true;}
	bool clearErrorState() { errorString.clear(); status=FAILED_EMPTY; return true; }

public:
	RESTRequest(DataMessage* msg);
	RESTRequest() {createTime=0; status=FAILED_EMPTY; storage = new DataMessage(); requireAuthentication = false; }
	virtual ~RESTRequest() {delete(storage);}

	Status getStatus() {return status;}
	const char* getSignature(bool& success) {success = (bool)signature.length(); return signature.c_str(); }

	const char* getOrigin(bool& success) {success = (bool)origin.length(); return origin.c_str(); }
	uint64 getCreateTime(bool& success) {success = (bool)createTime; return createTime; }

	uint64 getTime(const char* key, bool& success) {return storage->getTime(key, success);}
	const char* getString(const char* key, bool& success) {return storage->getString(key, success);}
	int64 getInt(const char* key, bool& success) {return storage->getInt(key, success);}
	double getDouble(const char* key, bool& success) {return storage->getDouble(key, success);}
	const char* getData(const char* key, uint32& size, bool& success) {return storage->getData(key, size, success);}
	char* getDataCopy(const char* key, uint32& size, bool& success) {return storage->getDataCopy(key, size, success);}
	bool getRequireAuthentication() {return this->requireAuthentication;}

	const char* getErrorString() {return errorString.c_str();}

	const char* getFilterString(const char* key, bool& success) { if (filterMap.find(key) == filterMap.end()) {success = false; return NULL; } else {success = true; return filterMap[key].c_str();} }
	int64 getFilterInt(const char* key, bool& success) { if (filterMap.find(key) == filterMap.end()) {success = false; return 0; } else {success = true; return utils::Ascii2Int64(filterMap[key].c_str());} }
	float64 getFilterFloat(const char* key, bool& success) { if (filterMap.find(key) == filterMap.end()) {success = false; return 0; } else {success = true; return utils::Ascii2Float64(filterMap[key].c_str());} }
	bool setFilter(const char* key, const char* val) {filterMap[key] = val; return true;}
	bool setFilter(const char* key, int64 val) {filterMap[key] = utils::StringFormat("%d", val); return true;}
	bool setFilter(const char* key, float64 val) {filterMap[key] = utils::StringFormat("%f", val); return true;}

	DataMessage* getStorageMessageCopy();
};


class RESTParser {
public:
	static bool UnitTest();

	RESTParser();
	virtual ~RESTParser();

	bool init(const char* restDefinition);

	RESTRequest* parseRequest(HTTPRequest* req);
	RESTRequest* parseRequest(DataMessage* msg);

	const char* getLastErrorString() {return lastErrorString.c_str(); }

protected:
	XMLNode mainNode;
	std::string lastErrorString;
	std::map<std::string, std::map<std::string, RESTParam> > paramSets;

	XMLNode findTopNodeForRequest(const char* requestString);
	std::map<std::string, RESTParam> parseParamSet(XMLNode objectNode);

	XMLNode parseRequest(const char* requestString, std::map<std::string, RESTParam> &paramSet, RESTRequest* restReq, std::string &signature);

	bool extractParameters(HTTPRequest* req, std::map<std::string, RESTParam> &paramSet, RESTRequest* restReq);
	bool extractParameters(DataMessage* msg, std::map<std::string, RESTParam> &paramSet, RESTRequest* restReq);

	bool extractArrayParameters(HTTPRequest* req, std::map<std::string, RESTParam> &paramSet, RESTRequest* restReq, int entryNum, const char* content, uint32 contentSize, const char* contentType);
	bool extractArrayParameters(DataMessage* msg, std::map<std::string, RESTParam> &paramSet, RESTRequest* restReq, int entryNum, const char* content, uint32 contentSize, const char* contentType);

	bool setParameterValue(HTTPRequest* req, RESTParam &param, std::string value, RESTRequest* restReq, uint32 entryNum = 0);
	bool setParameterValue(DataMessage* msg, RESTParam &param, std::string value, RESTRequest* restReq, uint32 entryNum = 0);
};


} // namespace cmlabs

#endif //_RESTAPI_H_

