/* ====================================================================
 * FTD4Linux Software License, Version 1.0
 *
 * Copyright (c) 2000-2004 The FTD4Linux Development Team.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        FTD4Linux Development Team (http://www.ftd4linux.2y.net/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "FTD" and "FTD4Linux" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact alguno@ftd4linux.2y.net.
 *
 * 5. Products derived from this software may not be called "FTD4Linux",
 *    nor may "FTD4Linux" appear in their name, without prior written
 *    permission of the FTD4Linux Development Team.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE FTD4LINUX DEVELOPMENT TEAM OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <glib.h>
#include <libxml/xmlmemory.h>
#include <libxml/debugXML.h>
#include <libxml/HTMLtree.h>
#include <libxml/xmlIO.h>
#include <libxml/xinclude.h>
#include <libxml/catalog.h>
#include <libxslt/xsltInternals.h>
#include <libxslt/xslt.h>
#include <libxslt/transform.h>
#include <libxslt/xsltutils.h>

#include "ftd4linux.h"

static xsltStylesheetPtr    ftd_gui_xslt_template_new(char *);

/**
 * Alle stylesheets die gebruikt worden voor het omzetten van gegevens naar HTML.
 */
GHashTable *templates = NULL;		/* gecompileerde stylesheets */
#define MAX_PARAMETERS 64

/**
 * Een eventuele foutmelding van de XML-parser.
 */
static char *xml_parser_errmsg = NULL;

/**
 * De callback-functie voor als er iets fout gaat met het parsen van de XML.
 */
static void
xml_parser_error_func (void *ctx, const char *msg, ...)
{
    va_list args;
    char *orig = xml_parser_errmsg;
    char *tmp;

    va_start(args, msg);

#ifdef __CEGCC__
    // Onder CeGCC kan er op dit punt met de originele code die verder hieronder staat
    // een stack overflow optreden.. Moet nog verder onderzocht worden.. Werk er voorlopig
    // omheen door de foutmelding direct naar de stdio te schrijven.
    vprintf(msg, args);
#else
    tmp = g_strdup_vprintf(msg, args);
    if (orig) {
        xml_parser_errmsg = g_strdup_printf("%s %s", orig, tmp);
        g_free(orig);
        g_free(tmp);
    } else {
        xml_parser_errmsg = tmp;
    }
#endif

    va_end(args);
}

/**
 * Initialiseert de XSLT engine.
 */
void
ftd_gui_xslt_init(void)
{
    char *xslt_path;

    xmlSubstituteEntitiesDefault(1);
    xmlLoadExtDtdDefaultValue = 1;

    templates = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) xsltFreeStylesheet);

