/*   Cataract - Static web photo gallery generator
 *   Copyright (C) 2008 Tomas Bzatek <tbzatek@users.sourceforge.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 2
 *   of the License, or 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, write to the Free Software
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>

#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>

#include <glib.h>

#include "xml-parser.h"
#include "gallery-utils.h"



/*
 *   xml_parser_load: initialize and load the XML document
 */
TXMLFile *
xml_parser_load (const gchar *filename)
{
  TXMLFile *file;

  file = g_malloc0 (sizeof (TXMLFile));

  /*  Load XML document  */
  file->doc = xmlParseFile (filename);
  if (! file->doc) {
    log_error ("Error: unable to parse file \"%s\"\n", filename);
    xml_parser_free (file);
    return NULL;
  }

  /*  Create xpath evaluation context  */
  file->xpathCtx = xmlXPathNewContext (file->doc);
  if (! file->xpathCtx) {
    log_error ("Error: unable to create new XPath context\n");
    xml_parser_free (file);
    return FALSE;
  }

  return file;
}


/*
 *   xml_parser_free: close the XML document parser and frees all memory
 */
void
xml_parser_free (TXMLFile *file)
{
  if (file) {
    if (file->xpathCtx)
      xmlXPathFreeContext (file->xpathCtx);
    if (file->doc)
      xmlFreeDoc (file->doc);
    g_free (file);
  }
}

/*
 *   xml_file_get_node_name: retrieve name of the XPath node
 */
gchar *
xml_file_get_node_name (TXMLFile *file, const gchar *x_path)
{
  xmlXPathObjectPtr xpathObj;
  xmlNodePtr cur;
  gchar *attrv;

  if (! file || ! x_path)
    return NULL;

  /* Evaluate xpath expression */
  xpathObj = xmlXPathEvalExpression ((const xmlChar *) x_path, file->xpathCtx);
  if (xpathObj == NULL) {
    log_error ("Error: unable to evaluate xpath expression \"%s\"\n", x_path);
    return NULL;
  }

  attrv = NULL;
  if ((xpathObj->nodesetval) && (xpathObj->nodesetval->nodeNr > 0)) {
    cur = xpathObj->nodesetval->nodeTab[0];
    if (cur->name)
      attrv = g_strdup ((const gchar *) cur->name);
  }

  xmlXPathFreeObject (xpathObj);
  return attrv;
}


/*
 *   xml_file_get_node_value: retrieve string value from XPath node
 *     - multiple matched nodes will be concatenated into one string
 *       - otherwise please use [0], [1] etc. quantificators
 */
gchar *
xml_file_get_node_value (TXMLFile *file, const gchar *x_path)
{
  xmlXPathObjectPtr xpathObj;
  xmlNodePtr cur;
  gchar *val, *valx;
  int i;

  if (! file || ! x_path)
    return NULL;

  /* Evaluate xpath expression */
  xpathObj = xmlXPathEvalExpression ((const xmlChar *) x_path, file->xpathCtx);
  if (xpathObj == NULL) {
    log_error ("Error: unable to evaluate xpath expression \"%s\"\n", x_path);
    return NULL;
  }

  val = NULL;
  if (xpathObj->nodesetval && xpathObj->nodesetval->nodeNr > 0)
    for (i = 0; i < xpathObj->nodesetval->nodeNr; i++) {
      cur = xpathObj->nodesetval->nodeTab[i];
      if (cur->content) {
        if (val == NULL) {
          val = g_strdup ((gchar *) cur->content);
        } else {
          valx = g_strconcat (val, (gchar *) cur->content, NULL);
          g_free (val);
          val = valx;
        }
      }
    }

  xmlXPathFreeObject (xpathObj);
  return val;
}

gchar *
xml_file_get_node_value_with_default (TXMLFile *file, const gchar *x_path, const gchar *_default)
{
  gchar *s;

  s = xml_file_get_node_value (file, x_path);

  return s ? s : g_strdup (_default);
}


/*
 *   xml_file_get_node_attribute: retrieve attribute value from XPath node
 */
gchar *
xml_file_get_node_attribute (TXMLFile *file, const gchar *x_path, const gchar *attr)
{
  xmlXPathObjectPtr xpathObj;
  xmlNodePtr cur;
  xmlChar *attrvx;
  gchar *attrv;

  if (! file || ! x_path)
    return NULL;

  /* Evaluate xpath expression */
  xpathObj = xmlXPathEvalExpression ((const xmlChar *) x_path, file->xpathCtx);
  if (xpathObj == NULL) {
    log_error ("Error: unable to evaluate xpath expression \"%s\"\n", x_path);
    return NULL;
  }

  attrv = NULL;
  if (xpathObj->nodesetval && xpathObj->nodesetval->nodeNr > 0) {
    cur = xpathObj->nodesetval->nodeTab[0];
    attrvx = xmlGetProp (cur, (const xmlChar *) attr);
    if (attrvx) {
      attrv = g_strdup ((gchar*) attrvx);
      xmlFree (attrvx);
    }
  }

  xmlXPathFreeObject (xpathObj);
  return attrv;
}

long int
xml_file_get_node_attribute_long (TXMLFile *file, const gchar *x_path, const gchar *attr, const int _default)
{
  gchar *s;
  long int i;

  s = xml_file_get_node_attribute (file, x_path, attr);
  if (s == NULL)
    return _default;

  i = atol (s);
  g_free (s);
  return i;
}

gboolean
xml_file_get_node_attribute_boolean (TXMLFile *file, const gchar *x_path, const gchar *attr, const gboolean _default)
{
  gchar *s;
  gboolean b;

  s = xml_file_get_node_attribute (file, x_path, attr);
  b = s ? (strcasecmp (s, "yes") == 0 || strcasecmp (s, "true") == 0) : _default;
  g_free (s);
  return b;
}


/*
 *   xml_file_get_node_present: existency test of the XPath node
 */
gboolean
xml_file_get_node_present (TXMLFile *file, const gchar *x_path)
{
  return (xml_file_node_get_children_count (file, x_path) > 0);
}


/*
 *   xml_file_node_get_children_count: retrieve number of children items of the specified XPath node
 */
int
xml_file_node_get_children_count (TXMLFile *file, const gchar *x_path)
{
  xmlXPathObjectPtr xpathObj;
  int count;

  if (! file || ! x_path)
    return 0;

  /* Evaluate xpath expression */
  xpathObj = xmlXPathEvalExpression ((const xmlChar *) x_path, file->xpathCtx);
  if (xpathObj == NULL) {
    log_error ("Error: unable to evaluate xpath expression \"%s\"\n", x_path);
    return 0;
  }

  count = 0;
  if (xpathObj->nodesetval)
    count = xpathObj->nodesetval->nodeNr;

  xmlXPathFreeObject (xpathObj);
  return count;
}
