#if defined _easyjson_included
 #endinput
#endif
#define _easyjson_included

enum JSONType
{
	Type_String,
	Type_Integer,
	Type_Float,
	Type_Object,
	Type_Boolean,
	Type_Array
}

stock Handle:CreateJSON()
{
	new Handle:m_hJSON = CreateTrie();
	new Handle:m_hJSONKeys = CreateArray(512);
	SetTrieValue(m_hJSON, "json_key_array", m_hJSONKeys);
	return m_hJSON;
}

stock Handle:DecodeJSON(const String:json[], &len=0)
{
	decl m_iPos, m_iIgnore;
	new m_iLength = strlen(json);

	m_iPos = JSONIgnore(json);
	if(m_iPos == -1 || json[m_iPos] != '{')
		return INVALID_HANDLE;
	++m_iPos;
	m_iIgnore = JSONIgnore(json[m_iPos]);
	if(m_iIgnore == -1)
		return INVALID_HANDLE;
	m_iPos += m_iIgnore;
	if(json[m_iPos] == '}')
		return INVALID_HANDLE;

	decl m_iStringStart;
	decl m_iStringEnd;

	new bool:m_bEnd = false;

	new Handle:m_hJSON = CreateJSON();
	while(m_iPos<m_iLength && json[m_iPos] != '}' && !m_bEnd)
	{
		if(GetJSONString(json[m_iPos], m_iStringStart, m_iStringEnd))
		{
			new String:m_szKey[m_iStringEnd-m_iStringStart+1];
			strcopy(m_szKey, m_iStringEnd-m_iStringStart+1, json[m_iPos+m_iStringStart]);
			m_iPos += m_iStringEnd+1;
			m_iPos += JSONIgnore(json[m_iPos]);

			if(json[m_iPos] == '{')
			{
				new m_iLen = -1;
				new Handle:m_hObject = DecodeJSON(json[m_iPos], m_iLen);
				if(m_hObject != INVALID_HANDLE)
				{
					JSONSetObject(m_hJSON, m_szKey, m_hObject);
					m_iPos += m_iLen;
					m_iPos += JSONIgnore(json[m_iPos]);
				}
				else
				{
					DestroyJSON(m_hJSON);
					return INVALID_HANDLE;
				}
			} else if(json[m_iPos] == '[')
			{
				new m_iLen = -1;
				new Handle:m_hObject = DecodeArray(json[m_iPos], m_iLen);
				if(m_hObject != INVALID_HANDLE)
				{
					JSONSetArray(m_hJSON, m_szKey, m_hObject);
					m_iPos += m_iLen;
					m_iPos += JSONIgnore(json[m_iPos]);
				}
				else
				{
					DestroyJSON(m_hJSON);
					return INVALID_HANDLE;
				}
			} else if(GetJSONString(json[m_iPos], m_iStringStart, m_iStringEnd))
			{
				new String:m_szValue[m_iStringEnd-m_iStringStart+1];
				strcopy(m_szValue, m_iStringEnd-m_iStringStart+1, json[m_iPos+m_iStringStart]);

				if(json[m_iPos+m_iStringEnd]=='}')
					m_bEnd = true;
				m_iPos += m_iStringEnd+1;
				m_iPos += JSONIgnore(json[m_iPos]);

				new m_iDot = 0;

				if(m_iStringStart != 0)
					JSONSetString(m_hJSON, m_szKey, m_szValue);
				else if(strcmp(m_szValue, "true")==0)
					JSONSetBoolean(m_hJSON, m_szKey, true);
				else if(strcmp(m_szValue, "false")==0)
					JSONSetBoolean(m_hJSON, m_szKey, false);
				else if(JSONIsNumeric(m_szValue))
					JSONSetInteger(m_hJSON, m_szKey, StringToInt(m_szValue));
				else if((m_iDot = FindCharInString(m_szValue, '.'))!=-1)
				{
					m_szValue[m_iDot] = 0;
					if(JSONIsNumeric(m_szValue) && JSONIsNumeric(m_szValue[m_iDot+1]))
					{
						m_szValue[m_iDot] = '.';
						JSONSetFloat(m_hJSON, m_szKey, StringToFloat(m_szValue));
					}
				}
			}
		}
	}
	if(len != 0)
		len = m_iPos;

	return m_hJSON;
}

