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

#include "myDbEnv.hpp"
#include "myXmlContainer.hpp"
using namespace DbXml;

void usage()
{
     std::cerr  << "This program deletes a DBXML document from a container. You should pre-load the\n"
                << "container using loadExamplesData.[sh|cmd] before running this example. You are\n"
                << "only required to pass this this program the path location of the database\n"
                << "environment that you specified when you pre-loaded the examples data:\n\n"

                << "\t-h <dbenv directory>" << std::endl;
        exit( -1 );
}

//Forward declare
std::string getValue( const XmlDocument &document, const std::string &XPath, XmlQueryContext &context );
void confirmDelete ( XmlContainer &container, const std::string &XPath, XmlQueryContext &context);

void doDeleteDocument( XmlContainer &container, const std::string &XPath, XmlQueryContext &context, DbTxn *txnid)
{
    ////////////////////////////////////////////////////////////////////////
    //////  Deletes a document from a DBXML container.               ///////
    ////////////////////////////////////////////////////////////////////////


    ///// some defensive code eliminated for clarity //

    try {
        std::cout << "Deleting documents for expression: '" << XPath << "' " << std::endl;

        std::cout << "Return to continue: ";
        getc(stdin);

        XmlResults results( container.queryWithXPath(txnid, XPath, &context ) );

        std::cout << "Found " << results.size() 
                  << " documents matching the expression '" 
                  << XPath << ". " << std::endl;

        XmlValue value;
        std::cout << "\n";
        while( results.next( value ) )
        {
            /// Retrieve the value as a document
            XmlDocument theDocument( value.asDocument(0) );

            /// Obtain information of interest from the document. Note that the 
            //  wildcard in the XPath expression allows us to not worry about what
            //  namespace this document uses.
            std::string item = getValue( theDocument, "/*/product/text()", context);
            std::cout << "Deleting document: " << item << std::endl;
            
            container.deleteDocument( txnid, theDocument );

            std::cout << "Deleted document: " << item << std::endl;

        }
    }
    //Catches XmlException
    catch(std::exception &e)
    {
        std::cerr << "Document deletion failed. \n";
        std::cerr << e.what() << "\n";

        //Abort the transaction. All changes made to the database since the start
        //of this transaction are discarded.
        txnid->abort();

        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 );

    //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);

    //create a context and declare the namespaces
    XmlQueryContext context;
    context.setNamespace( "fruits", "http://groceryItem.dbxml/fruits");
    context.setNamespace( "vegetables", "http://groceryItem.dbxml/vegetables");
    context.setNamespace( "desserts", "http://groceryItem.dbxml/desserts");

    //delete the document that describes Mabolo (a fruit)
    const std::string query = "/fruits:item[product = 'Mabolo']";
    doDeleteDocument( openedContainer.getContainer(), query, context, txn);

    //Commit the delete
    txn->commit( 0 );

    //The document should now no longer exist in the container. Just for fun,
    //  confirm the delete.
    confirmDelete ( openedContainer.getContainer(), query, context);

    return 0;
}

std::string getValue( const XmlDocument &document, const std::string &XPath, XmlQueryContext &context )
{
    /////////////////////////////////////////////////////////////////////////////////
    ///////////    Return specific information from a document. ///////////////////// 
    ///////////   !!!!!! Assumes a result set of size 1 !!!!!!! /////////////////////
    /////////////////////////////////////////////////////////////////////////////////
    

    // Exception handling omitted....
    
    //We don't want a document, we want a specific value. So set the
    //return type to Result Values
    context.setReturnType( XmlQueryContext::ResultValues );

    //Perform the query
    XmlResults result = document.queryWithXPath(XPath, &context);

    //We require a result set size of 1.
    assert( result.size() == 1 );

    //Get the value. If we allowed the result set to be larger than size 1,
    //we would have to loop through the results, processing each as is
    //required by our application.
    XmlValue value;
    result.next(value);

    //Set the result type back to Result Document
    context.setReturnType( XmlQueryContext::ResultDocuments );
    return value.asString( 0 );
        
}

void confirmDelete ( XmlContainer &container, const std::string &XPath, XmlQueryContext &context)
{
    // Exception handling omitted....
    
    std::cout << "Confirming the delete." << std::endl;
    std::cout << "The query: '" << XPath << "' should get result set size 0." << std::endl;
    XmlResults resultsConfirm( container.queryWithXPath(0, XPath, &context ) );
    if ( ! resultsConfirm.size() )
    {
        std::cout << "No documents found matching XPath query: '" 
            << XPath << "'." << std::endl;
        std::cout << "Deletion confirmed." << std::endl;
    } else {
        std::cout << "Found documents matching '" << XPath << "'! " 
            << resultsConfirm.size() << " documents found." << std::endl;
        std::cout << "Document deletion failed." << std::endl;
    }
    std::cout << "\n";

}
