#ifndef JSON_INCLUDED
#define JSON_INCLUDED

#ifdef _WIN32

typedef signed char int8_t;
typedef short int int16_t;
typedef int int32_t;
typedef long long int int64_t;
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long int uint64_t;

#else

#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#endif

#include <map>
#include <queue>
#include <vector>
#include <string>
#include <string.h>
#include <limits>
#include <algorithm>
#include <cinttypes>
using namespace std;



class JValue
{
public:
	enum TYPE
	{
		E_NULL = 0,
		E_INT,
		E_BOOL,
		E_FLOAT,
		E_ARRAY,
		E_OBJECT,
		E_STRING,
		E_DATE,
		E_DATA,
	};

public:
	JValue(TYPE type = E_NULL);
	JValue(int val);
	JValue(bool val);
	JValue(double val);
	JValue(int64_t val);
	JValue(const char* val);
	JValue(const string& val);
	JValue(const JValue& other);
	JValue(const char* val, size_t len);
	~JValue();

public:
	int asInt() const;
	bool asBool() const;
	double asFloat() const;
	int64_t asInt64() const;
	string asString() const;
	const char* asCString() const;
	time_t asDate() const;
	string asData() const;

	void assignData(const char* val, size_t size);
	void assignDate(time_t val);
	void assignDateString(time_t val);

	TYPE type() const;
	size_t size() const;
	void clear();

	JValue& at(int index);
	JValue& at(size_t index);
	JValue& at(const char* key);

	bool has(const char* key) const;
	int index(const char* ele) const;
	bool keys(vector<string>& arrKeys) const;

	bool join(JValue& jv);
	bool append(JValue& jv);

	bool remove(int index);
	bool remove(size_t index);
	bool remove(const char* key);

	JValue& back();
	JValue& front();

	bool push_back(int val);
	bool push_back(bool val);
	bool push_back(double val);
	bool push_back(int64_t val);
	bool push_back(const char* val);
	bool push_back(const string& val);
	bool push_back(const JValue& jval);
	bool push_back(const char* val, size_t len);

	bool isInt() const;
	bool isNull() const;
	bool isBool() const;
	bool isFloat() const;
	bool isArray() const;
	bool isObject() const;
	bool isString() const;
	bool isEmpty() const;
	bool isData() const;
	bool isDate() const;
	bool isDataString() const;
	bool isDateString() const;

	operator int() const;
	operator bool() const;
	operator double() const;
	operator int64_t() const;
	operator string() const;
	operator const char* () const;

	JValue& operator=(const JValue& other);

	JValue& operator[](int index);
	const JValue& operator[](int index) const;

	JValue& operator[](size_t index);
	const JValue& operator[](size_t index) const;

	JValue& operator[](int64_t index);
	const JValue& operator[](int64_t index) const;

	JValue& operator[](const char* key);
	const JValue& operator[](const char* key) const;

	JValue& operator[](const string& key);
	const JValue& operator[](const string& key) const;

	friend bool operator==(const JValue& jv, const char* psz)
	{
		return (0 == strcmp(jv.asCString(), psz));
	}

	friend bool operator==(const char* psz, const JValue& jv)
	{
		return (0 == strcmp(jv.asCString(), psz));
	}

	friend bool operator!=(const JValue& jv, const char* psz)
	{
		return (0 != strcmp(jv.asCString(), psz));
	}

	friend bool operator!=(const char* psz, const JValue& jv)
	{
		return (0 != strcmp(jv.asCString(), psz));
	}

private:
	void Free();
	char* NewString(const char* cstr);
	void CopyValue(const JValue& src);
	bool WriteDataToFile(const char* file, const char* data, size_t len);

public:
	static const JValue null;
	static const string nullData;

private:
	union HOLD
	{
		bool vBool;
		double vFloat;
		int64_t vInt64;
		char* vString;
		vector<JValue>* vArray;
		map<string, JValue>* vObject;
		time_t vDate;
		string* vData;
		wchar_t* vUnicode;
	} m_Value;

	TYPE m_eType;

public:
	string write() const;
	const char* write(string& strDoc) const;

	string styleWrite() const;
	const char* styleWrite(string& strDoc) const;

	bool read(const char* pdoc, string* pstrerr = NULL);
	bool read(const string& strdoc, string* pstrerr = NULL);

	string writePList() const;
	const char* writePList(string& strDoc) const;

	bool readPList(const string& strdoc, string* pstrerr = NULL);
	bool readPList(const char* pdoc, size_t len = 0, string* pstrerr = NULL);

	bool readFile(const char* file, string* pstrerr = NULL);
	bool readPListFile(const char* file, string* pstrerr = NULL);

	bool writeFile(const char* file);
	bool writePListFile(const char* file);
	bool styleWriteFile(const char* file);

