#include <string.h>
#include <libxml/xmlwriter.h>
#include "oxml.h"

const char *XMLENCODING = "utf-8";
xmlTextWriterPtr writer;
xmlDocPtr doc;

/**
 * Below function was from libxml2 examples
 *
 * ConvertInput:
 * @in: string in a given encoding
 * @encoding: the encoding used
 *
 * Converts @in into UTF-8 for processing with libxml2 APIs
 *
 * Returns the converted UTF-8 string, or NULL in case of error.
 */
static xmlChar *
ConvertInput(const char *in, const char *encoding)
{
	xmlChar *out;
	xmlCharEncodingHandlerPtr handler;
	xmlBufferPtr inbuf, outbuf;
	int ret;

	if (in == 0)
		return 0;

	handler = xmlFindCharEncodingHandler(encoding);

	if (!handler) {
		printf("ConvertInput: no encoding handler found for '%s'\n",
		       encoding ? encoding : "");
		return 0;
	}

	inbuf = xmlBufferCreate();
	if (inbuf)
		xmlBufferAdd(inbuf, (const xmlChar *)in, strlen(in));
	outbuf = xmlBufferCreate();

	if (inbuf == NULL || outbuf == NULL) {
		printf("ConvertInput: unable to allocate memory\n");
		if (inbuf) xmlBufferFree(inbuf);
		if (outbuf) xmlBufferFree(outbuf);
		xmlCharEncCloseFunc(handler);
		return 0;
	}

	ret = xmlCharEncInFunc(handler, outbuf, inbuf);
	if (ret < 0) {
		printf("ConvertInput: charset encoding conversion failed.\n");
		xmlBufferFree(inbuf);
		xmlBufferFree(outbuf);
		xmlCharEncCloseFunc(handler);
		return 0;
	}

	out = xmlStrdup(xmlBufferContent(outbuf));

	xmlBufferFree(inbuf);
	xmlBufferFree(outbuf);
	xmlCharEncCloseFunc(handler);

	return out;
}

static int XMLDEF(char *name, const char *format, ...) {
	char buffer[1024];
	va_list argp;
	int rc;
	xmlChar *tmp;

	va_start(argp, format);
	vsnprintf(buffer, sizeof(buffer), format, argp);
	va_end(argp);
	tmp = ConvertInput(buffer, XMLENCODING);
	rc = xmlTextWriterWriteElement(writer, BAD_CAST name, tmp);
	if (rc < 0) {
		printf("testXmlwriterTree: Error at xmlTextWriterWriteElement\n");
		return rc;
	}
	if (tmp != NULL)
		xmlFree(tmp);
	return 0;
}

static void XMLBOX(char *name) {
	int rc = xmlTextWriterStartElement(writer, BAD_CAST name);
	if (rc < 0) {
		printf("testXmlwriterTree: Error at xmlTextWriterStartElement\n");
		return;
	}
}

static void XMLRETURN(void)  {
	int rc = xmlTextWriterEndElement(writer);
	if (rc < 0) {
		printf("testXmlwriterTree: Error at xmlTextWriterStartElement\n");
		return;
	}
}

static void XMLSTART(void) {
	writer = xmlNewTextWriterDoc(&doc, 0);
	if (writer == NULL) {
		printf("testXmlwriterDoc: Error creating the xml writer\n");
		return;
	}

	xmlTextWriterSetIndent(writer, 1);

	int rc = xmlTextWriterStartDocument(writer, NULL, XMLENCODING, NULL);
	if (rc < 0) {
		printf("testXmlwriterTree: Error at xmlTextWriterStartDocument\n");
		return;
	}
	XMLBOX("lsdvd");
}

static void XMLSTOP(void) {
	XMLRETURN();
	xmlFreeTextWriter(writer);
	xmlSaveFileEnc("/dev/stdout", doc, XMLENCODING);
	xmlFreeDoc(doc);
}

