#include <fstream>
#include <cstdio>
#include <cassert>

#include "myDbEnv.hpp"
#include "myXmlContainer.hpp"

using namespace DbXml;

void usage()
{
	std::cerr  <<  "This program modifies documents found in a DBXML container. You should\n"
		   <<  "pre-load the container using loadExamplesData.[sh|cmd] before running this\n"
		   <<  "example. You are only required to pass this command the path location of the\n"
		   <<  "database environment that you specified when you pre-loaded the examples\n"
		   <<  "data:\n\n"
		
		   <<   "\t-h <dbenv directory>" << std::endl;
        exit( -1 );
}

// print out the string values of matching documents
void dumpDocuments(DbTxn *txn, XmlContainer &container, XmlQueryExpression &expression)
{
	////////////////////////////////////////////////////////////////////////
	//////  Updates a document stored in a DBXML container.          ///////
	////////////////////////////////////////////////////////////////////////
	
	///// some defensive code eliminated for clarity //
	
	try {
		XmlResults results( container.queryWithXPath(txn, expression ) );
		std::cout << "Found " << results.size() << " documents matching the expression '" << expression.getXPathQuery() << ". " << std::endl;
		XmlValue value;
		std::cout << "\n";
		while( results.next(value) ) {
			XmlDocument theDocument = value.asDocument();
			std::string docString;
			//Retrieve the document's value as a string.
			theDocument.getContentAsString(docString);
			std::cout << "Document content: \n" << docString << std::endl;
		}
	}
	//Catches XmlException
	catch(std::exception &e)
	{
		if (txn)
		    txn->abort();
		std::cerr << "Document query failed. ";
		std::cerr << e.what() << "\n";
		exit( -1 );
	}
}

int main(int argc, char **argv)
{
	
	std::string path2DbEnv;
	std::string theContainer = "namespaceExampleData.dbxml";
	for ( int i=1; i<argc; i++ )
	{
		if ( argv[i][0] == '-' )
		{
			switch(argv[i][1])
			{
			case 'h':
				path2DbEnv = argv[++i];
				break;
			default:
				usage();
			}
		}
	}
	
	if (! path2DbEnv.length() )
		usage();
	
	//open a container in the db environment
	myDbEnv theDBEnv( path2DbEnv ); 
	myXmlContainer openedContainer( theContainer, theDBEnv );
	
	
	//create a context and declare the namespaces
	XmlQueryContext context(XmlQueryContext::ResultDocumentsAndValues, XmlQueryContext::Eager);
	context.setNamespace( "fruits", "http://groceryItem.dbxml/fruits");
	context.setNamespace( "vegetables", "http://groceryItem.dbxml/vegetables");
	context.setNamespace( "desserts", "http://groceryItem.dbxml/desserts");
	const std::string XPath = "/fruits:item/product[text() = 'Zapote Blanco']";
	
	XmlQueryExpression expression = openedContainer.getContainer().parseXPathExpression(0, XPath, &context);
	
	std::cout << "Updating document for the expression: '" << XPath << "' " << std::endl;
	std::cout << "Return to continue: ";
	getc(stdin);
	
	// print the document(s) to be updated -- those that describe Zapote Blanco (a fruit)
	// The document strings are only being printed to show before/after results.
	// Most modification programs would not perform the additional queries.
	dumpDocuments(0, openedContainer.getContainer(), expression);
	
	std::cout << "About to update the document(s) above.\nLook for a new element after the target element, named 'description' ..." << std::endl;
	std::cout << "Return to continue: ";
	getc(stdin);
	
	//myDbEnv and myXmlContainer open with transactions. All subsequent
	//writes to them must also be performed inside a transaction.
	//Get a transaction
	DbTxn *txn;
	theDBEnv.getDbEnv().txn_begin(0, &txn, 0);
	
	try {
		// the modification is a new element in the target node, called "descripton,
		// which goes immediately after the <product> element.
		// if this program is run more than once, and committed, additional
		// identical elements are added.  It is easy to modify this program
		// to change the modification.
		XmlModify modify(expression, XmlModify::InsertAfter, XmlModify::Element, "description", "very nice");
		openedContainer.getContainer().modifyDocument(txn, modify);
		std::cout << "Peformed " << modify.getNumModifications() << " modification operations" << std::endl;
		//Catches XmlException
	} catch(std::exception &e)
	{
		txn->abort();
		std::cerr << "Document modification failed. ";
		std::cerr << e.what() << "\n";
		exit( -1 );
	}
	
	// print the modified documents
	dumpDocuments(txn, openedContainer.getContainer(), expression);
	
	std::cout << "If committed, this program will add a new element each time it is run." << std::endl;
	std::cout << "Press 'c' to commit this change:";
	char cmt = getc(stdin);
	if (cmt == 'c' || cmt == 'C')
		txn->commit( 0 );
	else
		txn->abort();
	return 0;
}