stock Handle:DecodeArray(const String:json[], &len=0)
{
	decl m_iPos, m_iIgnore;
	new m_iLength = strlen(json);

	m_iPos = JSONIgnore(json);
	if(m_iPos == -1 || json[m_iPos] != '[')
		return INVALID_HANDLE;
	++m_iPos;
	m_iIgnore = JSONIgnore(json[m_iPos]);
	if(m_iIgnore == -1)
		return INVALID_HANDLE;
	m_iPos += m_iIgnore;
	if(json[m_iPos] == ']')
		return INVALID_HANDLE;

	decl m_iStringStart;
	decl m_iStringEnd;

	new bool:m_bEnd = false;

	new Handle:m_hArray = CreateArray(1);
	while(m_iPos<m_iLength && json[m_iPos] != ']' && !m_bEnd)
	{
		m_iPos += JSONIgnore(json[m_iPos]);
		if(json[m_iPos] == '{')
		{
			new m_iLen = -1;
			new Handle:m_hObject = DecodeJSON(json[m_iPos], m_iLen);
			if(m_hObject != INVALID_HANDLE)
			{
				PushArrayCell(m_hArray, JSONCreateObject(m_hObject));
				m_iPos += m_iLen;
				m_iPos += JSONIgnore(json[m_iPos]);
			}
			else
			{
				DestroyJSONArray(m_hArray);
				return INVALID_HANDLE;
			}
		} else if(json[m_iPos] == '[')
		{
			new m_iLen = -1;
			new Handle:m_hObject = DecodeArray(json[m_iPos], m_iLen);
			if(m_hArray != INVALID_HANDLE)
			{
				PushArrayCell(m_hArray, JSONCreateArray(m_hObject));
				m_iPos += m_iLen;
				m_iPos += JSONIgnore(json[m_iPos]);
			}
			else
			{
				DestroyJSONArray(m_hArray);
				return INVALID_HANDLE;
			}
		} else if(GetJSONString(json[m_iPos], m_iStringStart, m_iStringEnd))
		{
			new String:m_szValue[m_iStringEnd-m_iStringStart+1];
			strcopy(m_szValue, m_iStringEnd-m_iStringStart+1, json[m_iPos+m_iStringStart]);

			if(json[m_iPos+m_iStringEnd]==']')
				m_bEnd = true;
			m_iPos += m_iStringEnd+1;
			m_iPos += JSONIgnore(json[m_iPos]);

			new m_iDot = 0;

			if(m_iStringStart != 0)
				PushArrayCell(m_hArray, JSONCreateString(m_szValue));
			else if(strcmp(m_szValue, "true")==0)
				PushArrayCell(m_hArray, JSONCreateBoolean(true));
			else if(strcmp(m_szValue, "false")==0)
				PushArrayCell(m_hArray, JSONCreateBoolean(false));
			else if(JSONIsNumeric(m_szValue))
				PushArrayCell(m_hArray, JSONCreateInteger(StringToInt(m_szValue)));
			else if((m_iDot = FindCharInString(m_szValue, '.'))!=-1)
			{
				m_szValue[m_iDot] = 0;
				if(JSONIsNumeric(m_szValue) && JSONIsNumeric(m_szValue[m_iDot+1]))
				{
					m_szValue[m_iDot] = '.';
					PushArrayCell(m_hArray, JSONCreateFloat(StringToFloat(m_szValue)));
				}
			}
		}
	}
	if(len != 0)
		len = m_iPos+1;

	return m_hArray;
}


