/*
  Copyright (c) 2015 Alex Diener
  
  This software is provided 'as-is', without any express or implied
  warranty. In no event will the authors be held liable for any damages
  arising from the use of this software.
  
  Permission is granted to anyone to use this software for any purpose,
  including commercial applications, and to alter it and redistribute it
  freely, subject to the following restrictions:
  
  1. The origin of this software must not be misrepresented; you must not
     claim that you wrote the original software. If you use this software
     in a product, an acknowledgment in the product documentation would be
     appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
     misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.
  
  Alex Diener alex@ludobloom.com
*/

#include "jsonserialization/JSONSerializationContext.h"
#include "utilities/Base64.h"
#include "utilities/IOUtilities.h"
#include <float.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>

#define stemobject_implementation JSONSerializationContext

v_begin();
v_func(dispose);
v_func(errorString);
v_func(beginStructure);
v_func(beginDictionary);
v_func(beginArray);
v_func(endStructure);
v_func(endDictionary);
v_func(endArray);
v_func(writeBoolean);
v_func(writeInt8);
v_func(writeUInt8);
v_func(writeInt16);
v_func(writeUInt16);
v_func(writeInt32);
v_func(writeUInt32);
v_func(writeInt64);
v_func(writeUInt64);
v_func(writeFloat);
v_func(writeDouble);
v_func(writeFixed16_16);
v_func(writeEnumeration);
v_func(writeEnumeration8);
v_func(writeEnumeration16);
v_func(writeEnumeration32);
v_func(writeBitfield8);
v_func(writeBitfield16);
v_func(writeBitfield32);
v_func(writeBitfield64);
v_func(writeString);
v_func(writeStringNullable);
v_func(writeBlob);
v_func(pushFormatHint);
v_func(popFormatHint);
v_end();

#define failWithStatus(STATUS, RETURN_CODE) \
	call_virtual(setError, self, (STATUS)); \
	if (self->jmpBuf != NULL) { \
		longjmp(*self->jmpBuf, self->status); \
	} \
	RETURN_CODE;

#define reallocCurrentNodeSubitems() \
	self->currentNode->subitems = realloc(self->currentNode->subitems, sizeof(struct JSONNode) * (self->currentNode->value.count + 1))

#define writeNode(TARGET, CONTAINER, KEY, TYPE, VALUE_FIELD, VALUE) \
	(TARGET).type = (TYPE); \
	if ((CONTAINER) != NULL && (CONTAINER)->type == JSON_TYPE_OBJECT) { \
		if ((KEY) == NULL) { \
			failWithStatus(SERIALIZATION_ERROR_NULL_KEY, return) \
		} \
		if (self->currentContainerType == JSON_SERIALIZATION_CONTAINER_TYPE_STRUCTURE) { \
			size_t nodeIndex; \
			for (nodeIndex = 0; nodeIndex < (CONTAINER)->value.count; nodeIndex++) { \
				if (!strcmp((CONTAINER)->subitems[nodeIndex].key, (KEY))) { \
					failWithStatus(JSON_SERIALIZATION_ERROR_DUPLICATE_STRUCTURE_KEY, return) \
				} \
			} \
		} \
		(TARGET).keyLength = strlen(KEY); \
		(TARGET).key = malloc((TARGET).keyLength + 1); \
		strcpy((TARGET).key, (KEY)); \
	} else { \
		(TARGET).key = NULL; \
	} \
	if ((TYPE) != JSON_TYPE_NULL) { \
		(TARGET).value.VALUE_FIELD = (VALUE); \
	}

#define writeNodeCommonArgs(TYPE, VALUE_FIELD) writeNode(self->currentNode->subitems[self->currentNode->value.count], self->currentNode, key, TYPE, VALUE_FIELD, value)

#define failIfContainerNotStarted() \
	if (self->currentNode == NULL) { \
		failWithStatus(SERIALIZATION_ERROR_NO_CONTAINER_STARTED, return) \
	}

JSONSerializationContext * JSONSerializationContext_create(void) {
	stemobject_create_implementation(init)
}

bool JSONSerializationContext_init(JSONSerializationContext * self) {
	call_super(init, self);
	self->rootNode = NULL;
	self->currentNode = NULL;
	self->nodeStack = NULL;
	self->nodeStackAllocatedSize = 0;
	self->nodeStackCurrentDepth = 0;
	self->rootNodeOwnedBySelf = true;
	self->formatHintStackDepth = 0;
	return true;
}

