/*
 * libsyncml - A syncml protocol implementation
 * Copyright (C) 2005  Armin Bauer <armin.bauer@opensync.org>
 * Copyright (C) 2007-2009  Michael Bell <michael.bell@opensync.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
 */

#include <libsyncml/syncml.h>

#include <libsyncml/syncml_internals.h>
#include <libsyncml/sml_elements_internals.h>
#include <libsyncml/sml_command_internals.h>
#include <libsyncml/sml_session_internals.h>
#include "libsyncml/sml_error_internals.h"

#ifdef ENABLE_WBXML

#include "sml_xml_assm.h"
#include "sml_xml_assm_internals.h"
#include "sml_xml_parse.h"
#include "sml_xml_parse_internals.h"
#include "sml_wbxml.h"
#include "sml_wbxml_internals.h"

SmlBool smlWbxmlConvertTo(WBXMLConvXML2WBXMLParams *params, const char *input, char **output, size_t *outputLen, SmlError** error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p, %p)", __func__, params, input, output, outputLen, error);
	smlAssert(input);
	smlAssert(strlen(input));
	smlAssert(output);
	smlAssert(outputLen);
	WBXMLError wberror;

	wberror = wbxml_conv_xml2wbxml((WB_UTINY*)input, (WB_UTINY**)output, (WB_ULONG*)outputLen, params);
	if (wberror != WBXML_OK)
		goto error;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error:
	smlErrorSet(error, SML_ERROR_GENERIC, (char *)wbxml_errors_string(wberror));
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

SmlBool smlWbxmlConvertFrom(WBXMLConvWBXML2XMLParams *params, const char *input, size_t inputLen, char **output, size_t *outputLen, SmlError** error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %i, %p, %p, %p)", __func__, params, input, inputLen, output, outputLen, error);
	WBXMLError wberror;

	smlTrace(TRACE_INTERNAL, "WBXML2 VERSION: %s", WBXML_LIB_VERSION);
	wberror = wbxml_conv_wbxml2xml_withlen((WB_UTINY*)input, inputLen, (WB_UTINY**)output, (WB_ULONG*)outputLen,  params);
	if (wberror != WBXML_OK)
		goto error;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
	
error:
	smlErrorSet(error, SML_ERROR_GENERIC, (char *)wbxml_errors_string(wberror));
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

static SmlBool smlWbxmlParserStart(SmlXmlParser *parser, const char *data, unsigned int size, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, parser, data, size, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(data);
	smlAssert(size);
	
	char *bin = smlPrintBinary(data, size);
	smlTrace(TRACE_INTERNAL, "Wbxml input: %s", bin);
	smlSafeCFree(&bin);
	smlLog("received-%i.wbxml", data, size);

	/* Ignorable whitespaces must be preserved because sometimes
	 * a buggy mobile does not the its data objects correctly
	 * encapsulated into CDATA.
	 */
	char *buffer = NULL;
	size_t buffer_size = 0;
	WBXMLConvWBXML2XMLParams params = {WBXML_ENCODER_XML_GEN_COMPACT, WBXML_LANG_UNKNOWN, 0, TRUE};
	if (!smlWbxmlConvertFrom(&params, data, size, &buffer, &buffer_size, error))
		goto error;
	smlTrace(TRACE_INTERNAL, "converted XML message: %s", buffer);
	
	if (!smlXmlParserStart(parser, buffer, buffer_size, error))
		goto error;
	smlSafeCFree(&buffer);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error:
	if (buffer)
		smlSafeCFree(&buffer);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

SmlXmlParser *smlWbxmlParserNew(SmlParserFunctions *functions, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, functions, error);
	CHECK_ERROR_REF
	smlAssert(functions);
	
	SmlXmlParser *parser = smlXmlParserNew(functions, error);
	if (!parser)
		goto error;
	
	functions->start = (SmlParserStartFunction)smlWbxmlParserStart;
	
	smlTrace(TRACE_EXIT, "%s: %p", __func__, parser);
	return parser;

error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return NULL;
}

SmlBool smlWbxmlAssemblerRun(SmlXmlAssembler *assm, char **data, unsigned int *size, SmlBool *end, SmlBool final, unsigned int maxsize, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p, %i, %i, %p)", __func__, assm, data, size, end, final, maxsize, error);
	CHECK_ERROR_REF
	smlAssert(assm);
	smlAssert(data);
	smlAssert(size);
	
	char *buffer = NULL;
	unsigned int buffer_size = 0;
	if (!smlXmlAssemblerRun(assm, &buffer, &buffer_size, end, final, 0, error))
		goto error;
	
	WBXMLConvXML2WBXMLParams params = {WBXML_VERSION_12, FALSE, FALSE, FALSE};
	const char *opt = smlAssemblerGetOption(assm->assembler, "USE_STRTABLE");
	if (opt && atoi(opt))
		params.use_strtbl = TRUE;
	if (!smlWbxmlConvertTo(&params, buffer, data, size, error))
		goto error;
	smlSafeCFree(&buffer);
	
	char *hex = smlPrintHex(*data, *size);
	smlTrace(TRACE_INTERNAL, "Wbxml assembled: %s", hex);
	smlSafeCFree(&hex);

	smlLog("sent-%i.wbxml", *data, *size);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error:
	if (buffer)
		smlSafeCFree(&buffer);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

unsigned int smlWbxmlAssemblerCheckSize(SmlXmlAssembler *assm, SmlBool headeronly, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %i, %p)", __func__, assm, headeronly, error);
	CHECK_ERROR_REF
	smlAssert(assm);
	
	unsigned int size = 0;
	char *data = NULL;
	char *buffer = NULL;
	unsigned int buffer_size = 0;
	if (!smlXmlAssemblerRunFull(assm, &buffer, &buffer_size, NULL, TRUE, FALSE, 0, error))
		goto error;
	
	WBXMLConvXML2WBXMLParams params = {WBXML_VERSION_12, FALSE, FALSE, FALSE};
	const char *opt = smlAssemblerGetOption(assm->assembler, "USE_STRTABLE");
	if (opt && atoi(opt))
		params.use_strtbl = TRUE;
	if (!smlWbxmlConvertTo(&params, buffer, &data, &size, error))
		goto error_free_buffer;
	
	smlSafeCFree(&data);
	smlSafeCFree(&buffer);
	
	smlTrace(TRACE_EXIT, "%s: %i", __func__, size);
	return size;

error_free_buffer:
	smlSafeCFree(&buffer);
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return 0;
}

/** @brief Creates a new XML assembler
 * 
 * @param session The session for which to create the assembler
 * @param error A pointer to an error struct
 * @return The new assembler or NULL in the case of an error
 * 
 */
SmlXmlAssembler *smlWbxmlAssemblerNew(SmlAssembler *assembler, SmlAssemblerFunctions *functions, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assembler, functions, error);
	CHECK_ERROR_REF
	
	SmlXmlAssembler *assm = smlXmlAssemblerNew(assembler, functions, error);
	if (!assm)
		goto error;
	
	functions->run = (SmlAssemblerRunFunction)smlWbxmlAssemblerRun;
	functions->check_size = (SmlAssemblerCheckFunction)smlWbxmlAssemblerCheckSize;
	
	smlTrace(TRACE_EXIT, "%s: %p", __func__, assm);
	return assm;
	
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return NULL;
}

#endif