stock EncodeJSON(&Handle:json, String:output[], maxlen, bool:beautify=true, &len=0, tabs=1)
{
	new Handle:m_hKeyArray = INVALID_HANDLE;
	if(!GetTrieValue(json, "json_key_array", m_hKeyArray))
		return;

	new m_iKeys = GetArraySize(m_hKeyArray);

	decl String:m_szKey[512];
	decl Handle:m_hObject;

	if(2 >= maxlen)
		return;

	new String:m_szTabs[tabs+1];
	if(beautify)
		for(new i=0;i<tabs;++i)
			m_szTabs[i] = '\t';

	new m_iPos = 1;
	output[0] = '{';
	if(beautify)
	{
		output[1] = '\n';
		++m_iPos;
	}
	for(new i=0;i<m_iKeys;++i)
	{
		GetArrayString(m_hKeyArray, i, m_szKey, sizeof(m_szKey));
		if(!GetTrieValue(json, m_szKey, m_hObject))
			continue;
		decl JSONType:m_eType;
		GetTrieValue(m_hObject, "type", m_eType);
		switch(m_eType)
		{
			case Type_String:
			{
				decl m_iLength;
				GetTrieValue(m_hObject, "size", m_iLength);
				decl String:m_szValue[m_iLength*2+1];
				GetTrieString(m_hObject, "value", m_szValue, m_iLength+1);
				ReplaceString(m_szValue, m_iLength*2+1, "\"", "\\\""); 
				m_iPos += Format(output[m_iPos], maxlen-m_iPos, "%s\"%s\": \"%s\"", m_szTabs, m_szKey, m_szValue);
			}

			case Type_Boolean:
			{
				decl bool:m_bValue;
				GetTrieValue(m_hObject, "value", m_bValue);
				if(m_bValue)
					m_iPos += Format(output[m_iPos], maxlen-m_iPos, "%s\"%s\": true", m_szTabs, m_szKey);
				else
					m_iPos += Format(output[m_iPos], maxlen-m_iPos, "%s\"%s\": false", m_szTabs, m_szKey);
			} 

			case Type_Integer:
			{
				decl m_iValue;
				GetTrieValue(m_hObject, "value", m_iValue);
				m_iPos += Format(output[m_iPos], maxlen-m_iPos, "%s\"%s\": %d", m_szTabs, m_szKey, m_iValue);
			}

			case Type_Float:
			{
				decl Float:m_fValue;
				GetTrieValue(m_hObject, "value", m_fValue);
				m_iPos += Format(output[m_iPos], maxlen-m_iPos, "%s\"%s\": %f", m_szTabs, m_szKey, m_fValue);
			}

			case Type_Object:
			{
				decl Handle:m_hValue;
				GetTrieValue(m_hObject, "value", m_hValue);
				m_iPos += Format(output[m_iPos], maxlen-m_iPos, "%s\"%s\": ", m_szTabs, m_szKey);

				new m_iLen = -1;
				EncodeJSON(m_hValue, output[m_iPos], maxlen-m_iPos, beautify, m_iLen, tabs+1);
				m_iPos += m_iLen;
			}

			case Type_Array:
			{
				decl Handle:m_hValue;
				GetTrieValue(m_hObject, "value", m_hValue);
				m_iPos += Format(output[m_iPos], maxlen-m_iPos, "%s\"%s\": ", m_szTabs, m_szKey);

				new m_iLen = -1;
				EncodeArray(m_hValue, output[m_iPos], maxlen-m_iPos, beautify, m_iLen, tabs+1);
				m_iPos += m_iLen+1;
			}

			default:
			{
				continue;
			}
		}
		if(m_iPos >= maxlen)
			return;
		if(m_iKeys != i+1)
		{
			output[m_iPos] = ',';
			++m_iPos;
		}
		if(m_iPos >= maxlen)
			return;
		if(beautify)
		{
			output[m_iPos] = '\n';
			++m_iPos;
		}
	}
	if(m_iPos < maxlen)
	{
		m_szTabs[tabs-1] = 0;
		m_iPos += Format(output[m_iPos], maxlen-m_iPos, "%s}", m_szTabs);
	}
	if(len != 0)
		len=m_iPos;
}

