#ifndef _LJSON_H_
#define _LJSON_H_

#include "lgi/common/Array.h"
#include "lgi/common/StringClass.h"

#define SkipWs(s) while (*s && strchr(" \t\r\n", *s)) s++;

class LJson
{
	struct Key
	{
		LString Name;
		
		LString Str;
		LArray<Key> Obj;
		LArray<Key> Array;

		Key *Get(const char *name, bool create = false)
		{
			if (Name.Equals(name))
				return this;
			for (unsigned i=0; i<Obj.Length(); i++)
			{
				if (Obj[i].Get(name))
					return Obj.AddressOf(i);
			}
			if (create)
			{
				char *ArrStart = strchr(name, '[');
				char *ArrEnd = ArrStart ? strchr(ArrStart+1, ']') : NULL;
				if (ArrStart && ArrEnd)
				{
					int Idx = atoi(ArrStart + 1);
					if (Idx < 0)
						return NULL;

					LString n;
					n.Set(name, ArrStart - name);
					if (n.IsEmpty())
					{
						return &Array[Idx];
					}

					for (unsigned i=0; i<Obj.Length(); i++)
					{
						if (Obj[i].Get(n))
							return &(Obj[i].Array[Idx]);
					}

					Key &k = Obj.New();
					k.Name = n;
					return &k.Array[Idx];
				}

				Key &k = Obj.New();
				k.Name = name;
				return &k;
			}
			return NULL;
		}

		void Empty()
		{
			Name.Empty();
			Str.Empty();
			Obj.Length(0);
		}

		LString Print(int Depth = 0)
		{
			LStringPipe r(512);
			LString s;
			LString d("");
			if (Depth)
			{
				int bytes = Depth << 2;
				d.Length(bytes);
				memset(d.Get(), ' ', bytes);
				d.Get()[bytes] = 0;
			}

			if (Name)
			{
				s.Printf("%s\"%s\": ", d.Get(), Name.Get());
				r += s;
			}
			if (Str)
			{
				LString q = LString::Escape(Str, Str.Length(), "\"\\");
				s.Printf("\"%s\"", q.Get());
				r += s;
			}

			if (Obj.Length())
			{
				// if (Name)
				{
					s.Printf("{\n");
					Depth++;
				}
				
				for (unsigned i=0; i<Obj.Length(); i++)
				{
					if (i)
						s += ",\n";
					Key &c = Obj[i];
					s += c.Print(Depth);
				}
				r += s;
				
				// if (Name)
				{
					s.Printf("\n%s}", d.Get());
					r += s;
					Depth--;
				}
			}
			else if (!Str)
			{
				r += "[ ";
				for (unsigned i=0; i<Array.Length(); i++)
				{
					Key &k = Array[i];
					if (i)
						r += ", ";
					/*
					if (k.Obj.Length())
						s = LString("{") + k.Print() + "}";
					else
					*/
						s = k.Print();
					r += s;
				}
				r += " ]";
			}

			return r.NewGStr();
		}

		Key *Deref(LString Addr, bool Create)
		{
			LString::Array p = Addr.SplitDelimit(".");
			Key *k = this;
			for (unsigned i=0; k && i<p.Length(); i++)
			{
				auto n = p[i];
				k = k->Get(n, Create);
			}
			return k;
		}
	};

	Key Root;
	const char *Start;

	bool ParseString(LString &s, const char *&c)
	{
		SkipWs(c);

		if (*c != '\"')
			return false;

		c++;
		const char *e = c;
		while (*e)
		{
			if (*e == '\\')
				e += 2;
			else if (*e == '\"')
				break;
			else
				e++;
		}
		if (*e != '\"')
			return false;
		s.Set(c, e - c);
		c = e + 1;
		return true;
	}

	bool ParseChar(char ch, const char *&c)
	{
		SkipWs(c);
		if (*c != ch)
			return false;
		c++;
		SkipWs(c);
		return true;
	}

	bool IsNumeric(char s)
	{
		return IsDigit(s) || strchr("-.e", s) != NULL;
	}

	bool ParseArray(LArray<Key> &Array, const char *&c)
	{
		if (*c != '[')
		{
			LAssert(0);
			return false;
		}

		c++;
		SkipWs(c);
		while (*c != ']')
		{
			auto Idx = Array.Length();
			Key &a = Array[Idx];
			if (!Parse(a, c))
			{
				Array.Length(Idx); // Delete failed object
				return false;
			}

			SkipWs(c);
			if (*c == ',')
			{
				c++;
				SkipWs(c);
			}
		}
		if (*c != ']')
		{
			LAssert(!"Unexpected token.");
			return false;
		}

		c++;
		return true;
	}

