//  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 "worldsquareui.h"
#include "xmlutil.h"
#include <string.h>
#include <libxml/xmlschemas.h>
#include <libxslt/xslt.h>
#include <libxslt/transform.h>

static xmlDocPtr worldsquare;
static xmlNodePtr root;
static xmlNsPtr ns;

static xsltStylesheetPtr xslNormalize;
static xsltStylesheetPtr xslReset;

void initWorldSquareXML (){
    xmlSubstituteEntitiesDefault (1);
    xmlLoadExtDtdDefaultValue = 1;
    xslNormalize = xsltParseStylesheetFile ((const xmlChar *) "web/benchmark/worldsquare_normalize.xsl");
    xslReset = xsltParseStylesheetFile ((const xmlChar *) "web/benchmark/worldsquare_reset.xsl");
}

static void normalize(){
    xmlDocPtr temp;
    temp = xsltApplyStylesheet(xslNormalize, worldsquare, NULL);
    xmlFreeDoc(worldsquare);
    xsltCleanupGlobals ();
    xmlCleanupParser ();
    worldsquare = temp;
    root = xmlDocGetRootElement(worldsquare);
}

xmlDocPtr getWorldSquareXML(){
    return worldsquare;
}

void saveWorldSquareXML (const char* filename){
    xmlSaveFileEnc(filename, worldsquare, "UTF-8");
}

void addAgentWorldSquareXML (const char* on, const char* direction, const char* spheresNbr){
    xmlNodePtr agent = xmlGetNodeByAttribute(root,"agent", "on", on);
    xmlNodePtr sphere;
    if (!agent) {
        xmlNodePtr hue;
        xmlNodePtr bumper;
        xmlNodePtr sonar;
        agent = xmlNewNode(ns, (const xmlChar*) "agent");
        xmlSetProp (agent, (const xmlChar*) "on", (const xmlChar*) on);
        xmlAddChild(root, agent);
        sphere = xmlNewNode(ns, (const xmlChar*) "sphere");
        xmlSetProp (sphere, (const xmlChar*) "number", (const xmlChar*) spheresNbr);
        xmlAddChild(agent, sphere);
        hue = xmlNewNode(ns, (const xmlChar*) "hue");
        xmlSetProp (hue, (const xmlChar*) "value", (const xmlChar*) "0");
        xmlAddChild(agent, hue);
        bumper = xmlNewNode(ns, (const xmlChar*) "bumper");
        xmlSetProp (bumper, (const xmlChar*) "front", (const xmlChar*) "0");
        xmlSetProp (bumper, (const xmlChar*) "back", (const xmlChar*) "0");
        xmlSetProp (bumper, (const xmlChar*) "left", (const xmlChar*) "0");
        xmlSetProp (bumper, (const xmlChar*) "right", (const xmlChar*) "0");
        xmlAddChild(agent, bumper);
        sonar = xmlNewNode(ns, (const xmlChar*) "sonar");
        xmlSetProp (sonar, (const xmlChar*) "front", (const xmlChar*) "0");
        xmlSetProp (sonar, (const xmlChar*) "back", (const xmlChar*) "0");
        xmlSetProp (sonar, (const xmlChar*) "left", (const xmlChar*) "0");
        xmlSetProp (sonar, (const xmlChar*) "right", (const xmlChar*) "0");
        xmlAddChild(agent, sonar);
    }
    else
        sphere = xmlGetNodeByName(agent,"sphere");
    xmlSetProp (sphere, (const xmlChar*) "number", (const xmlChar*) spheresNbr);
    xmlSetProp (agent, (const xmlChar*) "direction", (const xmlChar*) direction);
}

void moveAgentWorldSquareXML (const char* on, const char* at){
    xmlNodePtr agent = xmlGetNodeByAttribute(root,"agent", "on", on);
    xmlSetProp (agent, (const xmlChar*) "on", (const xmlChar*) at);
}

void addSphereWorldSquareXML (const char* on){
    if (!xmlGetNodeByAttribute(root,"sphere", "on", on)){
        xmlNodePtr sphere = xmlNewNode(ns, (const xmlChar*) "sphere");
        xmlSetProp (sphere, (const xmlChar*) "on", (const xmlChar*) on);
        xmlAddPrevSibling(xmlGetNodeByName(root, "sum_sounds"), sphere);
    }
}

const char* getSpheresNbrWorldSquareXML (const char* id){
    xmlNodePtr agent = xmlGetNodeByAttribute(root,"agent", "on", id);
    if (agent){
        xmlNodePtr sphere = xmlGetNodeByName(agent, "sphere");
        return (const char*) xmlGetProp(sphere, (const xmlChar*) "number");
    }
    else
        return "";
}

void setSpheresNbrWorldSquareXML (const char* id, const char* spheresNbr){
    xmlNodePtr agent = xmlGetNodeByAttribute(root,"agent", "on", id);
    if (agent){
        xmlNodePtr sphere = xmlGetNodeByName(agent, "sphere");
        xmlSetProp (sphere, (const xmlChar*) "number", (const xmlChar*) spheresNbr);
    }
}