stock EncodeArray(&Handle:json, String:output[], maxlen, bool:beautify=true, &len=0, tabs=1)
{
	decl Handle:m_hObject;

	if(2 >= maxlen)
		return;

	new String:m_szTabs[tabs+1];
	if(beautify)
		for(new i=0;i<tabs;++i)
			m_szTabs[i] = '\t';

	new m_iPos = 1;
	output[0] = '[';
	if(beautify)
	{
		output[1] = '\n';
		++m_iPos;
	}

	new m_iKeys = GetArraySize(json);

	for(new i=0;i<m_iKeys;++i)
	{
		m_hObject = GetArrayCell(json, i);

		decl JSONType:m_eType;
		GetTrieValue(m_hObject, "type", m_eType);
		switch(m_eType)
		{
			case Type_String:
			{
				decl m_iLength;
				GetTrieValue(m_hObject, "size", m_iLength);
				decl String:m_szValue[m_iLength*2+1];
				GetTrieString(m_hObject, "value", m_szValue, m_iLength+1);
				ReplaceString(m_szValue, m_iLength*2+1, "\"", "\\\""); 
				m_iPos += Format(output[m_iPos], maxlen-m_iPos, "%s\"%s\"", m_szTabs, m_szValue);
			}

			case Type_Boolean:
			{
				decl bool:m_bValue;
				GetTrieValue(m_hObject, "value", m_bValue);
				if(m_bValue)
					m_iPos += Format(output[m_iPos], maxlen-m_iPos, "%strue", m_szTabs);
				else
					m_iPos += Format(output[m_iPos], maxlen-m_iPos, "%sfalse", m_szTabs);
			} 

			case Type_Integer:
			{
				decl m_iValue;
				GetTrieValue(m_hObject, "value", m_iValue);
				m_iPos += Format(output[m_iPos], maxlen-m_iPos, "%s%d", m_szTabs, m_iValue);
			}

			case Type_Float:
			{
				decl Float:m_fValue;
				GetTrieValue(m_hObject, "value", m_fValue);
				m_iPos += Format(output[m_iPos], maxlen-m_iPos, "%s%f", m_szTabs, m_fValue);
			}

			case Type_Object:
			{
				decl Handle:m_hValue;
				GetTrieValue(m_hObject, "value", m_hValue);
				m_iPos += Format(output[m_iPos], maxlen-m_iPos, "%s", m_szTabs);

				new m_iLen = -1;
				EncodeJSON(m_hValue, output[m_iPos], maxlen-m_iPos, beautify, m_iLen, tabs+1);
				m_iPos += m_iLen;
			}

			case Type_Array:
			{
				decl Handle:m_hValue;
				GetTrieValue(m_hObject, "value", m_hValue);
				m_iPos += Format(output[m_iPos], maxlen-m_iPos, "%s", m_szTabs);

				new m_iLen = -1;
				EncodeArray(m_hValue, output[m_iPos], maxlen-m_iPos, beautify, m_iLen, tabs+1);
				m_iPos += m_iLen;
			}
		}
		if(m_iPos >= maxlen)
			return;
		if(m_iKeys != i+1)
		{
			output[m_iPos] = ',';
			++m_iPos;
		}
		if(m_iPos >= maxlen)
			return;
		if(beautify)
		{
			output[m_iPos] = '\n';
			++m_iPos;
		}
	}
	if(m_iPos < maxlen)
	{
		m_szTabs[tabs-1] = 0;
		m_iPos += Format(output[m_iPos], maxlen-m_iPos, "%s]", m_szTabs);
	}
	if(len != 0)
		len=m_iPos-1;
}

stock JSONSetString(&Handle:json, const String:key[], const String:value[])
{
	new Handle:m_hKeyArray = INVALID_HANDLE;
	if(!GetTrieValue(json, "json_key_array", m_hKeyArray))
		return;
	PushArrayString(m_hKeyArray, key);

	new Handle:m_hObject = JSONCreateString(value);
	SetTrieValue(json, key, m_hObject);
}

stock JSONSetBoolean(&Handle:json, const String:key[], bool:value)
{
	new Handle:m_hKeyArray = INVALID_HANDLE;
	if(!GetTrieValue(json, "json_key_array", m_hKeyArray))
		return;
	PushArrayString(m_hKeyArray, key);

	new Handle:m_hObject = JSONCreateBoolean(value);
	SetTrieValue(json, key, m_hObject);
}

