/*
 * This file is part of the Soprano Project.
 *
 * This file is based on source generated by kxm_compiler
 *
 * Copyright (C) 2008 Sebastian Trueg <trueg@kde.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "sparqlxmlresultparser.h"

#include <qfile.h>
#include <QtXml/QDomElement>
#include <QtXml/QDomNode>
#include <qtextstream.h>
#include <QTextStream>
#include <QtCore/QBuffer>
#include <QtCore/QDebug>

QString indent( int n = 0 )
{
    static int i = 0;
    i += n;
    QString space;
    return space.fill( ' ', i );
}

namespace Soprano {
    namespace Client {
        namespace SparqlParser {
            Unbound Unbound::parseElement( const QDomElement &element, bool *ok )
            {
                if ( element.tagName() != "unbound" ) {
                    //kError()() << "Expected 'unbound', got '" <<element.tagName() << "'.";
                    if ( ok ) *ok = false;
                    return Unbound();
                }

                Unbound result = Unbound();

                QDomNode n;
                for( n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
                    QDomElement e = n.toElement();
                }


                if ( ok ) *ok = true;
                return result;
            }

            QString Unbound::writeElement() const
            {
                QString xml;
                xml += indent() + "<unbound/>\n";
                return xml;
            }


            void Literal::setDatatype( const QString &v )
            {
                mDatatype = v;
            }

            QString Literal::datatype() const
            {
                return mDatatype;
            }

            void Literal::setXsi_type( const QString &v )
            {
                mXsi_type = v;
            }

            QString Literal::xsi_type() const
            {
                return mXsi_type;
            }

            void Literal::setXml_lang( const QString &v )
            {
                mXml_lang = v;
            }

            QString Literal::xml_lang() const
            {
                return mXml_lang;
            }

            Literal Literal::parseElement( const QDomElement &element, bool *ok )
            {
                if ( element.tagName() != "literal" ) {
                    //kError()() << "Expected 'literal', got '" <<element.tagName() << "'.";
                    if ( ok ) *ok = false;
                    return Literal();
                }

                Literal result = Literal();

                QDomNode n;
                for( n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
                    QDomElement e = n.toElement();
                }

                result.setDatatype( element.attribute( "datatype" ) );
                result.setXsi_type( element.attribute( "xsi:type" ) );
                result.setXml_lang( element.attribute( "xml:lang" ) );
                result.setData( element.text());

                if ( ok ) *ok = true;
                return result;
            }



            QString Literal::writeElement() const
            {
                QString xml;
                xml += indent() + "<literal datatype=\"" + datatype() + "\" xsi:type=\"" + xsi_type() + "\" xml:lang=\"" + xml_lang() + "\">\n";
                indent( 2 );
                indent( -2 );
                xml += indent() + "</literal>\n";
                return xml;
            }


            Bnode Bnode::parseElement( const QDomElement &element, bool *ok )
            {
                if ( element.tagName() != "bnode" ) {
                    //kError()() << "Expected 'bnode', got '" <<element.tagName() << "'.";
                    if ( ok ) *ok = false;
                    return Bnode();
                }

                Bnode result = Bnode();
                result.setId(element.text());



                if ( ok ) *ok = true;
                return result;
            }

            QString Bnode::writeElement() const
            {
                QString xml;
                xml += indent() + "<bnode>\n";
                indent( 2 );
                indent( -2 );
                xml += indent() + "</bnode>\n";
                return xml;
            }


            Uri Uri::parseElement( const QDomElement &element, bool *ok )
            {
                if ( element.tagName() != "uri" ) {
                    //kError()() << "Expected 'uri', got '" <<element.tagName() << "'.";
                    if ( ok ) *ok = false;
                    return Uri();
                }

                Uri result = Uri();
                result.setUri(QUrl::fromEncoded(element.text().toUtf8()));



                if ( ok ) *ok = true;
                return result;
            }

            QString Uri::writeElement() const
            {
                QString xml;
                xml += indent() + "<uri>\n";
                indent( 2 );
                indent( -2 );
                xml += indent() + "</uri>\n";
                return xml;
            }


            Binding::BindingType Binding::type() const {
                return mType;
            }

            void Binding::setName( const QString &v )
            {
                mName = v;
            }

            QString Binding::name() const
            {
                return mName;
            }

            void Binding::setUri( const Uri &v )
            {
                mUri = v;
            }

            Uri Binding::uri() const
            {
                return mUri;
            }

            void Binding::setBnode( const Bnode &v )
            {
                mBnode = v;
            }

            Bnode Binding::bnode() const
            {
                return mBnode;
            }

            void Binding::setLiteral( const Literal &v )
            {
                mLiteral = v;
            }

            Literal Binding::literal() const
            {
                return mLiteral;
            }

            void Binding::setUnbound( const Unbound &v )
            {
                mUnbound = v;
            }

            Unbound Binding::unbound() const
            {
                return mUnbound;
            }

            Binding Binding::parseElement( const QDomElement &element, bool *ok )
            {
                if ( element.tagName() != "binding" ) {
                    //kError()() << "Expected 'binding', got '" <<element.tagName() << "'.";
                    if ( ok ) *ok = false;
                    return Binding();
                }

                Binding result = Binding();
                result.mType = Binding::NONE;
                QDomNode n;
                for( n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
                    QDomElement e = n.toElement();
                    if ( e.tagName() == "uri" ) {
                        bool ok;
                        Uri o = Uri::parseElement( e, &ok );
                        if ( ok ) result.setUri( o );
                        result.mType = Binding::URI;
                    }
                    else if ( e.tagName() == "bnode" ) {
                        bool ok;
                        Bnode o = Bnode::parseElement( e, &ok );
                        if ( ok ) result.setBnode( o );
                        result.mType = Binding::BNODE;
                    }
                    else if ( e.tagName() == "literal" ) {
                        bool ok;
                        Literal o = Literal::parseElement( e, &ok );
                        if ( ok ) result.setLiteral( o );
                        result.mType = Binding::LITERAL;
                    }
                    else if ( e.tagName() == "unbound" ) {
                        bool ok;
                        Unbound o = Unbound::parseElement( e, &ok );
                        if ( ok ) result.setUnbound( o );
                        result.mType = Binding::UNBOUND;
                    }
                }

                result.setName( element.attribute( "name" ) );

                if ( ok ) *ok = true;
                return result;
            }

            QString Binding::writeElement() const
            {
                QString xml;
                xml += indent() + "<binding name=\"" + name() + "\">\n";
                indent( 2 );
                if( mType == Binding::URI )
                    xml += indent() + "<uri>" + mUri.writeElement() + "</uri>\n";
                else if( mType == Binding::BNODE )
                    xml += indent() + "<bnode>" + mBnode.writeElement() + "</bnode>\n";
                else if( mType == Binding::LITERAL)
                    xml += indent() + "<literal>" + mLiteral.writeElement() + "</literal>\n";
                else if(mType == Binding::UNBOUND)
                    xml += Unbound().writeElement();
                indent( -2 );
                xml += indent() + "</binding>\n";
                return xml;
            }


            void Result::setXml_lang( const QString &v )
            {
                mXml_lang = v;
            }

            QString Result::xml_lang() const
            {
                return mXml_lang;
            }

            void Result::setIndex( const QString &v )
            {
                mIndex = v;
            }

            QString Result::index() const
            {
                return mIndex;
            }

            void Result::addBinding( const Binding &v )
            {
                mBindingList.append( v );
            }

            void Result::setBindingList( const Binding::List &v )
            {
                mBindingList = v;
            }

            Binding::List Result::bindingList() const
            {
                return mBindingList;
            }

            Result Result::parseElement( const QDomElement &element, bool *ok )
            {
                if ( element.tagName() != "result" ) {
                    //kError()() << "Expected 'result', got '" <<element.tagName() << "'.";
                    if ( ok ) *ok = false;
                    return Result();
                }

                Result result = Result();

                QDomNode n;
                for( n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
                    QDomElement e = n.toElement();
                    if ( e.tagName() == "binding" ) {
                        bool ok;
                        Binding o = Binding::parseElement( e, &ok );
                        if ( ok ) result.addBinding( o );
                    }
                }

                result.setXml_lang( element.attribute( "xml:lang" ) );
                result.setIndex( element.attribute( "index" ) );

                if ( ok ) *ok = true;
                return result;
            }

            QString Result::writeElement() const
            {
                QString xml;
                xml += indent() + "<result xml:lang=\"" + xml_lang() + "\" index=\"" + index() + "\">\n";
                indent( 2 );
                foreach( const Binding &e, bindingList() ) {
                    xml += e.writeElement();
                }
                indent( -2 );
                xml += indent() + "</result>\n";
                return xml;
            }


            Boolean Boolean::parseElement( const QDomElement &element, bool *ok )
            {
                if ( element.tagName() != "boolean" ) {
                    //kError()() << "Expected 'boolean', got '" <<element.tagName() << "'.";
                    if ( ok ) *ok = false;
                    return Boolean();
                }

                Boolean result = Boolean();
                result.mValue = false;
                if(element.text().simplified() == QString("true")) {
                    result.mValue = true;
                }

                QDomNode n;
                for( n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
                    QDomElement e = n.toElement();
                }


                if ( ok ) *ok = true;
                result.mIsValid = true;
                return result;
            }

            QString Boolean::writeElement() const
            {
                QString xml;
                xml += indent() + "<boolean/>\n";
                return xml;
            }


            void Results::addResult( const Result &v )
            {
                mResultList.append( v );
            }

            void Results::setResultList( const Result::List &v )
            {
                mResultList = v;
            }

            Result::List Results::resultList() const
            {
                return mResultList;
            }

            Results Results::parseElement( const QDomElement &element, bool *ok )
            {
                if ( element.tagName() != "results" ) {
                    //kError()() << "Expected 'results', got '" <<element.tagName() << "'.";
                    if ( ok ) *ok = false;
                    return Results();
                }

                Results result = Results();

                QDomNode n;
                for( n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
                    QDomElement e = n.toElement();
                    if ( e.tagName() == "result" ) {
                        bool ok;
                        Result o = Result::parseElement( e, &ok );
                        if ( ok ) result.addResult( o );
                    }
                }


                if ( ok ) *ok = true;
                return result;
            }

            QString Results::writeElement() const
            {
                QString xml;
                xml += indent() + "<results>\n";
                indent( 2 );
                xml += Boolean().writeElement();
                foreach( const Result &e, resultList() ) {
                    xml += e.writeElement();
                }
                indent( -2 );
                xml += indent() + "</results>\n";
                return xml;
            }


            void Variable::setName( const QString &v )
            {
                mName = v;
            }

            QString Variable::name() const
            {
                return mName;
            }

            Variable Variable::parseElement( const QDomElement &element, bool *ok )
            {
                if ( element.tagName() != "variable" ) {
                    //kError()() << "Expected 'variable', got '" <<element.tagName() << "'.";
                    if ( ok ) *ok = false;
                    return Variable();
                }

                Variable result = Variable();

                QDomNode n;
                for( n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
                    QDomElement e = n.toElement();
                }

                result.setName( element.attribute( "name" ) );

                if ( ok ) *ok = true;
                return result;
            }

            QString Variable::writeElement() const
            {
                QString xml;
                xml += indent() + "<variable name=\"" + name() + "\"/>\n";
                return xml;
            }


            void Head::addVariable( const Variable &v )
            {
                mVariableList.append( v );
            }

            void Head::setVariableList( const Variable::List &v )
            {
                mVariableList = v;
            }

            Variable::List Head::variableList() const
            {
                return mVariableList;
            }

            Head Head::parseElement( const QDomElement &element, bool *ok )
            {
                if ( element.tagName() != "head" ) {
                    //kError()() << "Expected 'head', got '" <<element.tagName() << "'.";
                    if ( ok ) *ok = false;
                    return Head();
                }

                Head result = Head();

                QDomNode n;
                for( n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
                    QDomElement e = n.toElement();
                    if ( e.tagName() == "variable" ) {
                        bool ok;
                        Variable o = Variable::parseElement( e, &ok );
                        if ( ok ) result.addVariable( o );
                    }
                }


                if ( ok ) *ok = true;
                return result;
            }

            QString Head::writeElement() const
            {
                QString xml;
                xml += indent() + "<head>\n";
                indent( 2 );
                foreach( const Variable &e, variableList() ) {
                    xml += e.writeElement();
                }
                indent( -2 );
                xml += indent() + "</head>\n";
                return xml;
            }


            void Sparql::setXml_lang( const QString &v )
            {
                mXml_lang = v;
            }

            QString Sparql::xml_lang() const
            {
                return mXml_lang;
            }

            void Sparql::setXsi_schemaLocation( const QString &v )
            {
                mXsi_schemaLocation = v;
            }

            QString Sparql::xsi_schemaLocation() const
            {
                return mXsi_schemaLocation;
            }

            void Sparql::setHead( const Head &v )
            {
                mHead = v;
            }

            Head Sparql::head() const
            {
                return mHead;
            }

            void Sparql::setResults( const Results &v )
            {
                mResults = v;
            }

            Results Sparql::results() const
            {
                return mResults;
            }

            void Sparql::setBoolean( const Boolean &v )
            {
                mBoolean = v;
            }

            Boolean Sparql::boolean() const
            {
                return mBoolean;
            }


            Sparql Sparql::parseElement( const QDomElement &element, bool *ok )
            {
                if ( element.tagName() != "sparql" ) {
                    //kError()() << "Expected 'sparql', got '" <<element.tagName() << "'.";
                    if ( ok ) *ok = false;
                    return Sparql();
                }

                Sparql result = Sparql();

                QDomNode n;
                for( n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
                    QDomElement e = n.toElement();
                    if ( e.tagName() == "head" ) {
                        bool ok;
                        Head o = Head::parseElement( e, &ok );
                        if ( ok ) result.setHead( o );
                    }
                    else if ( e.tagName() == "boolean" ) {
                        bool ok;
                        Boolean o = Boolean::parseElement( e, &ok );
                        if ( ok ) result.setBoolean( o );
                    }
                    else if ( e.tagName() == "results" ) {
                        bool ok;
                        Results o = Results::parseElement( e, &ok );
                        if ( ok ) result.setResults( o );
                    }
                }

                result.setXml_lang( element.attribute( "xml:lang" ) );
                result.setXsi_schemaLocation( element.attribute( "xsi:schemaLocation" ) );

                if ( ok ) *ok = true;
                return result;
            }

            QString Sparql::writeElement() const
            {
                QString xml;
                xml += indent() + "<sparql xml:lang=\"" + xml_lang() + "\" xsi:schemaLocation=\"" + xsi_schemaLocation() + "\">\n";
                indent( 2 );
                xml += Head().writeElement();
                xml += Results().writeElement();
                indent( -2 );
                xml += indent() + "</sparql>\n";
                return xml;
            }

            Sparql Sparql::parseFile( const QString &filename, bool *ok )
            {
                QFile file( filename );
                if ( !file.open( QIODevice::ReadOnly ) ) {
                    //kError()() << "Unable to open file '" << filename << "'";
                    if ( ok ) *ok = false;
                    return Sparql();
                }

                QString errorMsg;
                int errorLine, errorCol;
                QDomDocument doc;
                if ( !doc.setContent( &file, false, &errorMsg, &errorLine, &errorCol ) ) {
                    //kError()() << errorMsg << " at " << errorLine << "," << errorCol;
                    if ( ok ) *ok = false;
                    return Sparql();
                }

                //kDebug() << "CONTENT:" << doc.toString();

                bool documentOk;
                Sparql c = parseElement( doc.documentElement(), &documentOk );
                if ( ok ) {
                    *ok = documentOk;
                }
                return c;
            }

            Sparql Sparql::parseByteArray( const QByteArray& bytes, bool *ok )
            {
                QString errorMsg;
                int errorLine, errorCol;
                QDomDocument doc;
                if ( !doc.setContent( bytes, false, &errorMsg, &errorLine, &errorCol ) ) {
                    //kError()() << errorMsg << " at " << errorLine << "," << errorCol;
                    if ( ok ) *ok = false;
                    return Sparql();
                }

                //kDebug() << "CONTENT:" << doc.toString();

                bool documentOk;
                Sparql c = parseElement( doc.documentElement(), &documentOk );
                if ( ok ) {
                    *ok = documentOk;
                }
                return c;
            }

            bool Sparql::writeFile( const QString &filename )
            {
                QFile file( filename );
                if ( !file.open( QIODevice::WriteOnly ) ) {
                    //kError()() << "Unable to open file '" << filename << "'";
                    return false;
                }

                QTextStream ts( &file );
                ts << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
                ts << writeElement();
                file.close();

                return true;
            }

        }
    }
}