void JSONSerializationContext_dispose(JSONSerializationContext * self) {
	if (self->rootNodeOwnedBySelf && self->rootNode != NULL) {
		JSONNode_dispose(self->rootNode);
	}
	free(self->nodeStack);
	call_super(dispose, self);
}

char * JSONSerializationContext_writeToString(JSONSerializationContext * self, enum JSONEmitterFormat format, size_t * outLength, struct JSONEmissionError * outError) {
	if (self->currentNode != NULL || self->status != SERIALIZATION_ERROR_OK) {
		if (outError != NULL) {
			outError->node = NULL;
			outError->code = JSON_SERIALIZATION_ERROR_INCOMPLETE;
			outError->description = "writeToString called in an incomplete or error state (check SerializationContext's status field)";
		}
		return NULL;
	}
	if (self->rootNode == NULL) {
		call_virtual(setError, self, SERIALIZATION_ERROR_NO_TOP_LEVEL_CONTAINER);
		if (outError != NULL) {
			outError->node = NULL;
			outError->code = SERIALIZATION_ERROR_NO_TOP_LEVEL_CONTAINER;
			outError->description = "writeToString called without any data having been serialized";
		}
		return NULL;
	}
	return JSONEmitter_writeString(self->rootNode, format, outLength, outError);
}

bool JSONSerializationContext_writeToFile(JSONSerializationContext * self, enum JSONEmitterFormat format, const char * filePath, struct JSONEmissionError * outError) {
	if (self->currentNode != NULL || self->status != SERIALIZATION_ERROR_OK) {
		if (outError != NULL) {
			outError->node = NULL;
			outError->code = JSON_SERIALIZATION_ERROR_INCOMPLETE;
			outError->description = "writeToFile called in an incomplete or error state (check SerializationContext's status field)";
		}
		return NULL;
	}
	if (self->rootNode == NULL) {
		call_virtual(setError, self, SERIALIZATION_ERROR_NO_TOP_LEVEL_CONTAINER);
		if (outError != NULL) {
			outError->node = NULL;
			outError->code = SERIALIZATION_ERROR_NO_TOP_LEVEL_CONTAINER;
			outError->description = "writeToFile called without any data having been serialized";
		}
		return NULL;
	}
	return JSONEmitter_writeFile(self->rootNode, format, filePath, outError);
}

struct JSONNode * JSONSerializationContext_writeToJSONNode(JSONSerializationContext * self) {
	if (self->currentNode != NULL || self->status != SERIALIZATION_ERROR_OK) {
		return NULL;
	}
	if (self->rootNode == NULL) {
		call_virtual(setError, self, SERIALIZATION_ERROR_NO_TOP_LEVEL_CONTAINER);
	}
	if (self->rootNodeOwnedBySelf) {
		self->rootNodeOwnedBySelf = false;
		return self->rootNode;
	}
	
	return JSONNode_copy(self->rootNode);
}

const char * JSONSerializationContext_errorString(JSONSerializationContext * self, int status) {
	return JSONSerialization_errorString(status);
}

static void writeNodeFormatHint(JSONSerializationContext * self, JSONNode * node) {
	if (self->formatHintStackDepth > 0) {
		node->stringLength = self->formatHintStack[self->formatHintStackDepth - 1];
	} else {
		node->stringLength = JSONEmitterFormat_multiLine;
	}
}

