

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  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.
//_/_/
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
#include "jsmn.h"

using namespace cmlabs;

/**
 * Allocates a fresh unused token from the token pull.
 */
static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
		jsmntok_t *tokens, size_t num_tokens) {
	jsmntok_t *tok;
	if (parser->toknext >= num_tokens) {
		return NULL;
	}
	tok = &tokens[parser->toknext++];
	tok->start = tok->end = -1;
	tok->size = 0;
#ifdef JSMN_PARENT_LINKS
	tok->parent = -1;
#endif
	return tok;
}

/**
 * Fills token type and boundaries.
 */
static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
                            int start, int end) {
	token->type = type;
	token->start = start;
	token->end = end;
	token->size = 0;
}

/**
 * Fills next available token with JSON primitive.
 */
static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
		size_t len, jsmntok_t *tokens, size_t num_tokens) {
	jsmntok_t *token;
	int start;

	start = parser->pos;

	for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
		switch (js[parser->pos]) {
#ifndef JSMN_STRICT
			/* In strict mode primitive must be followed by "," or "}" or "]" */
			case ':':
#endif
			case '\t' : case '\r' : case '\n' : case ' ' :
			case ','  : case ']'  : case '}' :
				goto found;
		}
		if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
			parser->pos = start;
			return JSMN_ERROR_INVAL;
		}
	}
#ifdef JSMN_STRICT
	/* In strict mode primitive must be followed by a comma/object/array */
	parser->pos = start;
	return JSMN_ERROR_PART;
#endif

found:
	if (tokens == NULL) {
		parser->pos--;
		return 0;
	}
	token = jsmn_alloc_token(parser, tokens, num_tokens);
	if (token == NULL) {
		parser->pos = start;
		return JSMN_ERROR_NOMEM;
	}
	jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
#ifdef JSMN_PARENT_LINKS
	token->parent = parser->toksuper;
#endif
	parser->pos--;
	return 0;
}

/**
 * Filsl next token with JSON string.
 */
static int jsmn_parse_string(jsmn_parser *parser, const char *js,
		size_t len, jsmntok_t *tokens, size_t num_tokens) {
	jsmntok_t *token;

	int start = parser->pos;

	parser->pos++;

	/* Skip starting quote */
	for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
		char c = js[parser->pos];

		/* Quote: end of string */
		if (c == '\"') {
			if (tokens == NULL) {
				return 0;
			}
			token = jsmn_alloc_token(parser, tokens, num_tokens);
			if (token == NULL) {
				parser->pos = start;
				return JSMN_ERROR_NOMEM;
			}
			jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
#ifdef JSMN_PARENT_LINKS
			token->parent = parser->toksuper;
#endif
			return 0;
		}

		/* Backslash: Quoted symbol expected */
		if (c == '\\' && parser->pos + 1 < len) {
			int i;
			parser->pos++;
			switch (js[parser->pos]) {
				/* Allowed escaped symbols */
				case '\"': case '/' : case '\\' : case 'b' :
				case 'f' : case 'r' : case 'n'  : case 't' :
					break;
				/* Allows escaped symbol \uXXXX */
				case 'u':
					parser->pos++;
					for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
						/* If it isn't a hex character we have an error */
						if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
									(js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
									(js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
							parser->pos = start;
							return JSMN_ERROR_INVAL;
						}
						parser->pos++;
					}
					parser->pos--;
					break;
				/* Unexpected symbol */
				default:
					parser->pos = start;
					return JSMN_ERROR_INVAL;
			}
		}
	}
	parser->pos = start;
	return JSMN_ERROR_PART;
}