void addSlabWorldSquareXML (const char* x, const char* y, const char* hue){
    char id[8] = "";
    xmlNodePtr slab;
    strcpy(id, x);
    strcat(id, y);
    slab = xmlGetNodeById(root, "slab", (const char*) id);
    if (!slab){
        slab = xmlNewNode(ns, (const xmlChar*) "slab");
        xmlSetProp (slab, (const xmlChar*) "id", (const xmlChar*) id);
        xmlSetProp (slab, (const xmlChar*) "x", (const xmlChar*) x);
        xmlSetProp (slab, (const xmlChar*) "y", (const xmlChar*) y);
        xmlSetProp (slab, (const xmlChar*) "hue", (const xmlChar*) hue);
        xmlAddPrevSibling(xmlFirstElementChild(root), slab);
        normalize();
    }
    else{
        xmlNodePtr sphere;
        xmlNodePtr agent;
        sphere = xmlGetNodeByAttribute(root,"sphere", "on", (const char*) id);
        if (sphere){
            xmlUnlinkNode(sphere);
            xmlFreeNode(sphere);
        }
        agent = xmlGetNodeByAttribute(root, "agent", "on", (const char*) id);
        if (agent){
            xmlUnlinkNode(agent);
            xmlFreeNode(agent);
        }
    }
}

void suppAllWorldSquareXML (const char* id){
    xmlNodePtr slab;
    xmlNodePtr sphere;
    xmlNodePtr agent;
    slab = xmlGetNodeById(root, "slab", id);
    xmlUnlinkNode(slab);
    xmlFreeNode(slab);
    sphere = xmlGetNodeByAttribute(root, "sphere", "on", id);
    if (sphere){
        xmlUnlinkNode(sphere);
        xmlFreeNode(sphere);
    }
    agent = xmlGetNodeByAttribute(root, "agent", "on", id);
    if (agent){
        xmlUnlinkNode(agent);
        xmlFreeNode(agent);
    }
    normalize();
}

void setHueSlabWorldSquareXML (const char* id, const char* hue){
    xmlNodePtr slab = xmlGetNodeById(root, "slab", id);
    xmlSetProp (slab, (const xmlChar*) "hue", (const xmlChar*) hue);
}

void suppSlabWorldSquareXML (const char* id){
    xmlNodePtr slab = xmlGetNodeById(root, "slab", id);
    xmlUnlinkNode(slab);
    xmlFreeNode(slab);
    normalize();
}

void suppAgentWorldSquareXML (const char* on){
    xmlNodePtr agent = xmlGetNodeByAttribute(root, "agent", "on", on);
    xmlUnlinkNode(agent);
    xmlFreeNode(agent);
}

void suppSphereWorldSquareXML (const char* on){
    xmlNodePtr sphere = xmlGetNodeByAttribute(root, "sphere", "on", on);
    xmlUnlinkNode(sphere);
    xmlFreeNode(sphere);
}

int readWorldSquareXML (const char* filename) {
    xmlDocPtr newWorldsquare = xmlReadFile(filename, "UTF-8", 0);
    if (newWorldsquare){
        xmlSchemaParserCtxtPtr parserCtxt = xmlSchemaNewParserCtxt("grammars/worldsquare.xsd");
        xmlSchemaPtr schema = xmlSchemaParse(parserCtxt);
        xmlSchemaValidCtxtPtr validCtxt;
        if (schema == NULL) {
            xmlSchemaFreeParserCtxt(parserCtxt);
            return 0;
        }
        validCtxt = xmlSchemaNewValidCtxt(schema);
        if (validCtxt == NULL) {
            xmlSchemaFree(schema);
            xmlSchemaFreeParserCtxt(parserCtxt);
            return 0;
        }
        if (!xmlSchemaValidateDoc(validCtxt, newWorldsquare)){
            if (worldsquare)
                xmlFreeDoc(worldsquare);
            if (!ns)
                ns = xmlNewNs(NULL,(xmlChar*)"http://www.nomoseed.org/worldsquare", NULL);
            worldsquare = xsltApplyStylesheet(xslReset, newWorldsquare, NULL);
            xmlFreeDoc(newWorldsquare);
            xsltCleanupGlobals ();
            xmlCleanupParser ();
            root = xmlDocGetRootElement(worldsquare);
            xmlSchemaFreeValidCtxt(validCtxt);
            xmlSchemaFree(schema);
            xmlSchemaFreeParserCtxt(parserCtxt);
            return 1;
        }
        else{
            xmlFreeDoc(newWorldsquare);
            xmlSchemaFreeValidCtxt(validCtxt);
            xmlSchemaFree(schema);
            xmlSchemaFreeParserCtxt(parserCtxt);
            return 0;
        }
    }
    else
        return 0;
}

void updateWorldSquareXML (const char* filename){
    worldsquare = xmlReadFile(filename, "UTF-8", 0 );
    root = xmlDocGetRootElement(worldsquare);
}

void resetWorldSquareXML(){
    xmlDocPtr newWorldsquare;
    newWorldsquare = xsltApplyStylesheet(xslReset, worldsquare, NULL);
    xmlFreeDoc(worldsquare);
    worldsquare = newWorldsquare;
    root = xmlDocGetRootElement(worldsquare);
}

void freeWorldSquareXML(){
    if (worldsquare)
        xmlFreeDoc(worldsquare);
    if (xslReset)
        xsltFreeStylesheet(xslReset);
    if (xslNormalize)
        xsltFreeStylesheet(xslNormalize);
    xsltCleanupGlobals ();
    xmlCleanupParser ();
}