void JSONSerializationContext_beginStructure(JSONSerializationContext * self, const char * key) {
	if (self->rootNode == NULL) {
		self->rootNode = malloc(sizeof(struct JSONNode));
		self->currentNode = self->rootNode;
		self->currentContainerType = JSON_SERIALIZATION_CONTAINER_TYPE_STRUCTURE;
		self->rootNode->type = JSON_TYPE_NULL;
		self->rootNode->subitems = NULL;
		writeNode(*self->rootNode, (struct JSONNode *) NULL, key, JSON_TYPE_OBJECT, count, 0);
		
	} else {
		if (self->currentNode == NULL) {
			failWithStatus(SERIALIZATION_ERROR_MULTIPLE_TOP_LEVEL_CONTAINERS, return)
		}
		if (self->nodeStackAllocatedSize <= self->nodeStackCurrentDepth) {
			self->nodeStackAllocatedSize++;
			self->nodeStack = realloc(self->nodeStack, sizeof(struct JSONSerializationContext_nodeStackItem) * self->nodeStackAllocatedSize);
		}
		self->nodeStack[self->nodeStackCurrentDepth].node = self->currentNode;
		self->nodeStack[self->nodeStackCurrentDepth].containerType = self->currentContainerType;
		self->nodeStackCurrentDepth++;
		
		reallocCurrentNodeSubitems();
		self->currentNode->subitems[self->currentNode->value.count].type = JSON_TYPE_NULL;
		self->currentNode->subitems[self->currentNode->value.count].subitems = NULL;
		writeNode(self->currentNode->subitems[self->currentNode->value.count], self->currentNode, key, JSON_TYPE_OBJECT, count, 0);
		self->currentNode = &self->currentNode->subitems[self->currentNode->value.count++];
		self->currentContainerType = JSON_SERIALIZATION_CONTAINER_TYPE_STRUCTURE;
	}
	writeNodeFormatHint(self, self->currentNode);
}

void JSONSerializationContext_beginDictionary(JSONSerializationContext * self, const char * key) {
	if (self->rootNode == NULL) {
		self->rootNode = malloc(sizeof(struct JSONNode));
		self->currentNode = self->rootNode;
		self->currentContainerType = JSON_SERIALIZATION_CONTAINER_TYPE_DICTIONARY;
		self->rootNode->type = JSON_TYPE_NULL;
		self->rootNode->subitems = NULL;
		writeNode(*self->rootNode, (struct JSONNode *) NULL, key, JSON_TYPE_OBJECT, count, 0);
		
	} else {
		if (self->currentNode == NULL) {
			failWithStatus(SERIALIZATION_ERROR_MULTIPLE_TOP_LEVEL_CONTAINERS, return)
		}
		if (self->nodeStackAllocatedSize <= self->nodeStackCurrentDepth) {
			self->nodeStackAllocatedSize++;
			self->nodeStack = realloc(self->nodeStack, sizeof(struct JSONSerializationContext_nodeStackItem) * self->nodeStackAllocatedSize);
		}
		self->nodeStack[self->nodeStackCurrentDepth].node = self->currentNode;
		self->nodeStack[self->nodeStackCurrentDepth].containerType = self->currentContainerType;
		self->nodeStackCurrentDepth++;
		
		reallocCurrentNodeSubitems();
		self->currentNode->subitems[self->currentNode->value.count].type = JSON_TYPE_NULL;
		self->currentNode->subitems[self->currentNode->value.count].subitems = NULL;
		writeNode(self->currentNode->subitems[self->currentNode->value.count], self->currentNode, key, JSON_TYPE_OBJECT, count, 0);
		self->currentNode = &self->currentNode->subitems[self->currentNode->value.count++];
		self->currentContainerType = JSON_SERIALIZATION_CONTAINER_TYPE_DICTIONARY;
	}
	writeNodeFormatHint(self, self->currentNode);
}

void JSONSerializationContext_beginArray(JSONSerializationContext * self, const char * key) {
	if (self->rootNode == NULL) {
		self->rootNode = malloc(sizeof(struct JSONNode));
		self->currentNode = self->rootNode;
		self->currentContainerType = JSON_SERIALIZATION_CONTAINER_TYPE_ARRAY;
		self->rootNode->type = JSON_TYPE_NULL;
		self->rootNode->subitems = NULL;
		writeNode(*self->rootNode, (struct JSONNode *) NULL, key, JSON_TYPE_ARRAY, count, 0);
		
	} else {
		if (self->currentNode == NULL) {
			failWithStatus(SERIALIZATION_ERROR_MULTIPLE_TOP_LEVEL_CONTAINERS, return)
		}
		if (self->nodeStackAllocatedSize <= self->nodeStackCurrentDepth) {
			self->nodeStackAllocatedSize++;
			self->nodeStack = realloc(self->nodeStack, sizeof(struct JSONSerializationContext_nodeStackItem) * self->nodeStackAllocatedSize);
		}
		self->nodeStack[self->nodeStackCurrentDepth].node = self->currentNode;
		self->nodeStack[self->nodeStackCurrentDepth].containerType = self->currentContainerType;
		self->nodeStackCurrentDepth++;
		
		reallocCurrentNodeSubitems();
		self->currentNode->subitems[self->currentNode->value.count].type = JSON_TYPE_NULL;
		self->currentNode->subitems[self->currentNode->value.count].subitems = NULL;
		writeNode(self->currentNode->subitems[self->currentNode->value.count], self->currentNode, key, JSON_TYPE_ARRAY, count, 0);
		self->currentNode = &self->currentNode->subitems[self->currentNode->value.count++];
		self->currentContainerType = JSON_SERIALIZATION_CONTAINER_TYPE_ARRAY;
	}
	writeNodeFormatHint(self, self->currentNode);
}

