/* Copyright (c) Dovecot authors, see top-level COPYING file */

#include "lib.h"
#include "str.h"
#include "strescape.h"
#include "dsync-serializer.h"
#include "dsync-deserializer.h"

struct dsync_deserializer {
	pool_t pool;
	const char *name;
	const char *const *required_fields;
	const char *const *keys;
	unsigned int *required_field_indexes;
	unsigned int required_field_count;
};

struct dsync_deserializer_decoder {
	pool_t pool;
	struct dsync_deserializer *deserializer;
	const char *const *values;
	unsigned int values_count;
};

static bool field_find(const char *const *names, const char *name,
		       unsigned int *idx_r)
{
	unsigned int i;

	for (i = 0; names[i] != NULL; i++) {
		if (strcmp(names[i], name) == 0) {
			*idx_r = i;
			return TRUE;
		}
	}
	return FALSE;
}

int dsync_deserializer_init(const char *name, const char *const *required_fields,
			    const char *header_line,
			    struct dsync_deserializer **deserializer_r,
			    const char **error_r)
{
	struct dsync_deserializer *deserializer;
	const char **dup_required_fields;
	unsigned int i, required_count;
	pool_t pool;

	*deserializer_r = NULL;

	pool = pool_alloconly_create("dsync deserializer", 1024);
	deserializer = p_new(pool, struct dsync_deserializer, 1);
	deserializer->pool = pool;
	deserializer->name = p_strdup(pool, name);
	deserializer->keys = (void *)p_strsplit_tabescaped(pool, header_line);

	deserializer->required_field_count = required_count =
		required_fields == NULL ? 0 :
		str_array_length(required_fields);
	dup_required_fields = p_new(pool, const char *, required_count + 1);
	deserializer->required_field_indexes =
		p_new(pool, unsigned int, required_count + 1);
	for (i = 0; i < required_count; i++) {
		dup_required_fields[i] =
			p_strdup(pool, required_fields[i]);
		if (!field_find(deserializer->keys, required_fields[i],
				&deserializer->required_field_indexes[i])) {
			*error_r = t_strdup_printf(
				"Header missing required field %s",
				required_fields[i]);
			pool_unref(&pool);
			return -1;
		}
	}
	deserializer->required_fields = dup_required_fields;

	*deserializer_r = deserializer;
	return 0;
}

void dsync_deserializer_deinit(struct dsync_deserializer **_deserializer)
{
	struct dsync_deserializer *deserializer = *_deserializer;

	*_deserializer = NULL;

	pool_unref(&deserializer->pool);
}

int dsync_deserializer_decode_begin(struct dsync_deserializer *deserializer,
				    const char *input,
				    struct dsync_deserializer_decoder **decoder_r,
				    const char **error_r)
{
	struct dsync_deserializer_decoder *decoder;
	unsigned int i;
	char **values;
	pool_t pool;

	*decoder_r = NULL;

	pool = pool_alloconly_create("dsync deserializer decode", 1024);
	decoder = p_new(pool, struct dsync_deserializer_decoder, 1);
	decoder->pool = pool;
	decoder->deserializer = deserializer;
	values = p_strsplit_tabescaped(pool, input);

	/* fix NULLs */
	for (i = 0; values[i] != NULL; i++) {
		if (values[i][0] == NULL_CHR) {
			/* NULL? */
			if (values[i][1] == '\0')
				values[i] = NULL;
			else
				values[i] += 1;
		}
	}
	decoder->values_count = i;

	/* see if all required fields exist */
	for (i = 0; i < deserializer->required_field_count; i++) {
		unsigned int ridx = deserializer->required_field_indexes[i];

		if (ridx >= decoder->values_count || values[ridx] == NULL) {
			*error_r = t_strdup_printf("Missing required field %s",
				deserializer->required_fields[i]);
			pool_unref(&pool);
			return -1;
		}
	}
	decoder->values = (void *)values;

	*decoder_r = decoder;
	return 0;
}

static bool
dsync_deserializer_find_field(struct dsync_deserializer *deserializer,
			      const char *key, unsigned int *idx_r)
{
	unsigned int i;

	for (i = 0; deserializer->keys[i] != NULL; i++) {
		if (strcmp(deserializer->keys[i], key) == 0) {
			*idx_r = i;
			return TRUE;
		}
	}
	return FALSE;
}

bool dsync_deserializer_decode_try(struct dsync_deserializer_decoder *decoder,
				   const char *key, const char **value_r)
{
	unsigned int idx;

	if (!dsync_deserializer_find_field(decoder->deserializer, key, &idx) ||
	    idx >= decoder->values_count) {
		*value_r = NULL;
		return FALSE;
	} else {
		*value_r = decoder->values[idx];
		return *value_r != NULL;
	}
}

const char *
dsync_deserializer_decode_get(struct dsync_deserializer_decoder *decoder,
			      const char *key)
{
	const char *value;

	if (!dsync_deserializer_decode_try(decoder, key, &value)) {
		i_panic("dsync_deserializer_decode_get() "
			"used for non-required key %s", key);
	}
	return value;
}

const char *
dsync_deserializer_decoder_get_name(struct dsync_deserializer_decoder *decoder)
{
	return decoder->deserializer->name;
}

void dsync_deserializer_decode_finish(struct dsync_deserializer_decoder **_decoder)
{
	struct dsync_deserializer_decoder *decoder = *_decoder;

	*_decoder = NULL;

	pool_unref(&decoder->pool);
}