/**
 * Parse JSON string and fill tokens.
 */
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
		jsmntok_t *tokens, unsigned int num_tokens) {
	int r;
	int i;
	jsmntok_t *token;
	int count = parser->toknext;

	for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
		char c;
		jsmntype_t type;

		c = js[parser->pos];
		switch (c) {
			case '{': case '[':
				count++;
				if (tokens == NULL) {
					break;
				}
				token = jsmn_alloc_token(parser, tokens, num_tokens);
				if (token == NULL)
					return JSMN_ERROR_NOMEM;
				if (parser->toksuper != -1) {
					tokens[parser->toksuper].size++;
#ifdef JSMN_PARENT_LINKS
					token->parent = parser->toksuper;
#endif
				}
				token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
				token->start = parser->pos;
				parser->toksuper = parser->toknext - 1;
				break;
			case '}': case ']':
				if (tokens == NULL)
					break;
				type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
#ifdef JSMN_PARENT_LINKS
				if (parser->toknext < 1) {
					return JSMN_ERROR_INVAL;
				}
				token = &tokens[parser->toknext - 1];
				for (;;) {
					if (token->start != -1 && token->end == -1) {
						if (token->type != type) {
							return JSMN_ERROR_INVAL;
						}
						token->end = parser->pos + 1;
						parser->toksuper = token->parent;
						break;
					}
					if (token->parent == -1) {
						break;
					}
					token = &tokens[token->parent];
				}
#else
				for (i = parser->toknext - 1; i >= 0; i--) {
					token = &tokens[i];
					if (token->start != -1 && token->end == -1) {
						if (token->type != type) {
							return JSMN_ERROR_INVAL;
						}
						parser->toksuper = -1;
						token->end = parser->pos + 1;
						break;
					}
				}
				/* Error if unmatched closing bracket */
				if (i == -1) return JSMN_ERROR_INVAL;
				for (; i >= 0; i--) {
					token = &tokens[i];
					if (token->start != -1 && token->end == -1) {
						parser->toksuper = i;
						break;
					}
				}
#endif
				break;
			case '\"':
				r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
				if (r < 0) return r;
				count++;
				if (parser->toksuper != -1 && tokens != NULL)
					tokens[parser->toksuper].size++;
				break;
			case '\t' : case '\r' : case '\n' : case ' ':
				break;
			case ':':
				parser->toksuper = parser->toknext - 1;
				break;
			case ',':
				if (tokens != NULL && parser->toksuper != -1 &&
						tokens[parser->toksuper].type != JSMN_ARRAY &&
						tokens[parser->toksuper].type != JSMN_OBJECT) {
#ifdef JSMN_PARENT_LINKS
					parser->toksuper = tokens[parser->toksuper].parent;
#else
					for (i = parser->toknext - 1; i >= 0; i--) {
						if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
							if (tokens[i].start != -1 && tokens[i].end == -1) {
								parser->toksuper = i;
								break;
							}
						}
					}
#endif
				}
				break;
#ifdef JSMN_STRICT
			/* In strict mode primitives are: numbers and booleans */
			case '-': case '0': case '1' : case '2': case '3' : case '4':
			case '5': case '6': case '7' : case '8': case '9':
			case 't': case 'f': case 'n' :
				/* And they must not be keys of the object */
				if (tokens != NULL && parser->toksuper != -1) {
					jsmntok_t *t = &tokens[parser->toksuper];
					if (t->type == JSMN_OBJECT ||
							(t->type == JSMN_STRING && t->size != 0)) {
						return JSMN_ERROR_INVAL;
					}
				}
#else
			/* In non-strict mode every unquoted value is a primitive */
			default:
#endif
				r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
				if (r < 0) return r;
				count++;
				if (parser->toksuper != -1 && tokens != NULL)
					tokens[parser->toksuper].size++;
				break;

#ifdef JSMN_STRICT
			/* Unexpected char in strict mode */
			default:
				return JSMN_ERROR_INVAL;
#endif
		}
	}

	if (tokens != NULL) {
		for (i = parser->toknext - 1; i >= 0; i--) {
			/* Unmatched opened object or array */
			if (tokens[i].start != -1 && tokens[i].end == -1) {
				return JSMN_ERROR_PART;
			}
		}
	}

	return count;
}

/**
 * Creates a new parser based over a given  buffer with an array of tokens
 * available.
 */
void jsmn_init(jsmn_parser *parser) {
	parser->pos = 0;
	parser->toknext = 0;
	parser->toksuper = -1;
}

int GetJSONTokenLevel(jsmntok_t *tokens, int startToken) {
	if (!tokens || startToken < 0) return 0;
	int level = 1;
	int n = startToken;
	while (tokens[n].parent >= 0) {
		n = tokens[n].parent;
		level++;
	}
	return level;
}

