/* $Id: TestXmlsq.c $ */

/*
*   Last updated:
*   $Date: 2021-07-18 10:29 $
*   $Version: 1.0.0 $
*/
/* Some tests using the diXmlsq C/C++ interface.
* Please report any bugs to <http://cryptosys.net/contact/>
*/
/******************************* LICENSE ***********************************
* Copyright (C) 2020-21 David Ireland, DI Management Services Pty Limited.
* All rights reserved. <www.di-mgt.com.au> <www.cryptosys.net>
* The code in this module is licensed under the terms of the MIT license,
* unless otherwise marked.
* For a copy, see <http://opensource.org/licenses/MIT>
****************************************************************************
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "diXmlsq.h"

/*
* Requires `xmlsq` to be installed on your system, available from <https://www.cryptosys.net/xmlsq/>.
* In particular that `diXmlsq.dll` is in your library path.
* You must link to `diXmlsq.lib`. In MSVC++ IDE, use
* Project > Properties > Linker > Input > Additional Dependencies and add the full path to `diXmlsq.lib`.
* E.g.
* Additional Dependencies = $(OutDir)diXmlsq.lib;%(AdditionalDependencies)

* Using command-line:
*     CL TestXmlsq.c /link ..\Release\diXmlsq.lib
*
* Test files are in `xmlsq-testfiles.zip`.
*/

#ifdef NDEBUG
/* Make sure assertion testing is turned on */
#undef NDEBUG
#endif
#include <assert.h>

/* Print quoted string to stdout */
static void pr_quoted_str(const char *prefix, const char *s, const char * suffix)
{
	if (prefix) printf("%s", prefix);
	printf("'%s'", s);
	if (suffix) printf("%s", suffix);
}

