/* 	$NetBSD$ */

/*-
 * Copyright (c) 2007 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jachym Holecek <freza@NetBSD.org>.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by the NetBSD
 *      Foundation, Inc. and its contributors.
 * 4. Neither the name of The NetBSD Foundation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/errno.h>

#include <prop/proplib.h>

#include "prop_object_impl.h"
#include "prop_codec_impl.h"


extern const struct _prop_codec 	prop_codec_xml;
extern const struct _prop_codec 	prop_codec_scn;

static prop_codec_t 			prop_codec_table[] = {
#if defined(_PROPLIB_CODEC_XML)
	&prop_codec_xml,
#endif
#if defined(_PROPLIB_CODEC_SCN)
	&prop_codec_scn,
#endif
	NULL
};

/* Default to the codec with smallest external representation available. */
#if !defined(_PROP_DEFAULT_CODEC)
#if defined(_PROPLIB_CODEC_SCN)
#define _PROP_DEFAULT_CODEC 	"scn"
#elif defined(_PROPLIB_CODEC_XML)
#define _PROP_DEFAULT_CODEC 	"xml"
#else
#define _PROP_DEFAULT_CODEC 	"" 	/* Runtime failure */
#endif
#endif

static const char *
prop_skip_space(const char *str)
{
	if (str == NULL)
		return (NULL);

	while (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r')
		str++;

	return (str);
}

static prop_codec_t
prop_codec_guess(char c)
{
	int 			i;

	/*
	 * Must be able to tell codec by the first non-white character.
	 * For binary codecs this implies their header must not begin
	 * with whitespace characters.
	 */
	for (i = 0; prop_codec_table[i]; i++)
		if (strchr((const char *)prop_codec_table[i]->codec_sense,
		    c) != NULL)
			return (prop_codec_table[i]);

	return (NULL);
}

static prop_object_t
prop_parse_single(prop_codec_t co, const char *str, prop_type_t type)
{
	prop_parser_t 		pa;
	prop_object_t 		po, pq;

	if (prop_parser_create(co, &pa))
		goto fail_0;

	if (prop_parser_exec(co, pa, (const u_char *)str, strlen(str)))
		goto fail_1;

	if ((po = prop_parser_yield(co, pa)) == NULL)
		goto fail_1;

	/* Expect ${str} contains exactly one object. */
	if ((pq = prop_parser_yield(co, pa)) != NULL) {
		prop_object_release(pq);
		goto fail_2;
	}

	if (prop_object_type(po) != type)
		goto fail_2;

	prop_parser_destroy(co, pa);
	return (po);

 fail_2:
	prop_object_release(po);
 fail_1:
	prop_parser_destroy(co, pa);
 fail_0:
	return (NULL);
}

const char *
prop_codec_list(void)
{
	static boolean_t 	doinit = TRUE;
	static char 		names[8]; 	/* XXX Large enough */
	size_t 			idx = 0;
	int 			i;

	/* XXX locking? */
	if (doinit) {
		for (i = 0; prop_codec_table[i]; i++) {
			strcpy(names + idx, prop_codec_table[i]->codec_name);
			idx += strlen(prop_codec_table[i]->codec_name);

			if (prop_codec_table[i + 1])
				names[idx++] = ' ';
		}

		names[idx] = '\0';
		doinit = FALSE;
	}

	return (names);
}

prop_codec_t
prop_codec_lookup(const char *name)
{
	int 			i;

	if (name == NULL)
		name = _PROP_DEFAULT_CODEC;

	for (i = 0; prop_codec_table[i]; i++)
		if (strcmp(prop_codec_table[i]->codec_name, name) == 0)
			return (prop_codec_table[i]);

	return (NULL);
}

prop_dictionary_t
prop_dictionary_internalize(const char *str)
{
	prop_codec_t 		co;

	if ((str = prop_skip_space(str)) == NULL)
		return (NULL);

	if ((co = prop_codec_guess(*str)) == NULL)
		return (NULL);

	if (co->codec_dictionary_internalize)
		return ((co->codec_dictionary_internalize)(str));

	return (prop_parse_single(co, str, PROP_TYPE_DICTIONARY));
}

prop_array_t
prop_array_internalize(const char *str)
{
	prop_codec_t 		co;

	if ((str = prop_skip_space(str)) == NULL)
		return (NULL);

	if ((co = prop_codec_guess(*str)) == NULL)
		return (NULL);

	if (co->codec_array_internalize)
		return ((co->codec_array_internalize)(str));

	return (prop_parse_single(co, str, PROP_TYPE_ARRAY));
}

char *
prop_dictionary_externalize(prop_dictionary_t pd)
{
	prop_codec_t 		co = prop_codec_lookup(_PROP_DEFAULT_CODEC);

	_PROP_ASSERT(co);

	if (co->codec_dictionary_externalize)
		return ((co->codec_dictionary_externalize)(pd));

	if (co->codec_externalize_compound)
		return ((co->codec_externalize_compound)(pd));

	return (NULL);
}

char *
prop_array_externalize(prop_array_t pa)
{
	prop_codec_t 		co = prop_codec_lookup(_PROP_DEFAULT_CODEC);

	_PROP_ASSERT(co);

	if (co->codec_array_externalize)
		return ((co->codec_array_externalize)(pa));

	if (co->codec_externalize_compound)
		return ((co->codec_externalize_compound)(pa));

	return (NULL);
}

char *
prop_codec_externalize(prop_codec_t co, prop_object_t po)
{
	_PROP_ASSERT(co);

	if (co->codec_externalize_compound)
		return ((co->codec_externalize_compound)(po));

	switch (prop_object_type(po)) {
	case PROP_TYPE_DICTIONARY:
		return ((co->codec_dictionary_externalize)(po));
	case PROP_TYPE_ARRAY:
		return ((co->codec_array_externalize)(po));
	default:
		return (NULL);
	}
}

int
prop_parser_create(prop_codec_t co, prop_parser_t *pp)
{
	_PROP_ASSERT(co);

	if (pp == NULL)
		return (EINVAL);

	if (co->codec_parser_create)
		return ((co->codec_parser_create)(pp));

	return (EOPNOTSUPP);
}

int
prop_parser_exec(prop_codec_t co, prop_parser_t pa, const u_char *str,
    size_t len)
{
	_PROP_ASSERT(co);

	if (co->codec_parser_exec)
		return ((co->codec_parser_exec)(pa, str, len));

	return (EOPNOTSUPP);
}

prop_object_t
prop_parser_yield(prop_codec_t co, prop_parser_t pa)
{
	_PROP_ASSERT(co);

	if (co->codec_parser_yield)
		return ((co->codec_parser_yield)(pa));

	return (NULL);
}

void
prop_parser_destroy(prop_codec_t co, prop_parser_t pa)
{
	_PROP_ASSERT(co);

	if (co->codec_parser_destroy)
		(co->codec_parser_destroy)(pa);
}