void JSONSerializationContext_endStructure(JSONSerializationContext * self) {
	if (self->currentNode == NULL) {
		failWithStatus(SERIALIZATION_ERROR_CONTAINER_UNDERFLOW, return)
	}
	
	if (self->currentContainerType != JSON_SERIALIZATION_CONTAINER_TYPE_STRUCTURE) {
		failWithStatus(SERIALIZATION_ERROR_CONTAINER_TYPE_MISMATCH, return)
	}
	
	if (self->nodeStackCurrentDepth > 0) {
		self->currentNode = self->nodeStack[--self->nodeStackCurrentDepth].node;
		self->currentContainerType = self->nodeStack[self->nodeStackCurrentDepth].containerType;
		
	} else {
		self->currentNode = NULL;
	}
}

void JSONSerializationContext_endDictionary(JSONSerializationContext * self) {
	if (self->currentNode == NULL) {
		failWithStatus(SERIALIZATION_ERROR_CONTAINER_UNDERFLOW, return)
	}
	
	if (self->currentContainerType != JSON_SERIALIZATION_CONTAINER_TYPE_DICTIONARY) {
		failWithStatus(SERIALIZATION_ERROR_CONTAINER_TYPE_MISMATCH, return)
	}
	
	if (self->nodeStackCurrentDepth > 0) {
		self->currentNode = self->nodeStack[--self->nodeStackCurrentDepth].node;
		self->currentContainerType = self->nodeStack[self->nodeStackCurrentDepth].containerType;
		
	} else {
		self->currentNode = NULL;
	}
}

void JSONSerializationContext_endArray(JSONSerializationContext * self) {
	if (self->currentNode == NULL) {
		failWithStatus(SERIALIZATION_ERROR_CONTAINER_UNDERFLOW, return)
	}
	
	if (self->currentContainerType != JSON_SERIALIZATION_CONTAINER_TYPE_ARRAY) {
		failWithStatus(SERIALIZATION_ERROR_CONTAINER_TYPE_MISMATCH, return)
	}
	
	if (self->nodeStackCurrentDepth > 0) {
		self->currentNode = self->nodeStack[--self->nodeStackCurrentDepth].node;
		self->currentContainerType = self->nodeStack[self->nodeStackCurrentDepth].containerType;
		
	} else {
		self->currentNode = NULL;
	}
}

void JSONSerializationContext_writeBoolean(JSONSerializationContext * self, const char * key, bool value) {
	failIfContainerNotStarted()
	reallocCurrentNodeSubitems();
	writeNodeCommonArgs(JSON_TYPE_BOOLEAN, boolean);
	self->currentNode->value.count++;
}

void JSONSerializationContext_writeInt8(JSONSerializationContext * self, const char * key, int8_t value) {
	failIfContainerNotStarted()
	reallocCurrentNodeSubitems();
	writeNodeCommonArgs(JSON_TYPE_NUMBER, number);
	self->currentNode->value.count++;
}

void JSONSerializationContext_writeUInt8(JSONSerializationContext * self, const char * key, uint8_t value) {
	failIfContainerNotStarted()
	reallocCurrentNodeSubitems();
	writeNodeCommonArgs(JSON_TYPE_NUMBER, number);
	self->currentNode->value.count++;
}

void JSONSerializationContext_writeInt16(JSONSerializationContext * self, const char * key, int16_t value) {
	failIfContainerNotStarted()
	reallocCurrentNodeSubitems();
	writeNodeCommonArgs(JSON_TYPE_NUMBER, number);
	self->currentNode->value.count++;
}

