#include <stdlib.h>
#include <string.h>
#include "newsraft.h"

struct deserialize_stream {
	struct string *entry;
	char delimiter;
	const char *prev_delimiter_pos;
	const char *delimiter_pos;
};

// You can change it without breaking compatibility with existing databases,
// because first character of serialized data is always a DELIMITER. And it
// is easy for the stream handler (see below) to figure out which character
// to take as a delimiter.
#define DELIMITER 31 // ASCII Unit Separator (control character)

static const char caret[2] = {DELIMITER, '^'};

void
serialize_caret(struct string **target)
{
	if (*target == NULL) {
		*target = crtes(100);
	}
	catas(*target, caret, 2);
}

void
serialize_array(register struct string **target, register const char *key, register size_t key_len, register const char *value, register size_t value_len)
{
	if (value == NULL || value_len == 0) {
		return; // Ignore empty entries.
	}
	make_string_fit_more(target, 1 + key_len + value_len);

	// Check tests/serialize_array.c to better understand what this function does.

	// Just append key and value to the target with DELIMITER at the beginning.
	// It's kind of messy here because we need to squeeze out all available performance
	// since this function does a lot of heavy lifting in the feed parsers code.
	(*target)->ptr[(*target)->len] = DELIMITER;
	memcpy((*target)->ptr + (*target)->len + 1, key, key_len);
	memcpy((*target)->ptr + (*target)->len + 1 + key_len, value, value_len);
	(*target)->len += 1 + key_len + value_len;
	(*target)->ptr[(*target)->len] = '\0';

	// Make sure there's no delimiters in the serialized data!
	for (size_t i = 0, end = (*target)->len; i < value_len; ++i) {
		if ((*target)->ptr[end - i - 1] == DELIMITER) {
			for (char *j = (*target)->ptr + end - i - 1; *j != '\0'; ++j) {
				*j = *(j + 1);
			}
			(*target)->len -= 1;
			(*target)->ptr[(*target)->len] = '\0';
			if ((*target)->len + value_len == end) {
				(*target)->len -= 1 + key_len;
				(*target)->ptr[(*target)->len] = '\0';
				return; // Value consists only of delimiters!
			}
		}
	}
	return;
}

void
serialize_string(struct string **target, const char *key, size_t key_len, const struct string *value)
{
	if (!STRING_IS_EMPTY(value)) {
		serialize_array(target, key, key_len, value->ptr, value->len);
	}
}

struct deserialize_stream *
open_deserialize_stream(const char *serialized_data)
{
	struct deserialize_stream *stream = newsraft_malloc(sizeof(struct deserialize_stream));
	stream->entry = crtes(10000);
	stream->delimiter = *serialized_data;
	if (stream->delimiter == '\0') {
		stream->prev_delimiter_pos = NULL;
		stream->delimiter_pos = NULL;
	} else {
		stream->prev_delimiter_pos = serialized_data;
		stream->delimiter_pos = strchr(serialized_data + 1, stream->delimiter);
	}
	return stream;
}

const struct string *
get_next_entry_from_deserialize_stream(struct deserialize_stream *stream)
{
	if (stream->delimiter_pos == NULL && stream->prev_delimiter_pos == NULL) {
		return NULL;
	}
	if (stream->delimiter_pos == NULL) {
		cpyas(&stream->entry, stream->prev_delimiter_pos + 1, strlen(stream->prev_delimiter_pos + 1));
		stream->prev_delimiter_pos = NULL;
	} else {
		cpyas(&stream->entry, stream->prev_delimiter_pos + 1, stream->delimiter_pos - stream->prev_delimiter_pos - 1);
		stream->prev_delimiter_pos = stream->delimiter_pos;
		stream->delimiter_pos = strchr(stream->delimiter_pos + 1, stream->delimiter);
	}
	return stream->entry;
}

void
close_deserialize_stream(struct deserialize_stream *stream)
{
	if (stream != NULL) {
		free_string(stream->entry);
		free(stream);
	}
}