stock JSONSetInteger(&Handle:json, const String:key[], value)
{
	new Handle:m_hKeyArray = INVALID_HANDLE;
	if(!GetTrieValue(json, "json_key_array", m_hKeyArray))
		return;
	PushArrayString(m_hKeyArray, key);

	new Handle:m_hObject = JSONCreateInteger(value);
	SetTrieValue(json, key, m_hObject);
}

stock JSONSetFloat(&Handle:json, const String:key[], Float:value)
{
	new Handle:m_hKeyArray = INVALID_HANDLE;
	if(!GetTrieValue(json, "json_key_array", m_hKeyArray))
		return;
	PushArrayString(m_hKeyArray, key);

	new Handle:m_hObject = JSONCreateFloat(value);
	SetTrieValue(json, key, m_hObject);
}

stock JSONSetObject(&Handle:json, const String:key[], Handle:value)
{
	new Handle:m_hKeyArray = INVALID_HANDLE;
	if(!GetTrieValue(json, "json_key_array", m_hKeyArray))
		return;
	PushArrayString(m_hKeyArray, key);

	new Handle:m_hObject = JSONCreateObject(value);
	SetTrieValue(json, key, m_hObject);
}

stock JSONSetArray(&Handle:json, const String:key[], Handle:value)
{
	new Handle:m_hKeyArray = INVALID_HANDLE;
	if(!GetTrieValue(json, "json_key_array", m_hKeyArray))
		return;
	PushArrayString(m_hKeyArray, key);

	new Handle:m_hObject = JSONCreateArray(value);
	SetTrieValue(json, key, m_hObject);
}

stock Handle:JSONCreateString(const String:value[])
{
	new Handle:m_hObject = CreateTrie();
	SetTrieString(m_hObject, "value", value);
	SetTrieValue(m_hObject, "type", Type_String);
	SetTrieValue(m_hObject, "size", strlen(value));
	return m_hObject;
}

stock Handle:JSONCreateBoolean(bool:value)
{
	new Handle:m_hObject = CreateTrie();
	SetTrieValue(m_hObject, "value", value);
	SetTrieValue(m_hObject, "type", Type_Boolean);
	SetTrieValue(m_hObject, "size", sizeof(value));
	return m_hObject;
}

stock Handle:JSONCreateInteger(value)
{
	new Handle:m_hObject = CreateTrie();
	SetTrieValue(m_hObject, "value", value);
	SetTrieValue(m_hObject, "type", Type_Integer);
	SetTrieValue(m_hObject, "size", sizeof(value));
	return m_hObject;
}

stock Handle:JSONCreateFloat(Float:value)
{
	new Handle:m_hObject = CreateTrie();
	SetTrieValue(m_hObject, "value", value);
	SetTrieValue(m_hObject, "type", Type_Float);
	SetTrieValue(m_hObject, "size", sizeof(value));

	return m_hObject;
}

stock Handle:JSONCreateObject(Handle:value)
{
	new Handle:m_hObject = CreateTrie();
	SetTrieValue(m_hObject, "value", value);
	SetTrieValue(m_hObject, "type", Type_Object);
	SetTrieValue(m_hObject, "size", sizeof(value));
	return m_hObject;
}

stock Handle:JSONCreateArray(Handle:value)
{
	new Handle:m_hObject = CreateTrie();
	SetTrieValue(m_hObject, "value", value);
	SetTrieValue(m_hObject, "type", Type_Array);
	SetTrieValue(m_hObject, "size", sizeof(value));
	return m_hObject;
}

stock bool:JSONGetString(&Handle:json, const String:key[], String:out[], maxlen)
{
	decl Handle:m_hObject;
	if(!GetTrieValue(json, key, m_hObject))
		return false;
	decl JSONType:m_eType;
	GetTrieValue(m_hObject, "type", m_eType);
	if(m_eType != Type_String)
		return false;
	GetTrieString(m_hObject, "value", out, maxlen);
	return true;
}