void oxml_print(struct dvd_info *dvd_info) {
	int j, i;

	XMLSTART();
	XMLDEF("device", "%s", dvd_info->discinfo.device);
	XMLDEF("title", "%s", dvd_info->discinfo.disc_title);
	XMLDEF("vmg_id", "%.12s", dvd_info->discinfo.vmg_id);
	XMLDEF("provider_id", "%.32s", dvd_info->discinfo.provider_id);

	for (j=0; j < dvd_info->title_count; j++)
	{
	if ( opt_t == j+1 || opt_t == 0 ) {

	/* GENERAL */
	if (dvd_info->titles[j].enabled) {

		XMLBOX("track");
		XMLDEF("ix", "%d", j+1);
		XMLDEF("length", "%.3f", dvd_info->titles[j].general.length);
		XMLDEF("vts_id", "%.12s", dvd_info->titles[j].general.vts_id);

		if (dvd_info->titles[j].parameter.format != NULL ) {
			XMLDEF("vts", "%d", dvd_info->titles[j].parameter.vts);
			XMLDEF("ttn", "%d", dvd_info->titles[j].parameter.ttn);
			XMLDEF("fps", "%.2f", dvd_info->titles[j].parameter.fps);
			XMLDEF("format", "%s", dvd_info->titles[j].parameter.format);
			XMLDEF("aspect", "%s", dvd_info->titles[j].parameter.aspect);
			XMLDEF("width", "%s", dvd_info->titles[j].parameter.width);
			XMLDEF("height", "%s", dvd_info->titles[j].parameter.height);
			XMLDEF("df", "%s", dvd_info->titles[j].parameter.df);
		}

		/* PALETTE */
		if (dvd_info->titles[j].palette != NULL ) {
			XMLBOX("palette");
			for (i=0; i < 16; i++) {
				XMLDEF("color","%06x", dvd_info->titles[j].palette[i]);
			}
			XMLRETURN();
		}

		/* ANGLES */
		if (dvd_info->titles[j].angle_count) { /* poor check, but there's no other info anyway. */
			XMLDEF("angles", "%d", dvd_info->titles[j].angle_count);
		}

		/* AUDIO */
		if (dvd_info->titles[j].audiostreams != NULL ) {
			for (i=0; i<dvd_info->titles[j].audiostream_count; i++)
			{
				XMLBOX("audio");
				XMLDEF("ix", "%d", i+1);
				XMLDEF("langcode", "%s", dvd_info->titles[j].audiostreams[i].langcode);
				XMLDEF("language", "%s", dvd_info->titles[j].audiostreams[i].language);
				XMLDEF("format", "%s", dvd_info->titles[j].audiostreams[i].format);
				XMLDEF("frequency", "%s", dvd_info->titles[j].audiostreams[i].frequency);
				XMLDEF("quantization", "%s", dvd_info->titles[j].audiostreams[i].quantization);
				XMLDEF("channels", "%d", dvd_info->titles[j].audiostreams[i].channels);
				XMLDEF("ap_mode", "%d", dvd_info->titles[j].audiostreams[i].ap_mode);
				XMLDEF("content", "%s", dvd_info->titles[j].audiostreams[i].content);
				XMLDEF("streamid", "0x%x", dvd_info->titles[j].audiostreams[i].streamid);
				XMLRETURN();
			}
		}

		/* CHAPTERS */
		if (dvd_info->titles[j].chapters != NULL) {
			for (i=0; i<dvd_info->titles[j].chapter_count; i++)
			{
				XMLBOX("chapter");
				XMLDEF("ix", "%d", i+1);
				XMLDEF("length", "%.3f", dvd_info->titles[j].chapters[i].length);
				XMLDEF("startcell", "%d", dvd_info->titles[j].chapters[i].startcell);
				XMLRETURN();
			}
		}

		/* CELLS */
		if (dvd_info->titles[j].cells != NULL) {
			for (i=0; i<dvd_info->titles[j].cell_count; i++)
			{
				XMLBOX("cell");
				XMLDEF("ix", "%d", i+1);
				XMLDEF("length", "%.3f", dvd_info->titles[j].cells[i].length);
				XMLRETURN();
			}
		}

		/* SUBTITLES */
		if (dvd_info->titles[j].subtitles != NULL) {
			for (i=0; i<dvd_info->titles[j].subtitle_count; i++)
			{
				XMLBOX("subp");
				XMLDEF("ix", "%d", i+1);
				XMLDEF("langcode", "%s", dvd_info->titles[j].subtitles[i].langcode);
				XMLDEF("language", "%s", dvd_info->titles[j].subtitles[i].language);
				XMLDEF("content", "%s", dvd_info->titles[j].subtitles[i].content);
				XMLDEF("streamid", "0x%x", dvd_info->titles[j].subtitles[i].streamid);
				XMLRETURN();
			}
		}
	XMLRETURN();
	}
	}
	}
	XMLDEF("dvddiscid", "%s", dvd_info->dvddiscid);

	if (! opt_t) {
		XMLDEF("longest_track", "%d", dvd_info->longest_track);
	}
	XMLSTOP();
}
