/*
 * Copyright (c) 2010 Remko Tronçon
 * Licensed under the GNU General Public License v3.
 * See Documentation/Licenses/GPLv3.txt for more information.
 */

#include "Swiften/Parser/XMPPParser.h"

#include <iostream>
#include <cassert>

#include "Swiften/Elements/ProtocolHeader.h"
#include "Swiften/Base/String.h"
#include "Swiften/Parser/XMLParser.h"
#include "Swiften/Parser/PlatformXMLParserFactory.h"
#include "Swiften/Parser/XMPPParserClient.h"
#include "Swiften/Parser/XMPPParser.h"
#include "Swiften/Parser/ElementParser.h"
#include "Swiften/Parser/PresenceParser.h"
#include "Swiften/Parser/IQParser.h"
#include "Swiften/Parser/MessageParser.h"
#include "Swiften/Parser/StreamFeaturesParser.h"
#include "Swiften/Parser/AuthRequestParser.h"
#include "Swiften/Parser/AuthSuccessParser.h"
#include "Swiften/Parser/AuthFailureParser.h"
#include "Swiften/Parser/AuthChallengeParser.h"
#include "Swiften/Parser/AuthResponseParser.h"
#include "Swiften/Parser/EnableStreamManagementParser.h"
#include "Swiften/Parser/StreamManagementEnabledParser.h"
#include "Swiften/Parser/StreamManagementFailedParser.h"
#include "Swiften/Parser/StanzaAckParser.h"
#include "Swiften/Parser/StanzaAckRequestParser.h"
#include "Swiften/Parser/StartTLSParser.h"
#include "Swiften/Parser/StartTLSFailureParser.h"
#include "Swiften/Parser/CompressParser.h"
#include "Swiften/Parser/CompressFailureParser.h"
#include "Swiften/Parser/CompressedParser.h"
#include "Swiften/Parser/UnknownElementParser.h"
#include "Swiften/Parser/TLSProceedParser.h"
#include "Swiften/Parser/ComponentHandshakeParser.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) : 
				xmlParser_(0),
				client_(client), 
				payloadParserFactories_(payloadParserFactories), 
				level_(0),
				currentElementParser_(0),
				parseErrorOccurred_(false) {
	xmlParser_ = PlatformXMLParserFactory().createXMLParser(this);
}

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

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

void XMPPParser::handleStartElement(const String& element, const String& ns, const AttributeMap& attributes) {
	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 String& element, const String& ns) {
	assert(level_ > TopLevel);
	--level_;
	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_ = NULL;
		}
	}
}

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

ElementParser* XMPPParser::createElementParser(const String& element, const 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 == "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 == "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();
}

}