void JSONSerializationContext_writeUInt16(JSONSerializationContext * self, const char * key, uint16_t value) {
	failIfContainerNotStarted()
	reallocCurrentNodeSubitems();
	writeNodeCommonArgs(JSON_TYPE_NUMBER, number);
	self->currentNode->value.count++;
}

void JSONSerializationContext_writeInt32(JSONSerializationContext * self, const char * key, int32_t value) {
	failIfContainerNotStarted()
	reallocCurrentNodeSubitems();
	writeNodeCommonArgs(JSON_TYPE_NUMBER, number);
	self->currentNode->value.count++;
}

void JSONSerializationContext_writeUInt32(JSONSerializationContext * self, const char * key, uint32_t value) {
	failIfContainerNotStarted()
	reallocCurrentNodeSubitems();
	writeNodeCommonArgs(JSON_TYPE_NUMBER, number);
	self->currentNode->value.count++;
}

#ifndef S_SPLINT_S
#if ULLONG_MAX < 0xFFFFFFFFFFFFFFFFull
#error long long is less than 64 bits wide; cannot print as %llu in JSONSerializationContext_writeInt64 and JSONSerializationContext_writeUInt64
#endif
#endif

void JSONSerializationContext_writeInt64(JSONSerializationContext * self, const char * key, int64_t value) {
	failIfContainerNotStarted()
	reallocCurrentNodeSubitems();
	writeNodeCommonArgs(JSON_TYPE_NUMBER, number);
	if (value > 1ll << DBL_MANT_DIG || value < -(1ll << DBL_MANT_DIG)) {
		// Not representable as double; use string instead
		self->currentNode->subitems[self->currentNode->value.count].type = JSON_TYPE_STRING;
		self->currentNode->subitems[self->currentNode->value.count].value.string = malloc(24); // Generous; INT64_MIN should only be 20 chars long
		self->currentNode->subitems[self->currentNode->value.count].stringLength = snprintf_safe(self->currentNode->subitems[self->currentNode->value.count].value.string, 24,
#if defined(WIN32)
		"%I64d"
#elif defined(linux) && defined(_LP64)
		"%ld"
#else
		"%lld"
#endif
		, value);
	}
	self->currentNode->value.count++;
}

void JSONSerializationContext_writeUInt64(JSONSerializationContext * self, const char * key, uint64_t value) {
	failIfContainerNotStarted()
	reallocCurrentNodeSubitems();
	writeNodeCommonArgs(JSON_TYPE_NUMBER, number);
	if (value > 1ull << DBL_MANT_DIG) {
		// Not representable as double; use string instead
		self->currentNode->subitems[self->currentNode->value.count].type = JSON_TYPE_STRING;
		self->currentNode->subitems[self->currentNode->value.count].value.string = malloc(24); // Generous; UINT64_MAX should only be 20 chars long
		self->currentNode->subitems[self->currentNode->value.count].stringLength = snprintf_safe(self->currentNode->subitems[self->currentNode->value.count].value.string, 24,
#if defined(WIN32)
		"%I64u"
#elif defined(linux) && defined(_LP64)
		"%lu"
#else
		"%llu"
#endif
		, value);
	}
	self->currentNode->value.count++;
}

void JSONSerializationContext_writeFloat(JSONSerializationContext * self, const char * key, float value) {
	failIfContainerNotStarted()
	reallocCurrentNodeSubitems();
	writeNodeCommonArgs(JSON_TYPE_NUMBER, number);
	self->currentNode->value.count++;
}

void JSONSerializationContext_writeDouble(JSONSerializationContext * self, const char * key, double value) {
	failIfContainerNotStarted()
	reallocCurrentNodeSubitems();
	writeNodeCommonArgs(JSON_TYPE_NUMBER, number);
	self->currentNode->value.count++;
}

void JSONSerializationContext_writeFixed16_16(JSONSerializationContext * self, const char * key, fixed16_16 value) {
	failIfContainerNotStarted()
	reallocCurrentNodeSubitems();
	writeNode(self->currentNode->subitems[self->currentNode->value.count],
	          self->currentNode,
	          key,
	          JSON_TYPE_STRING,
	          string,
	          malloc(11));
	self->currentNode->subitems[self->currentNode->value.count].stringLength = snprintf_safe(self->currentNode->subitems[self->currentNode->value.count].value.string, 11, "0x%05X", value);
	self->currentNode->value.count++;
}

