#include <cstdlib>
#include <iostream>
#include <sys/time.h>
#include "SWObjects.hpp"
#include "SPARQLfedParser/SPARQLfedParser.hpp"
#include "SPARQLSerializer.hpp"

/*
 * Build and usage example:
 * g++ -g -O0 -W -Wall -Wextra -Wnon-virtual-dtor -ansi -std=c++98 \
-I../swobjects/trunk -I../swobjects/trunk/lib -I../swobjects/trunk/SPARQLfedParser\
 -I../swobjects/trunk/TurtleSParser -I../swobjects/trunk/lib -pipe -c \
-o sparql2n3.o sparql2n3.cpp
 * g++ -o sparql2n3 sparql2n3.o -L../swobjects/trunk/lib -lSWObjects
 * ./sparql2n3 ../IARPA-PIR/query7.rq
 *
 */

using namespace w3c_sw;
class Sparql2n3 : public w3c_sw::SPARQLSerializer {

/* Declare some variables for out-of-order serialization. */
    std::stringstream preWhere;
    //std::set<const Variable*> knownVars;
    std::set<std::string> knownVars;
    bool starFlag;
    bool constructFlag;

    virtual void starVarSet (const StarVarSet* const) {
        //preWhere << ret;
        //std::cout << " RET tellp() before STAR " << ret.tellp() << "..." << std::endl;
        //ret << " STAR ";
        //std::cout << " RET tellp()  after STAR " << ret.tellp() << "..." << std::endl;
        starFlag = 1;
        }

   // virtual void variable (const Variable* v const, std::string lexicalValue) {

   //     }

    void _BasicGraphPattern (const BasicGraphPattern* self, const ProductionVector<const TriplePattern*>* p_TriplePatterns, const ProductionVector<const Filter*>* p_Filters, bool p_allOpts) {
	if (debug & DEBUG_graphs) ret << ' ' << self;
	//ret << std::endl;
	depth++;
	if (p_allOpts)
	    for (std::vector<const TriplePattern*>::const_iterator triple = p_TriplePatterns->begin();
		 triple != p_TriplePatterns->end(); triple++) {
		//lead();
        ret << "optional {" << std::endl << "  ";
		depth++;
		(*triple)->express(this);
		depth--;
		//lead();
        //ret  << std::endl;;
	    }
	else
	    p_TriplePatterns->express(this);
	p_Filters->express(this);
	depth--;
	//lead();
	//ret << std::endl;
    }

    virtual void namedGraphPattern (const NamedGraphPattern* const self, const POS* p_name, bool p_allOpts, const ProductionVector<const TriplePattern*>* p_TriplePatterns, const ProductionVector<const Filter*>* p_Filters) {
	//lead();
	p_name->express(this);
	ret << ' ';
	_BasicGraphPattern(self, p_TriplePatterns, p_Filters, p_allOpts);
    }

    virtual void defaultGraphPattern (const DefaultGraphPattern* const self, bool p_allOpts, const ProductionVector<const TriplePattern*>* p_TriplePatterns, const ProductionVector<const Filter*>* p_Filters) {
	//lead();
	_BasicGraphPattern(self, p_TriplePatterns, p_Filters, p_allOpts);
    }

    virtual void defaultGraphClause (const DefaultGraphClause* const, const POS* p_IRIref) {
	ret << std::endl << "s:source ";
	p_IRIref->express(this);
	ret << ";";
    }

    virtual void namedGraphClause (const NamedGraphClause* const, const POS* p_IRIref) {
	ret << std::endl << "s:source ";
	p_IRIref->express(this);
	ret << ";";
    }

    virtual void bnode (const BNode* const, std::string lexicalValue) {
	if (!constructFlag) ret << "_:" << lexicalValue; // rewrite when combining named BNodes from multiple docs?
    }

    virtual void solutionModifier (const SolutionModifier* const, std::vector<s_OrderConditionPair>* p_OrderConditions, int p_limit, int p_offset) {
	lead();
	//if (p_limit != LIMIT_None) ret << "LIMIT " << p_limit << std::endl;
	//if (p_offset != OFFSET_None) ret << "OFFSET " << p_offset << std::endl;
	//if (p_OrderConditions) {
	//    ret << "ORDER BY ";
	//    for (size_t i = 0; i < p_OrderConditions->size(); i++) {
	//	if (p_OrderConditions->at(i).ascOrDesc == ORDER_Desc) ret << "DESC ";
	//	p_OrderConditions->at(i).expression->express(this);
	//    }
	    ret << std::endl;
	}