stock bool:JSONGetObject(&Handle:json, const String:key[], &Handle:out)
{
	decl Handle:m_hObject;
	if(!GetTrieValue(json, key, m_hObject))
		return false;
	decl JSONType:m_eType;
	GetTrieValue(m_hObject, "type", m_eType);
	if(m_eType != Type_Object)
		return false;
	GetTrieValue(m_hObject, "value", out);
	return true;
}

stock bool:JSONGetArray(&Handle:json, const String:key[], &Handle:out)
{
	decl Handle:m_hObject;
	if(!GetTrieValue(json, key, m_hObject))
		return false;
	decl JSONType:m_eType;
	GetTrieValue(m_hObject, "type", m_eType);
	if(m_eType != Type_Array)
		return false;
	GetTrieValue(m_hObject, "value", out);
	return true;
}

stock bool:JSONGetBoolean(&Handle:json, const String:key[], &bool:value)
{
	decl Handle:m_hObject;
	if(!GetTrieValue(json, key, m_hObject))
		return false;
	decl JSONType:m_eType;
	GetTrieValue(m_hObject, "type", m_eType);
	if(m_eType != Type_Boolean)
		return false;
	GetTrieValue(m_hObject, "value", value);
	return true;
}

stock bool:JSONGetInteger(&Handle:json, const String:key[], &value)
{
	decl Handle:m_hObject;
	if(!GetTrieValue(json, key, m_hObject))
		return false;
	decl JSONType:m_eType;
	GetTrieValue(m_hObject, "type", m_eType);
	if(m_eType != Type_Integer)
		return false;
	GetTrieValue(m_hObject, "value", value);
	return true;
}

stock bool:JSONGetFloat(&Handle:json, const String:key[], &Float:value)
{
	decl Handle:m_hObject;
	if(!GetTrieValue(json, key, m_hObject))
		return false;
	decl JSONType:m_eType;
	GetTrieValue(m_hObject, "type", m_eType);
	// For compatibility reasons only, but you should follow strict types
	if(m_eType != Type_Float && m_eType != Type_Integer)
		return false;
	if(m_eType == Type_Integer)
	{
		decl m_iValue;
		GetTrieValue(m_hObject, "value", m_iValue);
		value = float(m_iValue);
	}
	else
		GetTrieValue(m_hObject, "value", value);
	return true;
}

stock bool:JSONGetArrayString(&Handle:array, idx, String:out[], maxlen)
{
	new Handle:m_hObject = GetArrayCell(array, idx);
	decl JSONType:m_eType;
	GetTrieValue(m_hObject, "type", m_eType);
	if(m_eType != Type_String)
		return false;
	GetTrieString(m_hObject, "value", out, maxlen);
	return true;
}

stock bool:JSONGetArrayObject(&Handle:array, idx, &Handle:out)
{
	new Handle:m_hObject = GetArrayCell(array, idx);
	decl JSONType:m_eType;
	GetTrieValue(m_hObject, "type", m_eType);
	if(m_eType != Type_Object)
		return false;
	GetTrieValue(m_hObject, "value", out);
	return true;
}

stock bool:JSONGetArrayArray(&Handle:array, idx, &Handle:out)
{
	new Handle:m_hObject = GetArrayCell(array, idx);
	decl JSONType:m_eType;
	GetTrieValue(m_hObject, "type", m_eType);
	if(m_eType != Type_Array)
		return false;
	GetTrieValue(m_hObject, "value", out);
	return true;
}

stock bool:JSONGetArrayBoolean(&Handle:array, idx, &bool:value)
{
	new Handle:m_hObject = GetArrayCell(array, idx);
	decl JSONType:m_eType;
	GetTrieValue(m_hObject, "type", m_eType);
	if(m_eType != Type_Boolean)
		return false;
	GetTrieValue(m_hObject, "value", value);
	return true;
}

stock bool:JSONGetArrayInteger(&Handle:array, idx, &value)
{
	new Handle:m_hObject = GetArrayCell(array, idx);
	decl JSONType:m_eType;
	GetTrieValue(m_hObject, "type", m_eType);
	if(m_eType != Type_Integer)
		return false;
	GetTrieValue(m_hObject, "value", value);
	return true;
}

