/*
 * Copyright (c) 2010-2016 Isode Limited.
 * All rights reserved.
 * See the COPYING file for more information.
 */

#include <Swiften/Parser/XMPPParser.h>

#include <cassert>
#include <string>

#include <Swiften/Elements/ProtocolHeader.h>
#include <Swiften/Parser/AuthChallengeParser.h>
#include <Swiften/Parser/AuthFailureParser.h>
#include <Swiften/Parser/AuthRequestParser.h>
#include <Swiften/Parser/AuthResponseParser.h>
#include <Swiften/Parser/AuthSuccessParser.h>
#include <Swiften/Parser/ComponentHandshakeParser.h>
#include <Swiften/Parser/CompressFailureParser.h>
#include <Swiften/Parser/CompressParser.h>
#include <Swiften/Parser/CompressedParser.h>
#include <Swiften/Parser/ElementParser.h>
#include <Swiften/Parser/EnableStreamManagementParser.h>
#include <Swiften/Parser/IQParser.h>
#include <Swiften/Parser/MessageParser.h>
#include <Swiften/Parser/PresenceParser.h>
#include <Swiften/Parser/StanzaAckParser.h>
#include <Swiften/Parser/StanzaAckRequestParser.h>
#include <Swiften/Parser/StartTLSFailureParser.h>
#include <Swiften/Parser/StartTLSParser.h>
#include <Swiften/Parser/StreamErrorParser.h>
#include <Swiften/Parser/StreamFeaturesParser.h>
#include <Swiften/Parser/StreamManagementEnabledParser.h>
#include <Swiften/Parser/StreamManagementFailedParser.h>
#include <Swiften/Parser/StreamResumeParser.h>
#include <Swiften/Parser/StreamResumedParser.h>
#include <Swiften/Parser/TLSProceedParser.h>
#include <Swiften/Parser/UnknownElementParser.h>
#include <Swiften/Parser/XMLParser.h>
#include <Swiften/Parser/XMLParserFactory.h>
#include <Swiften/Parser/XMPPParserClient.h>

// TODO: Whenever an error occurs in the handlers, stop the parser by returing
// a bool value, and stopping the XML parser

namespace Swift {

XMPPParser::XMPPParser(
        XMPPParserClient* client,
        PayloadParserFactoryCollection* payloadParserFactories,
        XMLParserFactory* xmlParserFactory) :
                xmlParser_(nullptr),
                client_(client),
                payloadParserFactories_(payloadParserFactories),
                level_(0),
                currentElementParser_(nullptr),
                parseErrorOccurred_(false) {
    xmlParser_ = xmlParserFactory->createXMLParser(this);
}

XMPPParser::~XMPPParser() {
    delete currentElementParser_;
}

bool XMPPParser::parse(const std::string& data) {
    bool xmlParseResult = xmlParser_->parse(data);
    return xmlParseResult && !parseErrorOccurred_;
}

void XMPPParser::handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) {
    if (!parseErrorOccurred_) {
        if (level_ == TopLevel) {
            if (element == "stream" && ns == "http://etherx.jabber.org/streams") {
                ProtocolHeader header;
                header.setFrom(attributes.getAttribute("from"));
                header.setTo(attributes.getAttribute("to"));
                header.setID(attributes.getAttribute("id"));
                header.setVersion(attributes.getAttribute("version"));
                client_->handleStreamStart(header);
            }
            else {
                parseErrorOccurred_ = true;
            }
        }
        else {
            if (level_ == StreamLevel) {
                assert(!currentElementParser_);
                currentElementParser_ = createElementParser(element, ns);
            }
            currentElementParser_->handleStartElement(element, ns, attributes);
        }
    }
    ++level_;
}

void XMPPParser::handleEndElement(const std::string& element, const std::string& ns) {
    assert(level_ > TopLevel);
    --level_;
    if (!parseErrorOccurred_) {
        if (level_ == TopLevel) {
            assert(element == "stream");
            client_->handleStreamEnd();
        }
        else {
            assert(currentElementParser_);
            currentElementParser_->handleEndElement(element, ns);
            if (level_ == StreamLevel) {
                client_->handleElement(currentElementParser_->getElement());
                delete currentElementParser_;
                currentElementParser_ = nullptr;
            }
        }
    }
}

void XMPPParser::handleCharacterData(const std::string& data) {
    if (!parseErrorOccurred_) {
        if (currentElementParser_) {
            currentElementParser_->handleCharacterData(data);
        }
    //else {
    //    std::cerr << "XMPPParser: Ignoring stray character data: " << data << std::endl;
    //}
    }
}

ElementParser* XMPPParser::createElementParser(const std::string& element, const std::string& ns) {
    if (element == "presence") {
        return new PresenceParser(payloadParserFactories_);
    }
    else if (element == "iq") {
        return new IQParser(payloadParserFactories_);
    }
    else if (element == "message") {
        return new MessageParser(payloadParserFactories_);
    }
    else if (element == "features"  && ns == "http://etherx.jabber.org/streams") {
        return new StreamFeaturesParser();
    }
    else if (element == "error" && ns == "http://etherx.jabber.org/streams") {
        return new StreamErrorParser();
    }
    else if (element == "auth") {
        return new AuthRequestParser();
    }
    else if (element == "success") {
        return new AuthSuccessParser();
    }
    else if (element == "failure" && ns == "urn:ietf:params:xml:ns:xmpp-sasl") {
        return new AuthFailureParser();
    }
    else if (element == "challenge" && ns == "urn:ietf:params:xml:ns:xmpp-sasl") {
        return new AuthChallengeParser();
    }
    else if (element == "response" && ns == "urn:ietf:params:xml:ns:xmpp-sasl") {
        return new AuthResponseParser();
    }
    else if (element == "starttls") {
        return new StartTLSParser();
    }
    else if (element == "failure" && ns == "urn:ietf:params:xml:ns:xmpp-tls") {
        return new StartTLSFailureParser();
    }
    else if (element == "compress") {
        return new CompressParser();
    }
    else if (element == "compressed") {
        return new CompressedParser();
    }
    else if (element == "failure" && ns == "http://jabber.org/protocol/compress") {
        return new CompressFailureParser();
    }
    else if (element == "proceed") {
        return new TLSProceedParser();
    }
    else if (element == "enable" && ns == "urn:xmpp:sm:2") {
        return new EnableStreamManagementParser();
    }
    else if (element == "enabled" && ns == "urn:xmpp:sm:2") {
        return new StreamManagementEnabledParser();
    }
    else if (element == "failed" && ns == "urn:xmpp:sm:2") {
        return new StreamManagementFailedParser();
    }
    else if (element == "resume" && ns == "urn:xmpp:sm:2") {
        return new StreamResumeParser();
    }
    else if (element == "resumed" && ns == "urn:xmpp:sm:2") {
        return new StreamResumedParser();
    }
    else if (element == "a" && ns == "urn:xmpp:sm:2") {
        return new StanzaAckParser();
    }
    else if (element == "r" && ns == "urn:xmpp:sm:2") {
        return new StanzaAckRequestParser();
    }
    else if (element == "handshake") {
        return new ComponentHandshakeParser();
    }
    return new UnknownElementParser();
}

}