    virtual void tableDisjunction (const TableDisjunction* const self, const ProductionVector<const TableOperation*>* p_TableOperations, const ProductionVector<const Filter*>* p_Filters) {
	lead();
	if (debug & DEBUG_graphs) ret << ' ' << self;
	//ret << std::endl;
	depth++;
	for (std::vector<const TableOperation*>::const_iterator it = p_TableOperations->begin();
	     it != p_TableOperations->end(); ++it) {
	    if (it != p_TableOperations->begin()) {
		lead(depth - 1);
		//ret << "UNION" << std::endl;
	    }
	    (*it)->express(this);
	}
	p_Filters->express(this);
	depth--;
	lead();
	ret << std::endl;
    }

    virtual void tableConjunction (const TableConjunction* const self, const ProductionVector<const TableOperation*>* p_TableOperations, const ProductionVector<const Filter*>* p_Filters) {
	lead();
	if (debug & DEBUG_graphs) ret << ' ' << self;
	//ret << "debug" << std::endl;
	depth++;
	p_TableOperations->express(this);
	p_Filters->express(this);
	depth--;
	lead();
	ret << std::endl;
    }

    virtual void optionalGraphPattern (const OptionalGraphPattern* const self, const TableOperation* p_GroupGraphPattern) {
	lead();
	//ret << "s:OptionalGraphPattern [ " << std::endl;
	if (debug & DEBUG_graphs) ret << ' ' << self;
	//depth++;
	p_GroupGraphPattern->express(this);
 	//depth--;
    //ret << "]; " << std::endl;
    }

    virtual void triplePattern (const TriplePattern* const, const POS* p_s, const POS* p_p, const POS* p_o) {
	lead();
    if (!constructFlag) ret << "s:triplePattern  { ";
	p_s->express(this);
	if (!constructFlag) ret << ' ';
	p_p->express(this);
    if (!constructFlag)	ret << ' ';
	p_o->express(this);
    if (!constructFlag)	ret << " };" << std::endl;
    }

    virtual void whereClause (const WhereClause* const, const TableOperation* p_GroupGraphPattern, const BindingClause* p_BindingClause) {
	//ret << "s:WhereClause :WHERE." << std::endl << std::endl;
        ret << "s:clause [" << std::endl;
	    p_GroupGraphPattern->express(this);
	    if (p_BindingClause) p_BindingClause->express(this);
        //ret << std::endl << "]; " << std::endl;

        //ericP's code
        //std::stringstream t;
        //t = ret;
        //ret = preWhere;


        //std::cout << " RET at end of WHERE " << ret << "..." << std::endl;

        }

        virtual void filter (const Filter* const, const Expression* p_Constraint) {
	    lead();
	    //ret << "s:Filter [ ";
	    p_Constraint->express(this); // but only express if this is a binding clause. how?
        //ret << "]; ";
    }

    virtual void functionCall (const FunctionCall* const, const URI* p_IRIref, const ArgList* p_ArgList) {

	if (p_IRIref->matches("http://www.w3.org/TR/rdf-sparql-query/#func-str"))
	    ret << "str";
	else if (p_IRIref->matches("http://www.w3.org/TR/rdf-sparql-query/#func-lang"))
	    ret << "lang";
	else if (p_IRIref->matches("http://www.w3.org/TR/rdf-sparql-query/#func-langMatches"))
	    ret << "langMatches";
	else if (p_IRIref->matches("http://www.w3.org/TR/rdf-sparql-query/#func-datatype"))
        //Don't express datatype so we can make policies.	    
        //ret << "datatype";
	    //ret << "(";
	    {p_ArgList->express(this);
	    //ret << ")";
        return;}
	else if (p_IRIref->matches("http://www.w3.org/TR/rdf-sparql-query/#func-bound"))
	    ret << "bound";
	else if (p_IRIref->matches("http://www.w3.org/TR/rdf-sparql-query/#func-sameTerm"))
	    ret << "sameTerm";
	else if (p_IRIref->matches("http://www.w3.org/TR/rdf-sparql-query/#func-isIRI"))
	    ret << "isIRI";
	else if (p_IRIref->matches("http://www.w3.org/TR/rdf-sparql-query/#func-isIRI"))
	    ret << "isIRI";
	else if (p_IRIref->matches("http://www.w3.org/TR/rdf-sparql-query/#func-isBlank"))
	    ret << "isBlank";
	else if (p_IRIref->matches("http://www.w3.org/TR/rdf-sparql-query/#func-isLiteral"))
	    ret << "isLiteral";
	else
	    p_IRIref->express(this);
	ret << "(";
	p_ArgList->express(this);
	ret << ")";
    }

/* Expressions */
    virtual void booleanNegation (const BooleanNegation* const, const Expression* p_Expression) {
	ret << " s:booleanNOT ";
	p_Expression->express(this);
    }