stock bool:JSONGetArrayFloat(&Handle:array, idx, &Float:value)
{
	new Handle:m_hObject = GetArrayCell(array, idx);
	decl JSONType:m_eType;
	GetTrieValue(m_hObject, "type", m_eType);
	// For compatibility reasons only, but you should follow strict types
	if(m_eType != Type_Float && m_eType != Type_Integer)
		return false;
	if(m_eType == Type_Integer)
	{
		decl m_iValue;
		GetTrieValue(m_hObject, "value", m_iValue);
		value = float(m_iValue);
	}
	else
		GetTrieValue(m_hObject, "value", value);
	return true;
}

stock JSONIgnore(const String:json[])
{
	new m_iLength = strlen(json);
	for(new i=0;i<m_iLength;++i)
		if(json[i] != ' ' && json[i] != '\t' && json[i] != ':' && json[i] != '\n' && json[i] != '\r' && json[i] != ',')
			return i;
	return -1;
}

stock JSONIsNumeric(const String:string[])
{
	new m_iLength = strlen(string);
	new i = 0;
	if(string[0] == '-')
		i = 1;
	for(;i<m_iLength;++i)
		if(!(48<=string[i]<=57))
			return false;
	return true;
}

stock bool:GetJSONString(const String:json[], &start, &end)
{
	decl m_iCharPos;

	new m_iPos = JSONIgnore(json);
	if(json[m_iPos] == '"')
		++m_iPos;
	start = m_iPos;
	if(json[m_iPos-1] == '"')
	{
		while((m_iCharPos = FindCharInString(json[m_iPos], '"')) != -1)
		{
			m_iPos += m_iCharPos;
			if(json[m_iPos-1] == '\\')
				continue;
			end = m_iPos;
			return true;
		}
	}

	new m_iLength = strlen(json);
	for(;m_iPos<m_iLength;++m_iPos)
	{
		if(m_iPos != 0 && !(48<=json[m_iPos]<=57) && !(97<=json[m_iPos]<=122) && json[m_iPos] != '-' && json[m_iPos] != '.')
		{
			end = m_iPos;
			return true;
		}
	}

	return false;
}

stock DestroyJSON(&Handle:json)
{
	new Handle:m_hKeyArray = INVALID_HANDLE;
	if(!GetTrieValue(json, "json_key_array", m_hKeyArray))
	{
		CloseHandle(json);
		return;
	}

	new m_iKeys = GetArraySize(m_hKeyArray);
	decl String:m_szKey[512];
	decl Handle:m_hObject;
	for(new i=0;i<m_iKeys;++i)
	{
		GetArrayString(m_hKeyArray, i, m_szKey, sizeof(m_szKey));
		if(!GetTrieValue(json, m_szKey, m_hObject))
			continue;
		decl JSONType:m_eType;
		GetTrieValue(m_hObject, "type", m_eType);
		if(m_eType == Type_Object)
		{
			new Handle:m_hValue;
			GetTrieValue(m_hObject, "value", m_hValue);
			DestroyJSON(m_hValue);
		} else if(m_eType == Type_Array)
		{
			new Handle:m_hValue;
			GetTrieValue(m_hObject, "value", m_hValue);
			DestroyJSONArray(m_hValue);
		}
		else
			CloseHandle(m_hObject);
	}

	CloseHandle(m_hKeyArray);
	CloseHandle(json);
}

stock DestroyJSONArray(&Handle:array)
{
	new m_iLength = GetArraySize(array);
	for(new i=0;i<m_iLength;++i)
	{
		new Handle:m_hItem;
		m_hItem = GetArrayCell(array, i);

		decl JSONType:m_eType;
		GetTrieValue(m_hItem, "type", m_eType);
		if(m_eType == Type_Object)
		{
			new Handle:m_hValue;
			GetTrieValue(m_hItem, "value", m_hValue);
			DestroyJSON(m_hValue);
		} else if(m_eType == Type_Array)
		{
			new Handle:m_hValue;
			GetTrieValue(m_hItem, "value", m_hValue);
			DestroyJSONArray(m_hValue);
		}
		else
			CloseHandle(m_hItem);
	}
	CloseHandle(array);
}