void JSONSerializationContext_writeEnumeration(JSONSerializationContext * self, const char * key, int value, unsigned int valueCount, Serialization_enumKeyValue * values) {
	failIfContainerNotStarted()
	
	int status = Serialization_checkEnumerationErrors(value, valueCount, values);
	if (status != SERIALIZATION_ERROR_OK) {
		failWithStatus(status, return);
	}
	
	reallocCurrentNodeSubitems();
	
	const char * enumKey = NULL;
	for (unsigned int valueIndex = 0; valueIndex < valueCount; valueIndex++) {
		if (values[valueIndex].value == value) {
			enumKey = values[valueIndex].key;
			break;
		}
	}
	if (enumKey == NULL) {
		failWithStatus(SERIALIZATION_ERROR_ENUM_NOT_NAMED, return)
	}
	
	self->currentNode->subitems[self->currentNode->value.count].stringLength = strlen(enumKey);
	writeNode(self->currentNode->subitems[self->currentNode->value.count],
	          self->currentNode,
	          key,
	          JSON_TYPE_STRING,
	          string,
	          malloc(self->currentNode->subitems[self->currentNode->value.count].stringLength + 1));
	strcpy(self->currentNode->subitems[self->currentNode->value.count].value.string, enumKey);
	self->currentNode->value.count++;
}

void JSONSerializationContext_writeEnumeration8(JSONSerializationContext * self, const char * key, int8_t value, unsigned int valueCount, Serialization_enumKeyValue * values) {
	call_virtual(writeEnumeration, self, key, value, valueCount, values);
}

void JSONSerializationContext_writeEnumeration16(JSONSerializationContext * self, const char * key, int16_t value, unsigned int valueCount, Serialization_enumKeyValue * values) {
	call_virtual(writeEnumeration, self, key, value, valueCount, values);
}

void JSONSerializationContext_writeEnumeration32(JSONSerializationContext * self, const char * key, int32_t value, unsigned int valueCount, Serialization_enumKeyValue * values) {
	call_virtual(writeEnumeration, self, key, value, valueCount, values);
}