    virtual void arithmeticNegation (const ArithmeticNegation* const, const Expression* p_Expression) {
	start(PREC_Neg);
	ret << "s:arithmeticNegation ";
	p_Expression->express(this);
	end();
    }
    virtual void arithmeticInverse (const ArithmeticInverse* const, const Expression* p_Expression) {
	start(PREC_Divide);
	ret << " s:arithmeticInverse ";
	p_Expression->express(this);
	end();
    }
    virtual void booleanConjunction (const BooleanConjunction* const, const ProductionVector<const Expression*>* p_Expressions) {
	start(PREC_And);
	for (std::vector<const Expression*>::const_iterator it = p_Expressions->begin();
	     it != p_Expressions->end(); ++it) {
	    if (it != p_Expressions->begin())
		ret << " s:booleanAND ";
	    (*it)->express(this);
	}
	end();
    }
    virtual void booleanDisjunction (const BooleanDisjunction* const, const ProductionVector<const Expression*>* p_Expressions) {
	start(PREC_Or);
	for (std::vector<const Expression*>::const_iterator it = p_Expressions->begin();
	     it != p_Expressions->end(); ++it) {
	    if (it != p_Expressions->begin())
		ret << " s:booleanOR ";
	    (*it)->express(this);
	}
	end();
    }
    virtual void arithmeticSum (const ArithmeticSum* const, const ProductionVector<const Expression*>* p_Expressions) {
	start(PREC_Plus);
	for (std::vector<const Expression*>::const_iterator it = p_Expressions->begin();
	     it != p_Expressions->end(); ++it) {
	    if (it != p_Expressions->begin())
		ret << " s:arithmeticSum ";
	    (*it)->express(this);
	}
	end();
    }
    virtual void arithmeticProduct (const ArithmeticProduct* const, const ProductionVector<const Expression*>* p_Expressions) {
	start(PREC_Times);
	for (std::vector<const Expression*>::const_iterator it = p_Expressions->begin();
	     it != p_Expressions->end(); ++it) {
	    if (it != p_Expressions->begin())
		ret << " s:arithmeticProduct ";
	    (*it)->express(this);
	}
	end();
    }

    virtual void booleanEQ (const BooleanEQ* const, const Expression* p_left, const Expression* p_right) {
    //ret << " debug less than ";
	//start(PREC_LT);
    ret << "s:triplePattern  { ";
	p_left->express(this);
	ret << " s:booleanEQ ";
    // Assumes p_right is an integer.
    ret << '"';
	p_right->express(this);
    ret << '"' ;//<< "^^xsd:integer";
	ret << "};" << std::endl;
	//end();
    }

    virtual void booleanNE (const BooleanNE* const, const Expression* p_left, const Expression* p_right) {
    //ret << " debug less than ";
	//start(PREC_LT);
    ret << "s:triplePattern  { ";
	p_left->express(this);
	ret << " s:booleanNE ";
    ret << '"';
	p_right->express(this);
    ret << '"' ;//<< "^^xsd:integer";
	ret << "};" << std::endl;
	//end();
    }

    virtual void booleanLT (const BooleanLT* const, const Expression* p_left, const Expression* p_right) {
    //ret << " debug less than ";
	//start(PREC_LT);
    ret << "s:triplePattern  { ";
	p_left->express(this);
	ret << " s:booleanLT ";
    ret << '"';
	p_right->express(this);
    ret << '"' ;//<< "^^xsd:integer";
	ret << "};" << std::endl;
	//end();
    }

    virtual void booleanGT (const BooleanGT* const, const Expression* p_left, const Expression* p_right) {
	//ret << std::endl;
    //ret << "      a s:ComparatorExpression;" << std::endl;
    ret << "s:triplePattern  { ";
	//start(PREC_GT);
	p_left->express(this);
	ret << " s:booleanGT ";
    // Assumes p_right is an integer.
    ret << '"';
	p_right->express(this);
    ret << '"' ;//<< "^^xsd:integer";
	ret << "};" << std::endl;
	//end();
    }

    virtual void booleanLE (const BooleanLE* const, const Expression* p_left, const Expression* p_right) {
   //ret << " debug less than ";
	//start(PREC_LT);
    ret << "s:triplePattern  { ";
	p_left->express(this);
	ret << " s:booleanLE ";
    ret << '"';
	p_right->express(this);
    ret << '"' ;//<< "^^xsd:integer";
	ret << "};" << std::endl;
	//end();
    }

