//  Copyright (2010-2013) Cédric Coussinet (cedric.coussinet@nomoseed.net)
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published
//  by the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program 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 General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program. If not, see <http://www.gnu.org/licenses/>

#include <libxml/parser.h>
#include <libxslt/xsltutils.h>

#include <libxslt/xsltInternals.h>
#include <libxslt/transform.h>
#include <libxslt/extensions.h>
#include <libxslt/security.h>

#include <libexslt/exslt.h>

#include "xsltprocessor.h"

const int cmp_max = 100;

XSLTProcessor::XSLTProcessor() {
    xmlSubstituteEntitiesDefault (1);
    xmlLoadExtDtdDefaultValue = 1;
    cmp = 0;
    params[0] = NULL;
    xsl = NULL;
}

XSLTProcessor::~XSLTProcessor (){
    if (xsl != NULL) {
        resetParameters();
        xsltFreeStylesheet (xsl);
    }
}

bool XSLTProcessor::importStylesheet (const QByteArray xslPath) {
    if (xsl!=NULL){
        resetParameters();
        xsltFreeStylesheet (xsl);
    }
    xsl = xsltParseStylesheetFile ((const xmlChar *) xslPath.data ());
    return xsl != NULL;
}

int XSLTProcessor::setNewParameter (const QString name, const QString value) {
    if (cmp <= cmp_max) {
        strings [cmp] = name.toUtf8();
        params[cmp] = strings[cmp].data();
        cmp++;
        strings [cmp] = ("\"" + value + "\"").toUtf8();
        params[cmp] = strings[cmp].data();
        cmp++;
        params[cmp] = NULL;
        return cmp;
    }
    return -1;
}

int XSLTProcessor::setNewParameter (const QString name, const int value){
    if (cmp <= cmp_max){
        strings [cmp] = name.toUtf8();
        params[cmp] = strings[cmp].data();
        cmp++;
        strings [cmp] = QString::number(value).toUtf8();
        params[cmp] = strings[cmp].data();
        cmp++;
        params[cmp] = NULL;
        return cmp;
    }
    return -1;
}

void XSLTProcessor::setParameter(const int i, const int value) {
    const int index = i*2+1;
    strings [index] = QString::number(value).toUtf8();
    params[index] = strings[index].data();
}

void XSLTProcessor::setParameter(const int i, const QString value) {
    const int index = i*2+1;
    strings[index] = ("\"" + value + "\"").toUtf8();
    params[index] = strings[index].data();
}

void XSLTProcessor::resetParameters (void){
    cmp = 0;
    params[0] = NULL;
}

QString XSLTProcessor::transformToFragment (const QString xml) {
    xmlDocPtr doc;
    doc = xmlParseMemory (xml.toUtf8().data(), xml.toUtf8().size());
    if (doc){
        xmlDocPtr xmlIn;
        xmlIn = xsltApplyStylesheet(xsl, doc, params);
        xmlChar* xmlOut;
        int len;
        xsltSaveResultToString (&xmlOut, &len, xmlIn, xsl);
        xsltCleanupGlobals ();
        xmlCleanupParser ();
        xmlFreeDoc (xmlIn);
        xmlFreeDoc (doc);
        resetParameters();
        QString result((const char*) xmlOut);
        xmlFree(xmlOut);
        return result;
    }
    return "";
}

xmlChar* XSLTProcessor::transformToFragment(const xmlDocPtr xml) {
    xmlDocPtr xmlOut;
    xmlOut = xsltApplyStylesheet(xsl, xml, params);
    xmlChar* charOut;
    int len;
    xsltSaveResultToString (&charOut, &len, xmlOut, xsl);
    xsltCleanupGlobals ();
    xmlCleanupParser ();
    xmlFreeDoc (xmlOut);
    return charOut;
}

xmlDocPtr XSLTProcessor::transform(const xmlDocPtr xml) {
    xmlDocPtr xmlOut;
    xmlOut = xsltApplyStylesheet(xsl, xml, params);
    xsltCleanupGlobals ();
    xmlCleanupParser ();
    return xmlOut;
}


void XSLTProcessor::transformSaveFile(const xmlDocPtr xml, const char* filename) {
    xmlDocPtr result;
    result = xsltApplyStylesheet(xsl, xml, params);
    xsltSaveResultToFilename(filename, result, xsl, 0);
    xmlFreeDoc(result);
    xsltCleanupGlobals ();
    xmlCleanupParser ();
}

xmlDocPtr XSLTProcessor::transformExtensionStr(const xmlDocPtr xml) {
    xmlDocPtr xmlOut;
    exsltStrRegister ();
    xmlXPathContextPtr ctxt = xmlXPathNewContext(xml);
    exsltStrXpathCtxtRegister(ctxt, (const xmlChar *) "str");
    xmlOut = xsltApplyStylesheet(xsl, xml, params);
    xmlXPathFreeContext(ctxt);
    xsltCleanupGlobals ();
    xmlCleanupParser ();
    return xmlOut;
}
xmlDocPtr XSLTProcessor::transformToFragment (const char * xml) {
    xmlDocPtr doc = xmlParseMemory (xml, strlen(xml));
    if (doc){
        xmlDocPtr xmlOut = xsltApplyStylesheet(xsl, doc, params);
        xsltCleanupGlobals ();
        xmlCleanupParser ();
        xmlFreeDoc (doc);
        return xmlOut;
    }
    return NULL;
}

bool XSLTProcessor::isEgal(const QString xslPath) {
    return this->path == xslPath;
}

QByteArray XSLTProcessor::transform (const QByteArray xslPath, const QString xml, const char ** params)
{
    xmlSubstituteEntitiesDefault (1);
    xmlLoadExtDtdDefaultValue = 1;

    xsltStylesheetPtr xsl;
    xsl = xsltParseStylesheetFile ((const xmlChar *) xslPath.data ());

    xmlDocPtr doc;
    doc = xmlParseMemory (xml.toUtf8 ().data(), xml.toUtf8 ().size ());

    xmlDocPtr xmlIn;
    xmlIn = xsltApplyStylesheet(xsl, doc, params);

    xmlChar* xmlOut;
    int len;
    xmlDocDumpMemoryEnc (xmlIn, &xmlOut, &len, "UTF-8");

    xsltFreeStylesheet (xsl);
    xmlFreeDoc (xmlIn);
    xmlFreeDoc (doc);
    xsltCleanupGlobals ();
    xmlCleanupParser ();

    return QByteArray ((const char*) xmlOut);
}

void XSLTProcessor::transform (const char * xslPath, const xmlDocPtr xmlIn, const char* outPath, const char ** params)
{
    xmlSubstituteEntitiesDefault (1);
    xmlLoadExtDtdDefaultValue = 1;

    xsltStylesheetPtr xsl;
    xsl = xsltParseStylesheetFile ((const xmlChar *) xslPath);
    xmlDocPtr result;
    result = xsltApplyStylesheet(xsl, xmlIn, params);
    xsltSaveResultToFilename(outPath, result, xsl, 0);
    xsltFreeStylesheet (xsl);
    xmlFreeDoc (result);
    xsltCleanupGlobals ();
    xmlCleanupParser ();
}