int GetJSONToken(jsmntok_t* tokens, int tokenCount, const char* json, const char* key, int parent, jsmntype_t type) {
	if (!tokens || !tokenCount || !key) return -1;

	int inst = 0;
	int keyLen = (int)strlen(key);
	if (!keyLen) return -1;

	for (int n = 0; n<tokenCount - 1; n++) {
		if (type == JSMN_UNDEFINED || tokens[n].type == type) {
			if (parent != tokens[n].parent)
				continue;
			if (keyLen == tokens[n].end - tokens[n].start && strncmp(json + tokens[n].start, key, keyLen) == 0) {
				return n+1;
			}
		}
	}
	return -1;
}

const char* GetJSONValue(jsmntok_t* tokens, int tokenCount, const char* json, int token, int &valLength) {
	valLength = 0;
	if (!tokens || !tokenCount || (token < 0) || (token >= tokenCount)) return NULL;
	valLength = tokens[token].end - tokens[token].start;
	return json + tokens[token].start;
}

std::string GetJSONValueString(jsmntok_t* tokens, int tokenCount, const char* json, int token) {
	int len = 0;
	const char* str = GetJSONValue(tokens, tokenCount, json, token, len);
	if (!str || !len)
		return "";
	return std::string(str, len);
}

int64 GetJSONValueInt64(jsmntok_t* tokens, int tokenCount, const char* json, int token) {
	return utils::Ascii2Int64(GetJSONValueString(tokens, tokenCount, json, token).c_str());
}

uint64 GetJSONValueUint64(jsmntok_t* tokens, int tokenCount, const char* json, int token) {
	return utils::Ascii2Uint64(GetJSONValueString(tokens, tokenCount, json, token).c_str());
}

float64 GetJSONValueFloat64(jsmntok_t* tokens, int tokenCount, const char* json, int token) {
	return utils::Ascii2Float64(GetJSONValueString(tokens, tokenCount, json, token).c_str());
}


const char* GetJSONValue(jsmntok_t* tokens, int tokenCount, const char* json, const char* key, jsmntype_t type, int &valLength, int level, int instance) {
	valLength = 0;
	if (!tokens || !tokenCount || !key) return NULL;

	int inst = 0;
	int keyLen = (int) strlen(key);
	if (!keyLen) return NULL;

	for (int n=0; n<tokenCount-1; n++) {
		if (type == JSMN_UNDEFINED || tokens[n].type == type) {
			if (keyLen == tokens[n].end - tokens[n].start && strncmp(json + tokens[n].start, key, keyLen) == 0) {
				if (level && GetJSONTokenLevel(tokens, n) != level)
					continue;
				if (instance && (++inst) != instance)
					continue;
				valLength = tokens[n+1].end - tokens[n+1].start;
				return json + tokens[n+1].start;
			}
		}
	}
	return NULL;
}

const char* GetJSONChildValue(jsmntok_t* tokens, int tokenCount, const char* json, const char* key, int parent, jsmntype_t type, int &valLength) {
	valLength = 0;
	if (!tokens || !tokenCount || !key) return NULL;

	int inst = 0;
	int keyLen = (int)strlen(key);
	if (!keyLen) return NULL;

	for (int n = 0; n<tokenCount - 1; n++) {
		if (type == JSMN_UNDEFINED || tokens[n].type == type) {
			if (parent != tokens[n].parent)
				continue;
			if (keyLen == tokens[n].end - tokens[n].start && strncmp(json + tokens[n].start, key, keyLen) == 0) {
				valLength = tokens[n + 1].end - tokens[n + 1].start;
				return json + tokens[n + 1].start;
			}
		}
	}
	return NULL;
}


std::string GetJSONValueString(jsmntok_t* tokens, int tokenCount, const char* json, const char* key, jsmntype_t type, int level, int instance) {
	int len = 0;
	const char* str = GetJSONValue(tokens, tokenCount, json, key, type, len, level, instance);
	if (!str || !len)
		return "";
	return std::string(str, len);
}

int64 GetJSONValueInt64(jsmntok_t* tokens, int tokenCount, const char* json, const char* key, jsmntype_t type, int level, int instance) {
	return utils::Ascii2Int64(GetJSONValueString(tokens, tokenCount, json, key, type, level, instance).c_str());
}