    virtual void booleanGE (const BooleanGE* const, const Expression* p_left, const Expression* p_right) {
   //ret << " debug less than ";
	//start(PREC_LT);
    ret << "s:triplePattern  { ";
	p_left->express(this);
	ret << " s:booleanGE ";
    ret << '"';
	p_right->express(this);
    ret << '"' ;//<< "^^xsd:integer";
	ret << "};" << std::endl;
	//end();
    }

    virtual void variable (const Variable* const, std::string lexicalValue) {
        //knownVars.insert(v);
        if (starFlag) {knownVars.insert(lexicalValue);}
        //std::cout << v << std::endl; //DEBUG
	    //preWhere << ':' << lexicalValue;
	    if (!constructFlag) ret << ':' << lexicalValue;
    }

    virtual void uri (const URI* const, std::string lexicalValue) {
    //if (DEBUG) ret << "# uri expressor" << std::endl;
	if (!constructFlag) ret << '<' << lexicalValue << '>';
    }

    virtual void posList (const POSList* const, const ProductionVector<const POS*>* p_POSs) {
        starFlag = 0;
	    for (std::vector<const POS*>::const_iterator it = p_POSs->begin();
	         it != p_POSs->end(); ++it) {
            ret << "     s:var ";
	        (*it)->express(this);
            ret << ";" << std::endl;
	    }
    }

    virtual void describe (const Describe* const, VarSet* p_VarSet, ProductionVector<const DatasetClause*>* p_DatasetClauses, WhereClause* p_WhereClause, SolutionModifier* p_SolutionModifier) {
	lead();
    starFlag = 0;
    // Output the namespace constants for our SPARQL-N3 serialization space.
    // Is there a better way to do this?
    //ret << "@prefix xsd: <http://www.w3.org/2001/XMLSchema#> ." << std::endl;
    ret << "@prefix s: <http://dig.csail.mit.edu/2009/IARPA-PIR/sparql#> ." << std::endl;
    //ret << "@prefix : <http://dig.csail.mit.edu/2009/IARPA-PIR/query1#> ." << std::endl;
	ret << std::endl;
    timeval tim;
    gettimeofday(&tim, NULL);
    srand(tim.tv_sec * tim.tv_usec);
    int random_integer = rand();
	ret << ":Query-" << random_integer << " a s:SPARQLQuery;" << std::endl;
	//ret << "DESCRIBE ";
	p_VarSet->express(this);
	p_DatasetClauses->express(this);
	p_WhereClause->express(this);
// TEST
        if (starFlag) {
            ret << std::endl << "]; " << std::endl;
            ret << "   s:retrieve [" << std::endl;
            for (std::set<std::string>::const_iterator it = knownVars.begin(); it != knownVars.end(); ++it) {
                    ret << "      s:var :" << *it << ";" << std::endl;
            }
            ret << "]." << std::endl;
        }
        //ret << t;

        else
            ret << std::endl << "]. " << std::endl;
// TEST
	p_SolutionModifier->express(this);
    }

    virtual void ask (const Ask* const, ProductionVector<const DatasetClause*>* p_DatasetClauses, WhereClause* p_WhereClause) {
	lead();
    constructFlag = 0;
    starFlag = 0;
    // Output the namespace constants for our SPARQL-N3 serialization space.
    // Is there a better way to do this?
    //ret << "@prefix xsd: <http://www.w3.org/2001/XMLSchema#> ." << std::endl;
    ret << "@prefix s: <http://dig.csail.mit.edu/2009/IARPA-PIR/sparql#> ." << std::endl;
    //ret << "@prefix : <http://dig.csail.mit.edu/2009/IARPA-PIR/query1#> ." << std::endl;
	ret << std::endl;
    timeval tim;
    gettimeofday(&tim, NULL);
    srand(tim.tv_sec * tim.tv_usec);
    int random_integer = rand();
	ret << ":Query-" << random_integer << " a s:SPARQLQuery;" << std::endl;
	//ret << "ASK ";
	p_DatasetClauses->express(this);
	p_WhereClause->express(this);
    ret << std::endl << "]. " << std::endl;
    }

