/*
 * This file is part of the XSL implementation.
 *
 * Copyright (C) 2009 Jakub Wieczorek <faw217@gmail.com>
 *
 * 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 "config.h"

#if ENABLE(XSLT)

#include "XSLTProcessor.h"

#include "Console.h"
#include "DOMWindow.h"
#include "Frame.h"
#include "SecurityOrigin.h"
#include "TransformSource.h"
#include "markup.h"
#include <wtf/Assertions.h>
#include <wtf/Vector.h>

#include <qabstractmessagehandler.h>
#include <qabstracturiresolver.h>
#include <qbuffer.h>
#include <qsourcelocation.h>
#include <qxmlquery.h>

namespace WebCore {

class XSLTMessageHandler : public QAbstractMessageHandler {

public:
    XSLTMessageHandler(Document* document = 0);
    virtual void handleMessage(QtMsgType type, const QString& description,
                               const QUrl& identifier, const QSourceLocation& sourceLocation);

private:
    Document* m_document;
};

XSLTMessageHandler::XSLTMessageHandler(Document* document)
    : QAbstractMessageHandler()
    , m_document(document)
{
}

void XSLTMessageHandler::handleMessage(QtMsgType type, const QString& description,
                                       const QUrl&, const QSourceLocation& sourceLocation)
{
    if (!m_document->frame())
        return;

    MessageLevel level;
    switch (type) {
    case QtDebugMsg:
        level = TipMessageLevel;
        break;
    case QtWarningMsg:
        level = WarningMessageLevel;
        break;
    case QtCriticalMsg:
    case QtFatalMsg:
        level = ErrorMessageLevel;
        break;
    default:
        level = LogMessageLevel;
        break;
    }

    Console* console = m_document->frame()->domWindow()->console();
    console->addMessage(XMLMessageSource, LogMessageType, level, description,
                        sourceLocation.line(), sourceLocation.uri().toString());
}

class XSLTUriResolver : public QAbstractUriResolver {

public:
    XSLTUriResolver(Document* document);
    virtual QUrl resolve(const QUrl& relative, const QUrl& baseURI) const;

private:
    Document* m_document;
};

XSLTUriResolver::XSLTUriResolver(Document* document)
    : QAbstractUriResolver()
    , m_document(document)
{
}

QUrl XSLTUriResolver::resolve(const QUrl& relative, const QUrl& baseURI) const
{
    QUrl url = baseURI.resolved(relative);

    if (!m_document->frame() || !m_document->securityOrigin()->canRequest(url))
        return QUrl();
    return url;
}

bool XSLTProcessor::transformToString(Node* sourceNode, String&, String& resultString, String&)
{
    bool success = false;

    RefPtr<XSLStyleSheet> stylesheet = m_stylesheet;
    if (!stylesheet && m_stylesheetRootNode) {
        Node* node = m_stylesheetRootNode.get();
        stylesheet = XSLStyleSheet::createForXSLTProcessor(node->parentNode() ? node->parentNode() : node,
            node->document()->url().string(),
            node->document()->url()); // FIXME: Should we use baseURL here?

        // According to Mozilla documentation, the node must be a Document node, an xsl:stylesheet or xsl:transform element.
        // But we just use text content regardless of node type.
        stylesheet->parseString(createMarkup(node));
    }

    if (!stylesheet || stylesheet->sheetString().isEmpty())
        return success;

    RefPtr<Document> ownerDocument = sourceNode->document();
    bool sourceIsDocument = (sourceNode == ownerDocument.get());

    QXmlQuery query(QXmlQuery::XSLT20);

    XSLTMessageHandler messageHandler(ownerDocument.get());
    XSLTUriResolver uriResolver(ownerDocument.get());
    query.setMessageHandler(&messageHandler);

    XSLTProcessor::ParameterMap::iterator end = m_parameters.end();
    for (XSLTProcessor::ParameterMap::iterator it = m_parameters.begin(); it != end; ++it)
        query.bindVariable(QString(it->first), QXmlItem(QVariant(QString(it->second))));

    QString source;
    if (sourceIsDocument && ownerDocument->transformSource())
        source = ownerDocument->transformSource()->platformSource();
    if (!sourceIsDocument || source.isEmpty())
        source = createMarkup(sourceNode);

    QBuffer inputBuffer;
    QBuffer styleSheetBuffer;
    QBuffer outputBuffer;

    inputBuffer.setData(source.toUtf8());
    styleSheetBuffer.setData(QString(stylesheet->sheetString()).toUtf8());

    inputBuffer.open(QIODevice::ReadOnly);
    styleSheetBuffer.open(QIODevice::ReadOnly);
    outputBuffer.open(QIODevice::ReadWrite);

    query.setFocus(&inputBuffer);
    query.setQuery(&styleSheetBuffer, QUrl(stylesheet->href()));

    query.setUriResolver(&uriResolver);

    success = query.evaluateTo(&outputBuffer);
    outputBuffer.reset();
    resultString = QString::fromUtf8(outputBuffer.readAll()).trimmed();

    if (m_stylesheet) {
        m_stylesheet->clearDocuments();
        m_stylesheet = 0;
    }

    return success;
}

} // namespace WebCore

#endif // ENABLE(XSLT)