#ifdef HAVE_MOZILLA
    if (!ftd_config_get()->gebruikFontFamily) {
        char *xslt_path2;

        xslt_path = XSLT_TYPE_LIST;
        xslt_path2 = XSLT_TYPE_LIST_ALT_FONT;
        g_hash_table_insert(templates, g_strdup(xslt_path),
                            ftd_gui_xslt_template_new(xslt_path2));
        g_free(xslt_path);
        g_free(xslt_path2);

        xslt_path = XSLT_TYPE_LIST_TOTAL;
        xslt_path2 = XSLT_TYPE_LIST_TOTAL_ALT_FONT;
        g_hash_table_insert(templates, g_strdup(xslt_path),
                            ftd_gui_xslt_template_new(xslt_path2));
        g_free(xslt_path);
        g_free(xslt_path2);

        xslt_path = XSLT_TYPE_LIST_EMPTY;
        xslt_path2 = XSLT_TYPE_LIST_EMPTY_ALT_FONT;
        g_hash_table_insert(templates, g_strdup(xslt_path),
                            ftd_gui_xslt_template_new(xslt_path2));
        g_free(xslt_path);
        g_free(xslt_path2);

        xslt_path = XSLT_TYPE_POSTINFO;
        xslt_path2 = XSLT_TYPE_POSTINFO_ALT_FONT;
        g_hash_table_insert(templates, g_strdup(xslt_path),
                            ftd_gui_xslt_template_new(xslt_path2));
        g_free(xslt_path);
        g_free(xslt_path2);

        xslt_path = XSLT_TYPE_WATCHLIST;
        xslt_path2 = XSLT_TYPE_WATCHLIST_ALT_FONT;
        g_hash_table_insert(templates, g_strdup(xslt_path),
                            ftd_gui_xslt_template_new(xslt_path2));
        g_free(xslt_path);
        g_free(xslt_path2);

        xslt_path = XSLT_TYPE_MAIL;
        xslt_path2 = XSLT_TYPE_MAIL_ALT_FONT;
        g_hash_table_insert(templates, g_strdup(xslt_path),
                            ftd_gui_xslt_template_new(xslt_path2));
        g_free(xslt_path);
        g_free(xslt_path2);

        xslt_path = XSLT_TYPE_MYFTD;
        xslt_path2 = XSLT_TYPE_MYFTD_ALT_FONT;
        g_hash_table_insert(templates, g_strdup(xslt_path),
                            ftd_gui_xslt_template_new(xslt_path2));
        g_free(xslt_path);
        g_free(xslt_path2);
    } else {
#endif
        xslt_path = XSLT_TYPE_LIST;
        g_hash_table_insert(templates, g_strdup(xslt_path),
                            ftd_gui_xslt_template_new(xslt_path));
        g_free(xslt_path);

        xslt_path = XSLT_TYPE_LIST_TOTAL;
        g_hash_table_insert(templates, g_strdup(xslt_path),
                            ftd_gui_xslt_template_new(xslt_path));
        g_free(xslt_path);

        xslt_path = XSLT_TYPE_LIST_EMPTY;
        g_hash_table_insert(templates, g_strdup(xslt_path),
                            ftd_gui_xslt_template_new(xslt_path));
        g_free(xslt_path);

        xslt_path = XSLT_TYPE_POSTINFO;
        g_hash_table_insert(templates, g_strdup(xslt_path),
                            ftd_gui_xslt_template_new(xslt_path));
        g_free(xslt_path);

        xslt_path = XSLT_TYPE_WATCHLIST;
        g_hash_table_insert(templates, g_strdup(xslt_path),
                            ftd_gui_xslt_template_new(xslt_path));
        g_free(xslt_path);

        xslt_path = XSLT_TYPE_MAIL;
        g_hash_table_insert(templates, g_strdup(xslt_path),
                            ftd_gui_xslt_template_new(xslt_path));
        g_free(xslt_path);

        xslt_path = XSLT_TYPE_MYFTD;
        g_hash_table_insert(templates, g_strdup(xslt_path),
                            ftd_gui_xslt_template_new(xslt_path));
        g_free(xslt_path);
#ifdef HAVE_MOZILLA
    }
#endif
}

/**
 * Maakt een nieuwe stylesheet aan.
 *
 * @arg     filename        De naam van hte bestand waar de stylesheet in staat.
 *
 * @return  Een nieuwe stylesheet met de inhoud van het gegeven bestand.
 */
static xsltStylesheetPtr
ftd_gui_xslt_template_new(char *filename) //filename's zie header
{
    xsltStylesheetPtr template;

    g_assert(filename);

    template = xsltParseStylesheetFile((const xmlChar *) filename);
    if (!template) {
        g_warning("ftd_gui_xslt_template_new(): failed to parse stylesheet: %s.\n", filename);
    }

    return template;
}

/**
 * Geeft alle stylesheets vrij.
 */
void
ftd_gui_xslt_free(void)
{
    if( templates ) {
        g_hash_table_destroy(templates);
        templates = NULL;
    }
}