#define writeBitfieldImplementation(NBITS) \
	int status = Serialization_checkBitfield##NBITS##Errors(value, bitNameCount, bitNames); \
	if (status != SERIALIZATION_ERROR_OK) { \
		failWithStatus(status, return); \
	} \
	failIfContainerNotStarted() \
	\
	/* http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan */ \
	unsigned int bitCount = 0; \
	uint##NBITS##_t uncountedBits = value; \
	while (uncountedBits > 0) { \
		bitCount++; \
		uncountedBits &= uncountedBits - 1; \
	} \
	\
	reallocCurrentNodeSubitems(); \
	struct JSONNode * array = &self->currentNode->subitems[self->currentNode->value.count]; \
	writeNode(*array, self->currentNode, key, JSON_TYPE_ARRAY, count, 0); \
	self->currentNode->value.count++; \
	if (bitCount == 0) { \
		array->subitems = NULL; \
	} else { \
		array->subitems = malloc(sizeof(struct JSONNode) * bitCount); \
		for (unsigned int bitIndex = 0; bitIndex < NBITS; bitIndex++) { \
			if (value & ((uint##NBITS##_t) 1 << bitIndex)) { \
				for (unsigned int bitNameIndex = 0; bitNameIndex < bitNameCount; bitNameIndex++) { \
					if (bitNames[bitNameIndex].index == bitIndex) { \
						array->subitems[array->value.count].type = JSON_TYPE_STRING; \
						array->subitems[array->value.count].key = NULL; \
						array->subitems[array->value.count].stringLength = strlen(bitNames[bitNameIndex].name); \
						array->subitems[array->value.count].value.string = malloc(array->subitems[array->value.count].stringLength + 1); \
						strcpy(array->subitems[array->value.count].value.string, bitNames[bitNameIndex].name); \
						array->value.count++; \
						break; \
					} \
				} \
			} \
		} \
		if (array->value.count < bitCount) { \
			failWithStatus(SERIALIZATION_ERROR_UNNAMED_BIT,) \
		} \
	}

void JSONSerializationContext_writeBitfield8(JSONSerializationContext * self, const char * key, uint8_t value, unsigned int bitNameCount, Serialization_bitName * bitNames) {
	writeBitfieldImplementation(8)
}

void JSONSerializationContext_writeBitfield16(JSONSerializationContext * self, const char * key, uint16_t value, unsigned int bitNameCount, Serialization_bitName * bitNames) {
	writeBitfieldImplementation(16)
}

void JSONSerializationContext_writeBitfield32(JSONSerializationContext * self, const char * key, uint32_t value, unsigned int bitNameCount, Serialization_bitName * bitNames) {
	writeBitfieldImplementation(32)
}

void JSONSerializationContext_writeBitfield64(JSONSerializationContext * self, const char * key, uint64_t value, unsigned int bitNameCount, Serialization_bitName * bitNames) {
	writeBitfieldImplementation(64)
}

void JSONSerializationContext_writeString(JSONSerializationContext * self, const char * key, const char * value) {
	failIfContainerNotStarted()
	reallocCurrentNodeSubitems();
	self->currentNode->subitems[self->currentNode->value.count].stringLength = strlen(value);
	writeNode(self->currentNode->subitems[self->currentNode->value.count],
	          self->currentNode,
	          key,
	          JSON_TYPE_STRING,
	          string,
	          malloc(self->currentNode->subitems[self->currentNode->value.count].stringLength + 1));
	strcpy(self->currentNode->subitems[self->currentNode->value.count].value.string, value);
	self->currentNode->value.count++;
}

void JSONSerializationContext_writeStringNullable(JSONSerializationContext * self, const char * key, const char * value) {
	failIfContainerNotStarted()
	reallocCurrentNodeSubitems();
	if (value == NULL) {
		writeNode(self->currentNode->subitems[self->currentNode->value.count],
		          self->currentNode,
		          key,
		          JSON_TYPE_NULL,
		          string,
		          NULL);
	} else {
		self->currentNode->subitems[self->currentNode->value.count].stringLength = strlen(value);
		writeNode(self->currentNode->subitems[self->currentNode->value.count],
		          self->currentNode,
		          key,
		          JSON_TYPE_STRING,
		          string,
		          malloc(self->currentNode->subitems[self->currentNode->value.count].stringLength + 1));
		strcpy(self->currentNode->subitems[self->currentNode->value.count].value.string, value);
	}
	self->currentNode->value.count++;
}

void JSONSerializationContext_writeBlob(JSONSerializationContext * self, const char * key, const void * value, size_t length) {
	char * encodedString;
	
	failIfContainerNotStarted()
	reallocCurrentNodeSubitems();
	if (value == NULL) {
		writeNode(self->currentNode->subitems[self->currentNode->value.count],
		          self->currentNode,
		          key,
		          JSON_TYPE_NULL,
		          string,
		          NULL);
	} else {
		self->currentNode->subitems[self->currentNode->value.count].stringLength = encodeBase64(value, length, NULL, 0);
		encodeBase64(value, length, encodedString = malloc(self->currentNode->subitems[self->currentNode->value.count].stringLength + 1), self->currentNode->subitems[self->currentNode->value.count].stringLength + 1);
		writeNode(self->currentNode->subitems[self->currentNode->value.count],
		          self->currentNode,
		          key,
		          JSON_TYPE_STRING,
		          string,
		          encodedString);
	}
	self->currentNode->value.count++;
}

void JSONSerializationContext_pushFormatHint(JSONSerializationContext * self, JSONEmitterFormat formatHint) {
	if (self->formatHintStackDepth < JSON_FORMAT_HINT_STACK_DEPTH_MAX) {
		self->formatHintStack[self->formatHintStackDepth++] = formatHint;
#ifdef DEBUG
	} else {
		fprintf(stderr, "Warning: JSONSerializationContext format hint stack overflow\n");
#endif
	}
}

void JSONSerializationContext_popFormatHint(JSONSerializationContext * self) {
	if (self->formatHintStackDepth > 0) {
		self->formatHintStackDepth--;
#ifdef DEBUG
	} else {
		fprintf(stderr, "Warning: JSONSerializationContext format hint stack underflow\n");
#endif
	}
}