uint64 GetJSONValueUint64(jsmntok_t* tokens, int tokenCount, const char* json, const char* key, jsmntype_t type, int level, int instance) {
	return utils::Ascii2Uint64(GetJSONValueString(tokens, tokenCount, json, key, type, level, instance).c_str());
}

float64 GetJSONValueFloat64(jsmntok_t* tokens, int tokenCount, const char* json, const char* key, jsmntype_t type, int level, int instance) {
	return utils::Ascii2Float64(GetJSONValueString(tokens, tokenCount, json, key, type, level, instance).c_str());
}


std::string GetJSONChildValueString(jsmntok_t* tokens, int tokenCount, const char* json, const char* key, int parent, jsmntype_t type) {
	int len = 0;
	const char* str = GetJSONChildValue(tokens, tokenCount, json, key, parent, type, len);
	if (!str || !len)
		return "";
	return std::string(str, len);
}

int64 GetJSONChildValueInt64(jsmntok_t* tokens, int tokenCount, const char* json, const char* key, int parent, jsmntype_t type) {
	return utils::Ascii2Int64(GetJSONChildValueString(tokens, tokenCount, json, key, parent, type).c_str());
}

uint64 GetJSONChildValueUint64(jsmntok_t* tokens, int tokenCount, const char* json, const char* key, int parent, jsmntype_t type) {
	return utils::Ascii2Uint64(GetJSONChildValueString(tokens, tokenCount, json, key, parent, type).c_str());
}

float64 GetJSONChildValueFloat64(jsmntok_t* tokens, int tokenCount, const char* json, const char* key, int parent, jsmntype_t type) {
	return utils::Ascii2Float64(GetJSONChildValueString(tokens, tokenCount, json, key, parent, type).c_str());
}




std::vector<int> GetJSONArrayIndexes(jsmntok_t* tokens, int tokenCount, const char* json, const char* key, jsmntype_t type, int level, int instance) {
	std::vector<int> indexes;

	if (!tokens || !tokenCount || !key) return indexes;

	int inst = 0;
	int keyLen = (int) strlen(key);
	if (!keyLen) return indexes;

	for (int n=0; n<tokenCount-1; n++) {
		if (type == JSMN_UNDEFINED || tokens[n].type == type) {
			if (keyLen == tokens[n].end - tokens[n].start && strncmp(json + tokens[n].start, key, keyLen) == 0) {
				if (level && GetJSONTokenLevel(tokens, n) != level)
					continue;
				if (instance && (++inst) != instance)
					continue;
				// We now need to find all nodes who's parent is n+1
				for (int m=n+2; m<tokenCount-1; m++) {
					if (tokens[m].parent == n+1)
						indexes.push_back(m);
				}
				return indexes;
			}
		}
	}
	return indexes;
}

std::vector<int> GetJSONChildArrayIndexes(jsmntok_t* tokens, int tokenCount, const char* json, const char* key, int parent, jsmntype_t type) {
	std::vector<int> indexes;

	if (!tokens || !tokenCount || !key) return indexes;

	int inst = 0;
	int keyLen = (int)strlen(key);
	if (!keyLen) return indexes;

	for (int n = 0; n<tokenCount - 1; n++) {
		if (type == JSMN_UNDEFINED || tokens[n].type == type) {
			if (parent != tokens[n].parent)
				continue;
			if (keyLen == tokens[n].end - tokens[n].start && strncmp(json + tokens[n].start, key, keyLen) == 0) {
				// We now need to find all nodes who's parent is n+1
				for (int m = n + 2; m<tokenCount - 1; m++) {
					if (tokens[m].parent == n + 1)
						indexes.push_back(m);
				}
				return indexes;
			}
		}
	}
	return indexes;
}


std::vector<int> GetJSONChildArrayIndexes(jsmntok_t* tokens, int tokenCount, const char* json, int parent, jsmntype_t type) {
	std::vector<int> indexes;

	if (!tokens || !tokenCount) return indexes;

	if ((parent < 0) || (parent >= tokenCount)) return indexes;

	int inst = 0;

	for (int n = 0; n<tokenCount - 1; n++) {
		if (type == JSMN_UNDEFINED || tokens[n].type == type) {
			if (parent == tokens[n].parent+1) {
				indexes.push_back(n);
			}
		}
	}
	return indexes;
}
