/*
 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
 * Copyright (C) 2012 - Scilab Enterprises - Calixte DENIZET
 *
 * This file must be used under the terms of the CeCILL.
 * This source file is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
 * are also available at
 * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
 *
 */

/*--------------------------------------------------------------------------*/
#include <stdlib.h>
#include <libxml/xpath.h>
#include <libxml/xmlreader.h>
#include "getScilabPreference.h"
#include "GetXmlFileEncoding.h"
#include "MALLOC.h"
#include "FileExist.h"
#include "stricmp.h"
#ifdef _MSC_VER
#include "strdup_windows.h"
#endif
#include "getshortpathname.h"
#include "BOOL.h"
#include "SCIHOME.h"

#define XCONF "%s/XConfiguration.xml"

/*--------------------------------------------------------------------------*/
static unsigned char isInit = 0;
static ScilabPreferences scilabPref;
static char * emptyAttribute = "";
/*--------------------------------------------------------------------------*/
static void getPrefs();
static char * getAttribute(xmlDocPtr doc, xmlXPathContextPtr xpathCtxt, const char * xpath);
static void initPrefs();
static void getDocAndCtxt(xmlDocPtr * doc, xmlXPathContextPtr * xpathCtxt);
/*--------------------------------------------------------------------------*/
const ScilabPreferences * getScilabPreferences()
{
    getPrefs();
    return &scilabPref;
}
/*--------------------------------------------------------------------------*/
void initPrefs()
{
    scilabPref.heapSize = NULL;
    scilabPref.adaptToDisplay = NULL;
    scilabPref.columnsToDisplay = NULL;
    scilabPref.linesToDisplay = NULL;
    scilabPref.historySaveAfter = NULL;
    scilabPref.historyFile = NULL;
    scilabPref.historyLines = NULL;
    scilabPref.historyEnable = NULL;
    scilabPref.ieee = NULL;
    scilabPref.format = NULL;
    scilabPref.formatWidth = NULL;
    scilabPref.language = NULL;
    scilabPref.startup_dir_use = NULL;
    scilabPref.startup_dir_default = NULL;
    scilabPref.startup_dir_previous = NULL;
}
/*--------------------------------------------------------------------------*/
void reloadScilabPreferences()
{
    clearScilabPreferences();
    getPrefs();
}
/*--------------------------------------------------------------------------*/
void clearScilabPreferences()
{
    if (isInit == 1)
    {
        if (scilabPref.heapSize)
        {
            FREE((void*)scilabPref.heapSize);
        }
        if (scilabPref.adaptToDisplay)
        {
            FREE((void*)scilabPref.adaptToDisplay);
        }
        if (scilabPref.columnsToDisplay)
        {
            FREE((void*)scilabPref.columnsToDisplay);
        }
        if (scilabPref.linesToDisplay)
        {
            FREE((void*)scilabPref.linesToDisplay);
        }
        if (scilabPref.historySaveAfter)
        {
            FREE((void*)scilabPref.historySaveAfter);
        }
        if (scilabPref.historyFile)
        {
            FREE((void*)scilabPref.historyFile);
        }
        if (scilabPref.historyLines)
        {
            FREE((void*)scilabPref.historyLines);
        }
        if (scilabPref.historyEnable)
        {
            FREE((void*)scilabPref.historyEnable);
        }
        if (scilabPref.ieee)
        {
            FREE((void*)scilabPref.ieee);
        }
        if (scilabPref.format)
        {
            FREE((void*)scilabPref.format);
        }
        if (scilabPref.formatWidth)
        {
            FREE((void*)scilabPref.formatWidth);
        }
        if (scilabPref.language)
        {
            FREE((void*)scilabPref.language);
        }
        if (scilabPref.startup_dir_use)
        {
            FREE((void*)scilabPref.startup_dir_use);
        }
        if (scilabPref.startup_dir_default)
        {
            FREE((void*)scilabPref.startup_dir_default);
        }
        if (scilabPref.startup_dir_previous)
        {
            FREE((void*)scilabPref.startup_dir_previous);
        }
        initPrefs();
    }
    isInit = 0;
}
/*--------------------------------------------------------------------------*/
void getPrefs()
{
    if (!isInit)
    {
        xmlDocPtr doc = NULL;
        xmlXPathContextPtr xpathCtxt = NULL;

        initPrefs();

        getDocAndCtxt(&doc, &xpathCtxt);
        if (doc == NULL)
        {
            return;
        }

        scilabPref.heapSize = strdup(getAttribute(doc, xpathCtxt, HEAPSIZE_XPATH));
        scilabPref.adaptToDisplay = strdup(getAttribute(doc, xpathCtxt, ADAPTTODISPLAY_XPATH));
        scilabPref.columnsToDisplay = strdup(getAttribute(doc, xpathCtxt, COLUMNSTODISPLAY_XPATH));
        scilabPref.linesToDisplay = strdup(getAttribute(doc, xpathCtxt, LINESTODISPLAY_XPATH));
        scilabPref.historySaveAfter = strdup(getAttribute(doc, xpathCtxt, HISTORYSAVEAFTER_XPATH));
        scilabPref.historyFile = strdup(getAttribute(doc, xpathCtxt, HISTORYFILE_XPATH));
        scilabPref.historyLines = strdup(getAttribute(doc, xpathCtxt, HISTORYLINES_XPATH));
        scilabPref.historyEnable = strdup(getAttribute(doc, xpathCtxt, HISTORYENABLE_XPATH));
        scilabPref.ieee = strdup(getAttribute(doc, xpathCtxt, IEEE_XPATH));
        scilabPref.format = strdup(getAttribute(doc, xpathCtxt, FORMAT_XPATH));
        scilabPref.formatWidth = strdup(getAttribute(doc, xpathCtxt, FORMATWIDTH_XPATH));
        scilabPref.language = strdup(getAttribute(doc, xpathCtxt, LANGUAGE_XPATH));
        scilabPref.startup_dir_use = strdup(getAttribute(doc, xpathCtxt, STARTUP_DIR_USE_XPATH));
        scilabPref.startup_dir_default = strdup(getAttribute(doc, xpathCtxt, STARTUP_DIR_DEFAULT_XPATH));
        scilabPref.startup_dir_previous = strdup(getAttribute(doc, xpathCtxt, STARTUP_DIR_PREVIOUS_XPATH));

        xmlXPathFreeContext(xpathCtxt);
        xmlFreeDoc(doc);

        isInit = 1;
    }
}
/*--------------------------------------------------------------------------*/
char * getAttribute(xmlDocPtr doc, xmlXPathContextPtr xpathCtxt, const char * xpath)
{
    char * value = emptyAttribute;
    xmlXPathObjectPtr xpathObj = xmlXPathEval((const xmlChar*)xpath, xpathCtxt);
    if (xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeMax)
    {
        value = (char *)((xmlAttrPtr)xpathObj->nodesetval->nodeTab[0])->children->content;
    }

    if (xpathObj)
    {
        xmlXPathFreeObject(xpathObj);
    }

    return value;
}
/*--------------------------------------------------------------------------*/
void getDocAndCtxt(xmlDocPtr * doc, xmlXPathContextPtr * xpathCtxt)
{
    char * SCIHOME = NULL;
    char * path = NULL;
    BOOL bConvert = FALSE;
    char * shortfilename_xml_conf = NULL;
    char * ret = NULL;
    *doc = NULL;
    *xpathCtxt = NULL;

    SCIHOME = getSCIHOME();
    path = (char *)MALLOC(strlen(SCIHOME) + strlen(XCONF));

    sprintf(path, XCONF, SCIHOME);
    FREE(SCIHOME);

    if (FileExist(path))
    {
        shortfilename_xml_conf = getshortpathname(path, &bConvert);
        if (shortfilename_xml_conf)
        {
            *doc = xmlParseFile(shortfilename_xml_conf);
            FREE(shortfilename_xml_conf);
            shortfilename_xml_conf = NULL;
        }
    }

    FREE(path);
    path = NULL;

    if (*doc == NULL)
    {
        *xpathCtxt = NULL;
        return;
    }

    if (stricmp((*doc)->encoding, "utf-8"))
    {
        xmlFreeDoc(*doc);
        *doc = NULL;
        *xpathCtxt = NULL;
        return;
    }

    *xpathCtxt = xmlXPathNewContext(*doc);
    if (*xpathCtxt == NULL)
    {
        xmlFreeDoc(*doc);
        *doc = NULL;
    }
}
/*--------------------------------------------------------------------------*/
char * getPrefAttributeValue(const char * xpath, const char * attribute)
{
    xmlDocPtr doc = NULL;
    xmlXPathContextPtr xpathCtxt = NULL;
    char * ret = NULL;
    unsigned int xlen = 0;
    unsigned int alen = 0;
    char * query = NULL;

    if (!xpath || !attribute)
    {
        return NULL;
    }

    query = (char *)MALLOC((xlen + alen + 2 + 1) * sizeof(char));
    if (!query)
    {
        return NULL;
    }

    xlen = (unsigned int) strlen(xpath);
    alen = (unsigned int) strlen(attribute);

    getDocAndCtxt(&doc, &xpathCtxt);
    if (doc == NULL)
    {
        FREE(query);
        return NULL;
    }

    sprintf(query, "%s/@%s", xpath, attribute);
    query[xlen + alen + 2] = '\0';

    ret = strdup(getAttribute(doc, xpathCtxt, (const xmlChar*)query));
    FREE(query);

    xmlXPathFreeContext(xpathCtxt);
    xmlFreeDoc(doc);

    return ret;
}
/*--------------------------------------------------------------------------*/
char ** getPrefAttributesValues(const char * xpath, const char ** attributes, const unsigned int attrLen)
{
    xmlDocPtr doc = NULL;
    xmlXPathContextPtr xpathCtxt = NULL;
    xmlXPathObjectPtr xpathObj = NULL;
    char ** ret = NULL;

    if (!xpath || !attributes || !attrLen)
    {
        return NULL;
    }

    getDocAndCtxt(&doc, &xpathCtxt);
    if (doc == NULL || xpathCtxt == NULL)
    {
        return NULL;
    }

    xpathObj = xmlXPathEval((const xmlChar*)xpath, xpathCtxt);
    if (xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeMax)
    {
        unsigned int i;
        xmlNode * node = (xmlNode*)xpathObj->nodesetval->nodeTab[0];
        ret = (char**)MALLOC(sizeof(char*) * attrLen);
        if (!ret)
        {
            xmlXPathFreeObject(xpathObj);
            xmlXPathFreeContext(xpathCtxt);
            xmlFreeDoc(doc);
            return NULL;
        }

        for (i = 0; i < attrLen; i++)
        {
            xmlAttr * attrs = xmlHasProp(node, (const xmlChar *)attributes[i]);
            if (attrs)
            {
                ret[i] = strdup((const char *)attrs->children->content);
            }

            if (!attrs || !ret[i])
            {
                unsigned int j;
                for (j = 0; j < i; j++)
                {
                    free(ret[j]);
                }
                FREE(ret);
                ret = NULL;
                break;
            }
        }
    }

    if (xpathObj)
    {
        xmlXPathFreeObject(xpathObj);
    }

    xmlXPathFreeContext(xpathCtxt);
    xmlFreeDoc(doc);

    return ret;
}
/*--------------------------------------------------------------------------*/
void setPrefAttributesValues(const char * xpath, const char ** kv, const unsigned int kvLen)
{
    xmlDocPtr doc = NULL;
    xmlXPathContextPtr xpathCtxt = NULL;
    xmlXPathObjectPtr xpathObj = NULL;
    char * SCIHOME = NULL;
    char * path = NULL;
    BOOL bConvert = FALSE;
    char * shortfilename_xml_conf = NULL;

    if (!xpath || !kv || !kvLen)
    {
        return;
    }

    getDocAndCtxt(&doc, &xpathCtxt);
    if (doc == NULL)
    {
        return;
    }

    xpathObj = xmlXPathEval((const xmlChar*)xpath, xpathCtxt);
    if (xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeMax)
    {
        int i;
        xmlNode * node = (xmlNode*)xpathObj->nodesetval->nodeTab[0];
        for (i = 0; i < (int)kvLen / 2; i++)
        {
            xmlAttr * attrs = xmlHasProp(node, (const xmlChar *)kv[2 * i]);
            if (attrs)
            {
                attrs->children->content = xmlStrdup((const xmlChar *)kv[2 * i + 1]);
            }
            else
            {
                break;
            }
        }
    }

    if (xpathObj)
    {
        xmlXPathFreeObject(xpathObj);
    }

    xmlXPathFreeContext(xpathCtxt);

    // Save
    xmlThrDefIndentTreeOutput(1);

    SCIHOME = getSCIHOME();
    path = (char *)MALLOC(strlen(SCIHOME) + strlen(XCONF));

    sprintf(path, XCONF, SCIHOME);
    FREE(SCIHOME);

    if (FileExist(path))
    {
        shortfilename_xml_conf = getshortpathname(path, &bConvert);
        if (shortfilename_xml_conf)
        {
            xmlSaveFormatFile(shortfilename_xml_conf, doc, 2);
            FREE(shortfilename_xml_conf);
            shortfilename_xml_conf = NULL;
        }
    }

    FREE(path);
    path = NULL;

    xmlFreeDoc(doc);
}