int main(void)
{
	long n, i;
	const char *xmlfile;
	const char *query;
	char sbuf[256];
	char qbuf[128];
	char *buf;
	long nchars;

	n = XMLSQ_Gen_Version();
	printf("Version = %05ld\n", n);

	xmlfile = "bookstore.xml";
	printf("FILE: %s\n", xmlfile);

	query = "/";
	printf("Get the root element...\n");
	printf("Query: %s\n", query);
	nchars = XMLSQ_GetText(NULL, 0, xmlfile, query, 0);
	assert(nchars >= 0);
	buf = malloc(nchars + 1);
	nchars = XMLSQ_GetText(buf, nchars, xmlfile, query, 0);
	printf("%s\n", buf);
	free(buf);

	query = "(//title)[2]";
	printf("Get text of the 2nd title element...\n");
	printf("Query: %s\n", query);
	nchars = XMLSQ_GetText(NULL, 0, xmlfile, query, 0);
	assert(nchars >= 0);
	buf = malloc(nchars + 1);
	nchars = XMLSQ_GetText(buf, nchars, xmlfile, query, 0);
	printf("%s\n", buf);
	free(buf);

	query = "//title";
	printf("Get text of the 1st title element...\n");
	printf("Query: %s\n", query);
	nchars = XMLSQ_GetText(NULL, 0, xmlfile, query, 0);
	assert(nchars >= 0);
	buf = malloc(nchars + 1);
	nchars = XMLSQ_GetText(buf, nchars, xmlfile, query, 0);
	printf("%s\n", buf);
	free(buf);

	query = "//title";
	printf("Full Xpath query...\n");
	printf("Query: %s\n", query);
	nchars = XMLSQ_FullQuery(NULL, 0, xmlfile, query, 0);
	assert(nchars >= 0);
	buf = malloc(nchars + 1);
	nchars = XMLSQ_FullQuery(buf, nchars, xmlfile, query, 0);
	printf("%s\n", buf);
	free(buf);

	query = "//title";
	printf("Full Xpath query with `raw` option...\n");
	printf("Query: %s\n", query);
	nchars = XMLSQ_FullQuery(NULL, 0, xmlfile, query, XMLSQ_RAW);
	assert(nchars >= 0);
	buf = malloc(nchars + 1);
	nchars = XMLSQ_FullQuery(buf, nchars, xmlfile, query, XMLSQ_RAW);
	printf("%s\n", buf);
	free(buf);

	query = "(//title)[3]";
	printf("Get text of the 3rd title element (UTF-8-encoded)...\n");
	// To show properly in cmd type ``chcp 65001`` before calling.
	printf("Query: %s\n", query);
	nchars = XMLSQ_GetText(NULL, 0, xmlfile, query, 0);
	assert(nchars >= 0);
	buf = malloc(nchars + 1);
	nchars = XMLSQ_GetText(buf, nchars, xmlfile, query, 0);
	printf("%s\n", buf);
	free(buf);

	query = "(//title)[3]";
	printf("Get text of the 3rd title element (asciified)...\n");
	printf("Query: %s\n", query);
	nchars = XMLSQ_GetText(NULL, 0, xmlfile, query, XMLSQ_ASCIIFY);
	assert(nchars >= 0);
	buf = malloc(nchars + 1);
	nchars = XMLSQ_GetText(buf, nchars, xmlfile, query, XMLSQ_ASCIIFY);
	printf("%s\n", buf);
	free(buf);

	query = "//book/title/@lang";
	printf("Get text of `lang` attribute in first title element ...\n");
	printf("Query: %s\n", query);
	// Use fixed-length output buffer (!DANGER!)
	nchars = XMLSQ_GetText(sbuf, sizeof(sbuf) - 1, xmlfile, query, 0);
	assert(nchars >= 0);
	printf("%s\n", sbuf);

	query = "//book/title/@lang";
	printf("Same query in full-query mode...\n");
	printf("Query: %s\n", query);
	// Use fixed-length output buffer (!DANGER!)
	nchars = XMLSQ_FullQuery(sbuf, sizeof(sbuf) - 1, xmlfile, query, 0);
	assert(nchars >= 0);
	printf("%s\n", sbuf);


	// USE COUNT TO ITERATE MATCHING ELEMENTS

	query = "//title";
	printf("Use the Count function to query each matching element in turn...\n");
	printf("Query: %s\n", query);
	n = XMLSQ_Count(xmlfile, query, 0);
	printf("Count: %ld\n", n);

	for (i = 1; i <= n; i++) {	// NB one-based
		// Compose query
		sprintf(qbuf, "(//title)[%ld]", i);
		printf(" Query: %s\n", qbuf);
		// then use it
		nchars = XMLSQ_GetText(sbuf, sizeof(sbuf) - 1, xmlfile, qbuf, XMLSQ_ASCIIFY);
		assert(nchars >= 0);
		printf(" %s\n", sbuf);
	}
	// HANDLE ERROR STRINGS

	printf("\nTEST ERRORS...\n");
	// Bad query
	query = "///badquery";
	printf("Query: %s\n", query);
	nchars = XMLSQ_GetText(NULL, 0, xmlfile, query, 0);
	assert(nchars < 0);
	if (nchars < 0) {
		// return -N => error string is of length N
		nchars = -nchars;
		// Allocate buffer to receive error message
		buf = malloc(nchars + 1);
		nchars = XMLSQ_GetText(buf, nchars, xmlfile, query, 0);
		printf("%s\n", buf);
		free(buf);
	}

	// Missing file
	xmlfile = "missing.file";
	query = "//a";
	printf("FILE: %s\n", xmlfile);
	nchars = XMLSQ_GetText(NULL, 0, xmlfile, query, 0);
	assert(nchars < 0);
	if (nchars < 0) {
		// return -N => error string is of length N
		nchars = -nchars;
		// Allocate buffer to receive error message
		buf = malloc(nchars + 1);
		nchars = XMLSQ_GetText(buf, nchars, xmlfile, query, 0);
		printf("%s\n", buf);
		free(buf);
	}

	// Input not valid XML
	xmlfile = "notxml.txt";
	query = "//a";
	printf("FILE: %s\n", xmlfile);
	nchars = XMLSQ_GetText(NULL, 0, xmlfile, query, 0);
	assert(nchars < 0);
	if (nchars < 0) {
		// return -N => error string is of length N
		nchars = -nchars;
		// Allocate buffer to receive error message
		buf = malloc(nchars + 1);
		nchars = XMLSQ_GetText(buf, nchars, xmlfile, query, 0);
		printf("%s\n", buf);
		free(buf);
	}


	// TEST EMPTY VALUES

	printf("\nTESTING FOR EMPTY VALUES...\n");

	xmlfile = "<a><b foo=''></b><c>test1</c><e/></a>";	// Pass XML as a string
	printf("xml=%s\n", xmlfile);
	query = "/";
	printf("Query: %s\n", query);
	nchars = XMLSQ_GetText(sbuf, sizeof(sbuf) - 1, xmlfile, query, 0);
	assert(nchars >= 0);
	printf("%s\n", sbuf);

	printf("Get text of element b (empty, so add quote delimiters to see something)...\n");
	query = "a/b";
	printf("Query: %s\n", query);
	nchars = XMLSQ_GetText(sbuf, sizeof(sbuf) - 1, xmlfile, query, 0);
	assert(nchars >= 0);
	pr_quoted_str("", sbuf, "\n");

	printf("And empty attribute foo=\"\"...\n");
	query = "//b/@foo";
	printf("Query: %s\n", query);
	nchars = XMLSQ_GetText(sbuf, sizeof(sbuf) - 1, xmlfile, query, 0);
	assert(nchars >= 0);
	pr_quoted_str("", sbuf, "\n");

	printf("And empty element e...\n");
	query = "//e";
	printf("Query: %s\n", query);
	nchars = XMLSQ_GetText(sbuf, sizeof(sbuf) - 1, xmlfile, query, 0);
	assert(nchars >= 0);
	pr_quoted_str("", sbuf, "\n");

	printf("But we get the same result if the element is missing...\n");
	query = "//notthere";
	printf("Query: %s\n", query);
	nchars = XMLSQ_GetText(sbuf, sizeof(sbuf) - 1, xmlfile, query, 0);
	assert(nchars >= 0);
	pr_quoted_str("", sbuf, "\n");

	printf("To differentiate: use the Count function...\n");
	query = "//e";
	printf("Query: %s\n", query);
	n = XMLSQ_Count(xmlfile, query, 0);
	printf("Count=%ld\n", n);
	assert(n > 0);

	query = "//notthere";
	printf("Query: %s\n", query);
	n = XMLSQ_Count(xmlfile, query, 0);
	printf("Count=%ld\n", n);
	assert(n == 0);




	return 0;
}