    virtual void construct (const Construct* const, DefaultGraphPattern* p_ConstructTemplate, ProductionVector<const DatasetClause*>* p_DatasetClauses, WhereClause* p_WhereClause, SolutionModifier* p_SolutionModifier) {
    constructFlag = 1;
    starFlag = 0;
	lead();

    // Output the namespace constants for our SPARQL-N3 serialization space.
    // Is there a better way to do this?
    //ret << "@prefix xsd: <http://www.w3.org/2001/XMLSchema#> ." << std::endl;
    ret << "@prefix s: <http://dig.csail.mit.edu/2009/IARPA-PIR/sparql#> ." << std::endl;
    //ret << "@prefix : <http://dig.csail.mit.edu/2009/IARPA-PIR/query1#> ." << std::endl;
	ret << std::endl;
    timeval tim;
    gettimeofday(&tim, NULL);
    srand(tim.tv_sec * tim.tv_usec);
    int random_integer = rand();
	ret << ":Query-" << random_integer << " a s:SPARQLQuery;" << std::endl;

	//ret << "CONSTRUCT ";
    starFlag = 1;
    //ret << std::endl << "CONSTRUCT ConstructTemplate:" << std::endl;
	p_ConstructTemplate->express(this);
    starFlag = 0;
    //ret << std::endl << "CONSTRUCT DatasetClauses:" << std::endl;
	p_DatasetClauses->express(this);
    constructFlag = 0;
    //ret << std::endl << "CONSTRUCT WhereClause:" << std::endl;
    lead();
	p_WhereClause->express(this);
    starFlag = 1;
// TEST
        if (starFlag) {
            ret << "]; " << std::endl;
            ret << "   s:retrieve [" << std::endl;
            for (std::set<std::string>::const_iterator it = knownVars.begin(); it != knownVars.end(); ++it) {
                    ret << "      s:var :" << *it << ";" << std::endl;
            }
            ret << "]." << std::endl;
        }
        //ret << t;

        else
            ret << std::endl << "]. " << std::endl;
// TEST
    starFlag = 0;
    //ret << std::endl << "CONSTRUCT SolutionModifier:" << std::endl;
	p_SolutionModifier->express(this);
    }

    virtual void select (const Select* const, e_distinctness /*p_distinctness*/, VarSet* p_VarSet, ProductionVector<const DatasetClause*>* p_DatasetClauses, WhereClause* p_WhereClause, SolutionModifier* p_SolutionModifier) {
    constructFlag = 0;
	lead();
    // Output the namespace constants for our SPARQL-N3 serialization space.
    // Is there a better way to do this?
    //ret << "@prefix xsd: <http://www.w3.org/2001/XMLSchema#> ." << std::endl;
    ret << "@prefix s: <http://dig.csail.mit.edu/2009/IARPA-PIR/sparql#> ." << std::endl;
    //ret << "@prefix : <http://dig.csail.mit.edu/2009/IARPA-PIR/query1#> ." << std::endl;
	ret << std::endl;
    timeval tim;
    gettimeofday(&tim, NULL);
    srand(tim.tv_sec * tim.tv_usec);
    int random_integer = rand();
	ret << ":Query-" << random_integer << " a s:SPARQLQuery;" << std::endl;
    // TODO: Add a random number to the query. Perhaps a date/time stamp.
    //ret << "   s:cardinality :ALL;" << std::endl;
	//if (p_distinctness == DIST_distinct) ret << "DISTINCT ";
	//if (p_distinctness == DIST_reduced) ret << "REDUCED ";
    ret << "   s:retrieve [" << std::endl;
    // See if we can catch the * here.
    //if (p_VarSet->express(this) == '*') {
    //    ret << "s:AllVar" << std::endl;
    //}
	p_VarSet->express(this);
    ret << "   ];";
	p_DatasetClauses->express(this);
	ret << std::endl;
	lead();
	p_WhereClause->express(this);
// TEST
        if (starFlag) {
            ret << std::endl << "]; " << std::endl;
            ret << "   s:retrieve [" << std::endl;
            for (std::set<std::string>::const_iterator it = knownVars.begin(); it != knownVars.end(); ++it) {
                    ret << "      s:var :" << *it << ";" << std::endl;
            }
            ret << "]." << std::endl;
        }
        //ret << t;

        else
            ret << std::endl << "]. " << std::endl;
// TEST
	p_SolutionModifier->express(this);
    }

};

int main (int argc, char** argv) {
    /* Manage invocation arguments. */
    if (argc != 2) {
	throw "Translate a SPARQL query to N3. Usage: sparql2n3 <sparqlFile>";
    }
    const char* sparqlFile = argv[1];

    /* Set up parser. */
    w3c_sw::POSFactory f;
    w3c_sw::SPARQLfedDriver sparqlParser("", &f);

    if (sparqlParser.parse_file(sparqlFile)) {
	std::string msg = std::string("failed to parse sparql \"") + 
	    sparqlFile + std::string("\".");
	throw msg;
    }
    Sparql2n3 s;
    sparqlParser.root->express(&s);
    std::cout << s.getSPARQLstring();
    delete sparqlParser.root;
    return 0;
}

