//========================================================================================
// A light and efficient JSON library
//
// AUTHOR:  Gabor Nagy
// DATE:    2025-Feb-15 01:03:59
//
// Copyright (C) 2014-2026 by Gabor Nagy. All rights reserved.
//========================================================================================
#ifndef _EJSON_h
#define _EJSON_h

#include <ETypes.h>
#include <EMalloc.h>
#include <EStrings.h>
#include <ELists.h>


#if defined (__cplusplus) && (!(defined (__CPLUSLIBS)))
extern "C" {
#endif

#define EJsNAME_ALLOCATED  -1

typedef struct EJsNode_S  EJsNode;
typedef struct EJsNs_S    EJsNs;

#define EJsCAST  (EJsChar*)
typedef uint8_t  EJsChar;

enum
{
	EJsSTRING = 1,
	EJsSTRING_LITERAL,
	EJsPOINTER_ARRAY,
	EJsBINARY
};

struct EJsNode_S
{
	EJsChar*  Name;
	EJsChar*  NamePrefix;
	uint16_t  Type;
	uint32_t  Flags;  // EJsCONTENTSTRING_STATIC, etc.
	int       ID;     // Index of the Name field into the tag id list, or EJsNAME_ALLOCATED if Name was dynamically allocated.

	EJsChar*  Content;uint8_t  ContentType;EIndex  ContentCount;
	EJsNode*  Parent;
	EJsNode*  Child;
	EJsNode*  NextSibling;
	EJsNode*  Next;

// Used by the USD plugin
//
	EJsChar*  QualifierName;
	EJsChar*  InstanceName;
	EJsChar*  SemanticName;
	double    TimeStamp;
};

typedef struct
{
	EJsChar*  Name;
	EJsNode*  Children;
} EJsDoc;


struct EJsNs_S
{
};


typedef struct
{
	EJsNode*  Tag;
} _EJsTagStackEntry;

// Flags for EJsNode_HrcSave
//
#define EJsCOLORS                     0x0001
#define EJsSINGLE_LINE                0x0020
#define EJsARRAY_SINGLE_LINE          0x0040
#define EJsCOMMAS_FOR_SIBLINGS        0x0080
#define EJsINDENT_SPACE_2             0x0100  // Use 2 spaces instead of tabs
#define EJsINDENT_SPACE_4             0x0200  // Use 4 spaces instead of tabs

// Node types
//
#define EJsNODE                       0x00001
#define EJsOBJECT                     0x00002
#define EJsARRAY                      0x00004
#define EJsVALUE                      0x00008
#define EJsHEADER                     0x00010
#define EJsNODETYPEMASK               0x0001F

// Node flags
//
#define EJsQUALIFIER_STATIC           0x00020
#define EJsNAMEPREFIX_STATIC          0x00040
#define EJsNAME_STATIC                0x00080
#define EJsNAME_QUOTES                0x00100
#define EJsNAME_COLON_AFTER           0x00200
#define EJsNAME_SCOPE_OPEN_SAME_LINE  0x00400
#define EJsEQUALSIGN_BEFORE_VALUE     0x00800
#define EJsINSTANCENAME_STATIC        0x01000
#define EJsINSTANCENAME_COLON         0x02000
#define EJsINSTANCENAME_QUOTES        0x04000
#define EJsSEMANTICNAME_COLON         0x08000
#define EJsSEMANTICNAME_STATIC        0x10000
#define EJsCONTENTSTRING_STATIC       0x20000
#define EJsCONTENTSTRING_QUOTES       0x40000
#define EJsNODEFLAGSMASK              0x7FFE0



//#define EJsJSON_OBJECT_FLAGS   (EJsOBJECT)
#define EJsJSON_OBJECT_FLAGS    (EJsOBJECT|EJsNAME_QUOTES|EJsNAME_COLON_AFTER|EJsCONTENTSTRING_QUOTES)
#define EJsJSON_ARRAY_FLAGS     (EJsARRAY|EJsNAME_QUOTES|EJsNAME_COLON_AFTER)
#define EJsJSON_NODE_FLAGS      (EJsNODE|EJsNAME_QUOTES|EJsNAME_COLON_AFTER)
#define EJsJSON_VALUE_FLAGS     (EJsVALUE|EJsNAME_QUOTES|EJsNAME_COLON_AFTER)

typedef struct
{
	uint32_t        Key;
	const uint8_t*  String;
} EHashEntry;

typedef struct
{
	uint32_t  Key;
	EListA(, EHashEntry, Entries);
} EHashBucket;

#define _E_GetC(mDst)  do { int  lCh = PBuffer[LBufferPos++];if(LBufferPos >= PBufferLen || lCh == 0) mDst = EOF;else mDst = lCh; } while(0)

#define _EJs_StrAddCh(m, mN) do\
{\
	if(m##_Len + mN >= m##_LenAllocated)\
	{\
		const EIndex  lNewSize2A = m##_LenAllocated * 2, lNewSizeC = m##_Len + mN;\
		const EIndex  lNewSize = E_Max(lNewSize2A, lNewSizeC) + 2;\
		typeof(m)  lT = ERealloc(m, lNewSize);\
		if(lT) { m##_LenAllocated = lNewSize;m = lT; }\
	}\
} while(0)


static const size_t  E_HashInitialValue = 2166136261U;
static inline uint32_t E_Hash(const uint8_t* PString, const size_t PLen)
{
	uint32_t  LHash = E_HashInitialValue;
	int  LC = 0;for(;LC < PLen;LC++)
	{
		LHash = 17 * LHash + PString[LC];
	}
//	return(LHash % PTableSize);
//printf("%s() %s:%d  [%s] %d  %08x\n", __FUNCTION__, __FILE__, __LINE__, PString, (int)PLen, LHash & 0xFF);fflush(stdout);
	return(LHash);
}


#define _EJs_PopStack() do\
{\
	if(LScope > 0)\
	{\
		LScope--;\
		LCreateNewNodeAtOpenBracket = TRUE;\
		LState = 0;\
/*printf("%s() %s:%d %c %d\n", __FUNCTION__, __FILE__, __LINE__, LCh, LScope);fflush(stdout);*/\
	}\
	else\
	{\
		LResult = E_CORRUPT_DATA;\
		if(LDebug) { printf("%s() %s:%d %s%d%s: %sstack underflow%s state %s%d%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, LLine + 1, ECodeNormal,  ECodeYellow, ECodeNormal,  ECodeBlue, LState, ECodeNormal);fflush(stdout); }\
		LState = 0;\
		goto Done;\
	}\
} while(0)


#define _EJs_AddTag(mTagStack, mName) do\
{\
	ENewClr(LTag);\
	LTag->ID      = EJsNAME_ALLOCATED;\
	LTag->Flags   = 0;\
	LTag->Content = NULL;\
	LTag->Child   = LTag->NextSibling = LTag->Next = NULL;\
\
	_EJsTagStackEntry*  lTagStackEntry = mTagStack + LScope;\
\
	if(LHierarchy == NULL) LHierarchy = LTag;\
\
	if(LScope)\
	{\
		LTag->Parent = mTagStack[LScope - 1].Tag;\
\
		if(lTagStackEntry->Tag) lTagStackEntry->Tag->NextSibling = LTag;\
		else if(LTag->Parent) LTag->Parent->Child = LTag;\
	}\
	else\
	{\
		LTag->Parent = NULL;\
		if(lTagStackEntry->Tag) lTagStackEntry->Tag->NextSibling = LTag;\
	}\
	lTagStackEntry->Tag = LTag;\
/*printf("%s() added <%s> scope %d\n", __FUNCTION__, LTag->Name, LScope);fflush(stdout);*/\
} while(0)


static inline const uint8_t* _EJs_StringInTable(const uint8_t* PString, const int PString_Len,  EHashBucket* PFWStringHashTable, const int PHashMask)
{
	if(PFWStringHashTable)
	{
		const uint32_t  LHash = E_Hash(PString, PString_Len) & PHashMask;
		EHashBucket*    LBucket = PFWStringHashTable + LHash;
//printf("%s() %s:%d  [%s] %d  %08x  %lld\n", __FUNCTION__, __FILE__, __LINE__, (char*)PString, PString_Len, LHash, (long long)LBucket->Entries_Count);fflush(stdout);
		EIndex  LEC = 0;const int  LEN = LBucket->Entries_Count;
		{
			EHashEntry*  LEntry = LBucket->Entries;for(;LEC < LEN;LEC++, LEntry++)
			{
				const uint8_t*  LT = (uint8_t*)LEntry->String;
				if(EStr_Equal((char*)LT, (char*)PString)) return(LT);
			}
		}
	}
	return(NULL);
}


static inline void _EJs_SetString(EJsNode* PTag,  const uint8_t* PString, const int PString_Len,  EHashBucket* PFWStringHashTable, const int PHashMask)
{
	if(PFWStringHashTable)
	{
		const uint32_t  LHash = E_Hash(PString, PString_Len) & PHashMask;
		EHashBucket*    LBucket = PFWStringHashTable + LHash;
		const uint8_t*  LFWString = NULL;
//printf("%s() %s:%d  [%s] %d  %08x  %lld\n", __FUNCTION__, __FILE__, __LINE__, (char*)PString, PString_Len, LHash, (long long)LBucket->Entries_Count);fflush(stdout);
		EIndex  LEC = 0;const int  LEN = LBucket->Entries_Count;
		{
			EHashEntry*  LEntry = LBucket->Entries;for(;LEC < LEN;LEC++, LEntry++)
			{
				const uint8_t*  LT = LEntry->String;
				if(EStr_Equal((char*)LT, (char*)PString)) { LFWString = LT;break; }
			}
		}
		if(LFWString)
		{
//printf("%s() %s:%d  [%s] %08x  %p:%s\n", __FUNCTION__, __FILE__, __LINE__, (char*)PString, LHash, LFWString, LFWString);fflush(stdout);
			PTag->Name = (uint8_t*)LFWString;PTag->ID = 0;  // EFIXME set this to 0 for now (just not to EJsNAME_ALLOCATED)
		}
		else { PTag->Name = (EJsChar*)EStrDup((char*)PString);PTag->ID = EJsNAME_ALLOCATED; }
	}
	else { PTag->Name = (EJsChar*)EStrDup((char*)PString);PTag->ID = EJsNAME_ALLOCATED; }
}




//----------------------------------------------------------------
// Function prototypes
//----------------------------------------------------------------

extern EJsDoc*  EJsDoc_LoadFromMemory(const uint8_t* PBuffer, const EIndex PBufferLen,  const uint8_t** PFlyWeightStrings, const int PNFlyWeightStrings,  int* PResult);
extern EJsDoc*  EJsDoc_New(const EJsChar* PName);
extern void     EJsDoc_Free(EJsDoc* PDoc);
extern void     EJsNode_HrcSave(FILE* PFile, EJsNode* PRootNode, const uint16_t PFlags);
extern char*    EJsNode_HrcSaveToMemory(EJsNode* PRootNode, const char* PPrefix, const uint16_t PFlags);

extern EJsNode*  EJsNode_New(EJsNs* PNameSpace, EJsChar* PName, const uint32_t PType);
extern EJsNode*  EJsNode_NewChild(EJsNode* PParent, EJsNs* PNs, EJsChar* PNamePrefix, EJsChar* PName, const EJsChar* PContent, const uint32_t PType);
extern void      EJsNode_Free(EJsNode* PNode);

#if defined (__cplusplus) && (!(defined (__CPLUSLIBS)))
}	// Close scope of 'extern "C"' declaration
#endif

#endif	// _EJSON_h