/**
 * Voegt data (XML) en stijl (XSLT) samen om een HTML uitvoer te bouwen.
 *
 * @arg     xmlData         De data. Een string met XML gecodeerde data erin.
 * @arg     type            De naam van de stylesheet die gebruikt moet worden.
 * @arg     filename        De bestandsnaam waar de uitvoer naartoe geschreven dient te worden.
 *
 * @return  0 indien dit succesvol was.
 */
int
ftd_gui_xslt_process(FtdString *xmlData, char *type, char *filename)
{
    xsltStylesheetPtr template;
    xmlDocPtr doc, html;
    xmlNodePtr cur;
    int ret;
    char *volledigeFilename;
    char *params[MAX_PARAMETERS + 1];
    char *full_dir;
    FtdString *tmp1, *tmp2, *tmp3, *tmp4, *tmp5;

    g_assert(xmlData);

    template = g_hash_table_lookup(templates, type);
    if (!template) {
        g_warning("ftd_gui_xslt_process(): attempt to use stylesheet named '%s' but\n"
                        "it does not seem to exist or it could not be parsed successfully\n", type);
        return -1;
    }

    xmlSetGenericErrorFunc(NULL, xml_parser_error_func);

    if (xml_parser_errmsg) {
        g_free(xml_parser_errmsg);
        xml_parser_errmsg = NULL;
    }

    doc = xmlParseMemory(ftd_string_value(xmlData),
                         ftd_string_length(xmlData));
    cur = xmlDocGetRootElement(doc);
    if (!cur) {
        g_warning("ftd_gui_xslt_process(): cannot parse document.\n");
        g_print("%s\n", ftd_string_value(xmlData));
        g_print("Reason: %s\n", xml_parser_errmsg);
        return -2;
    }

    full_dir = ftd_file_parse_data_dir(NULL);
    tmp1 = ftd_string_new(full_dir);
    tmp2 = ftd_string_replace(tmp1, "\\", "/");
    tmp3 = ftd_string_replace(tmp2, " ", "%20");
    tmp4 = ftd_string_replace(tmp3, "(", "%28");
    tmp5 = ftd_string_replace(tmp4, ")", "%29");
    params[0] = g_strdup("cssDir");
#ifdef WIN32
    params[1] = g_strdup_printf("'file:///%s/'", ftd_string_value(tmp5));
#else
    params[1] = g_strdup_printf("'file://%s/'", ftd_string_value(tmp5));
#endif
    params[2] = g_strdup("imgDir");
#ifdef WIN32
    params[3] = g_strdup_printf("'file:///%s/images'", ftd_string_value(tmp5));
#else
    params[3] = g_strdup_printf("'file://%s/images'", ftd_string_value(tmp5));
#endif

    ftd_string_free(tmp5);
    ftd_string_free(tmp4);
    ftd_string_free(tmp3);
    ftd_string_free(tmp2);
    ftd_string_free(tmp1);
    g_free(full_dir);
    params[4] = NULL;

    html = xsltApplyStylesheet(template, doc, (const char **) params);

    g_free(params[0]);
    g_free(params[1]);
    g_free(params[2]);
    g_free(params[3]);

    cur = xmlDocGetRootElement(html);
    if (!cur) {
        g_warning("ftd_xslt_process(): failed to apply stylesheet to document.\n");
        xmlFreeDoc(doc);
        return -3;
    }

    volledigeFilename = ftd_file_parse_cache_filename(filename, FALSE);
    ret = xsltSaveResultToFilename(volledigeFilename, html, template, 0);
    if (ret < 0) {
        g_warning("ftd_xslt_process(): cannot store result html page.\n");
        g_warning("ftd_xslt_process(): ret = %i\n", ret);
        g_warning("ftd_xslt_process(): volledigeFilename = %s\n", volledigeFilename);
        xmlFreeDoc(doc);
        xmlFreeDoc(html);
        g_free(volledigeFilename);
        return -4;
    }

    g_free(volledigeFilename);
    xmlFreeDoc(doc);
    xmlFreeDoc(html);

    return 0;
}