	// Value parser: can be one of:
	// - Object
	// - Array
	// - String
	// - Number
	// - Boolean (true or false)
	// - NULL
	bool Parse(Key &k, const char *&c)
	{
		SkipWs(c);

		if (*c == '\"')
		{
			// String
			return ParseString(k.Str, c);
		}
		else if (*c == '{')
		{
			// Objects
			c++;
			SkipWs(c);
			while (*c && *c != '}')
			{
				LString n;
				if (!ParseString(n, c))
					return false;
				if (!ParseChar(':', c))
					return false;
				
				auto Idx = k.Obj.Length();
				auto &Member = k.Obj[Idx];
				Member.Name = n;
				if (!Parse(Member, c))
				{
					k.Obj.Length(Idx); // Delete incomplete obj.
					return false;
				}

				SkipWs(c);
				if (*c == ',')
				{
					c++;
					SkipWs(c);
				}
			}

			if (*c == '}')
			{
				c++;
				return true;
			}
		}
		else if (*c == '[')
		{
			// Parse array
			return ParseArray(k.Array, c);
		}
		else if (IsNumeric(*c))
		{
			const char *e = c;
			while (*e && IsNumeric(*e))
				e++;
			k.Str.Set(c, e - c);
			c = e;
			return true;
		}
		else if (IsAlpha(*c))
		{
			// Boolean?
			const char *e = c;
			while (*e && IsAlpha(*e))
				e++;
			k.Str.Set(c, e - c);
			c = e;
			return true;
		}
		else if (!*c)
			return true;

		return false;
	}

	Key *Deref(LString Addr, bool Create)
	{
		return Root.Deref(Addr, Create);
	}

public:
	LJson(const char *c = NULL)
	{
		Start = NULL;
		if (c)
			SetJson(c);
	}

	void Empty()
	{
		Root.Empty();
	}

	bool SetJson(const char *c)
	{
		Empty();
		if (!c)
			return false;

		Start = c;
		bool b = Parse(Root, c);
		if (!b)
			LgiTrace("%s:%i - Error at char " LPrintfSizeT "\n", _FL, c - Start);
		return b;
	}

	LString GetJson()
	{
		LString s = Root.Print(0);
		#ifdef _DEBUG
		LAssert(LIsUtf8(s));
		#endif
		return s;
	}

	LString Get(LString Addr)
	{
		Key *k = Deref(Addr, false);
		return k ? k->Str : LString();
	}

	struct Pair
	{
		LString key;
		LString value;

		Pair() { }
		Pair(LString k, LString v) : key(k), value(v) { }
	};

	class Iter
	{
		LJson *j;
		LArray<Key> *a;

	public:
		struct IterPos
		{
			Iter *It;
			size_t Pos;

			IterPos(Iter *i, size_t p)
			{
				It = i;
				Pos = p;
			}

			bool operator !=(const IterPos &i) const
			{
				bool Eq = It == i.It && Pos == i.Pos;
				return !Eq;
			}

			IterPos &operator *()
			{
				return *this;
			}

			operator LString()
			{
				return (*It->a)[Pos].Str;
			}

			IterPos &operator++()
			{
				Pos++;
				return *this;
			}

			LString Get(LString Addr)
			{
				auto &Arr = *It->a;
				if (Pos >= Arr.Length())
					return LString();
				Key &k = Arr[Pos];
				Key *v = k.Deref(Addr, false);
				if (!v)
					return LString();

				return v->Str;
			}

			Pair Get()
			{
				auto &Arr = *It->a;
				if (Pos >= Arr.Length())
					return Pair();
				Key &k = Arr[Pos];
				if (k.Name)
					return Pair(k.Name, k.Str);
				if (k.Obj.Length())
					return Pair(k.Obj[0].Name, k.Obj[0].Str);
				return Pair();
			}

			Iter GetArray(LString Addr)
			{
				Iter a(It->j);

				auto &Arr = *It->a;
				if (Pos < Arr.Length())
				{
					Key *k = Arr[Pos].Deref(Addr, false);
					if (k)
						a.Set(&k->Array);
				}

				return a;
			}
		};

		Iter(LJson *J = NULL) : j(J)
		{
			a = NULL;
		}

		void Set(LJson *J)
		{
			j = J;
		}

		void Set(LArray<Key> *arr)
		{
			a = arr;
		}

		size_t Length()
		{
			return a ? a->Length() : 0;
		}

		IterPos begin()
		{
			return IterPos(this, 0);
		}

		IterPos end()
		{
			return IterPos(this, a ? a->Length() : 0);
		}
	};

	Iter GetArray(LString Addr)
	{
		Iter a(this);
		Key *k = Deref(Addr, false);
		if (k)
			a.Set(&k->Array);

		return a;
	}

	bool Set(LString Addr, const char *Val)
	{
		Key *k = Deref(Addr, true);
		if (!k)
			return false;
		k->Str = Val;
		return true;
	}

	bool Set(LString Addr, int64_t Int)
	{
		char s[32];
		sprintf_s(s, sizeof(s), LPrintfInt64, Int);
		return Set(Addr, s);
	}

	bool Set(LString Addr, double Dbl)
	{
		char s[32];
		sprintf_s(s, sizeof(s), "%f", Dbl);
		return Set(Addr, s);
	}

	bool Set(LString Addr, LArray<LString> &Array)
	{
		Key *k = Deref(Addr, true);
		if (!k)
			return false;
		for (auto &a : Array)
			k->Array.New().Str = a;
		return true;
	}

	bool Set(LString Addr, LArray<LJson> &Array)
	{
		Key *k = Deref(Addr, true);
		if (!k)
			return false;
		for (auto &in : Array)
		{
			auto &out = k->Array.New();
			out = in.Root;
		}
		return true;
	}

	LArray<LString> GetKeys(const char *Addr = NULL)
	{
		Key *k = Deref(Addr, true);
		LArray<LString> a;
		if (k)
			for (auto &i : k->Obj)
				a.Add(i.Name);
		return a;
	}
};

#endif