	bool readPath(const char* path, ...);
	bool readPListPath(const char* path, ...);
	bool writePath(const char* path, ...);
	bool writePListPath(const char* path, ...);
	bool styleWritePath(const char* path, ...);
};

class JReader
{
public:
	bool parse(const char* pdoc, JValue& root);
	void error(string& strmsg) const;

private:
	struct Token
	{
		enum TYPE
		{
			E_Error = 0,
			E_End,
			E_Null,
			E_True,
			E_False,
			E_Number,
			E_String,
			E_ArrayBegin,
			E_ArrayEnd,
			E_ObjectBegin,
			E_ObjectEnd,
			E_ArraySeparator,
			E_MemberSeparator
		};
		TYPE type;
		const char* pbeg;
		const char* pend;
	};

	void skipSpaces();
	void skipComment();

	bool match(const char* pattern, int patternLength);

	bool readToken(Token& token);
	bool readValue(JValue& jval);
	bool readArray(JValue& jval);
	void readNumber();

	bool readString();
	bool readObject(JValue& jval);

	bool decodeNumber(Token& token, JValue& jval);
	bool decodeString(Token& token, string& decoded);
	bool decodeDouble(Token& token, JValue& jval);

	char GetNextChar();
	bool addError(const string& message, const char* ploc);

private:
	const char* m_pBeg;
	const char* m_pEnd;
	const char* m_pCur;
	const char* m_pErr;
	string m_strErr;
};

class JWriter
{
public:
	static void FastWrite(const JValue& jval, string& strDoc);
	static void FastWriteValue(const JValue& jval, string& strDoc);

public:
	const string& StyleWrite(const JValue& jval);

private:
	void PushValue(const string& strval);
	void StyleWriteValue(const JValue& jval);
	void StyleWriteArrayValue(const JValue& jval);
	bool isMultineArray(const JValue& jval);

public:
	static string v2s(double val);
	static string v2s(int64_t val);
	static string v2s(const char* val);

	static string vstring2s(const char* val);
	static string d2s(time_t t);

private:
	string m_strDoc;
	string m_strTab;
	bool m_bAddChild;
	vector<string> m_childValues;
};

//////////////////////////////////////////////////////////////////////////
class PReader
{
public:
	PReader();

public:
	bool parse(const char* pdoc, size_t len, JValue& root);
	void error(string& strmsg) const;

private:
	struct Token
	{
		enum TYPE
		{
			E_Error = 0,
			E_End,
			E_Null,
			E_True,
			E_False,
			E_Key,
			E_Data,
			E_Date,
			E_Integer,
			E_Real,
			E_String,
			E_ArrayBegin,
			E_ArrayEnd,
			E_ArrayNull,
			E_DictionaryBegin,
			E_DictionaryEnd,
			E_DictionaryNull,
			E_ArraySeparator,
			E_MemberSeparator
		};

		Token()
		{
			pbeg = NULL;
			pend = NULL;
			type = E_Error;
		}

		TYPE type;
		const char* pbeg;
		const char* pend;
	};

	bool readToken(Token& token);
	bool readLabel(string& label);
	bool readValue(JValue& jval, Token& token);
	bool readArray(JValue& jval);
	bool readNumber();

	bool readString();
	bool readDictionary(JValue& jval);

	void endLabel(Token& token, const char* szLabel);

	bool decodeNumber(Token& token, JValue& jval);
	bool decodeString(Token& token, string& decoded, bool filter = true);
	bool decodeDouble(Token& token, JValue& jval);

	void skipSpaces();
	bool addError(const string& message, const char* ploc);

public:
	bool parseBinary(const char* pbdoc, size_t len, JValue& pv);

private:
	uint32_t getUInt24FromBE(const char* v);
	void byteConvert(uint8_t* v, size_t size);
	uint64_t getUIntVal(const char* v, size_t size);
	bool readUIntSize(const char*& pcur, size_t& size);
	bool readBinaryValue(const char*& pcur, JValue& pv);
	bool readUnicode(const char* pcur, size_t size, JValue& pv);

public:
	static void XMLUnescape(string& strval);

private: //xml
	const char* m_pBeg;
	const char* m_pEnd;
	const char* m_pCur;
	const char* m_pErr;
	string m_strErr;

private: //binary
	const char* m_pTrailer;
	uint64_t m_uObjects;
	uint8_t m_uOffsetSize;
	const char* m_pOffsetTable;
	uint8_t m_uDictParamSize;
};

class PWriter
{
public:
	static void FastWrite(const JValue& pval, string& strdoc);
	static void FastWriteValue(const JValue& pval, string& strdoc, string& strindent);

public:
	static void XMLEscape(string& strval);
	static string& StringReplace(string& context, const string& from, const string& to);
};

#endif // JSON_INCLUDED
