/*
 * This file is part of the Poliqarp suite.
 * 
 * Copyright (C) 2009 by Instytut Podstaw Informatyki Polskiej
 * Akademii Nauk (IPI PAN; Institute of Computer Science, Polish
 * Academy of Sciences; cf. www.ipipan.waw.pl).  All rights reserved.
 * 
 * This file may be distributed and/or modified under the terms of the
 * GNU General Public License version 2 as published by the Free Software
 * Foundation and appearing in the file gpl.txt included in the packaging
 * of this file.  (See http://www.gnu.org/licenses/translations.html for
 * unofficial translations. )
 * 
 * A commercial license is available from IPI PAN (contact
 * Michal.Ciesiolka@ipipan.waw.pl or ipi@ipipan.waw.pl for more
 * information).  Licensees holding a valid commercial license from IPI
 * PAN may use this file in accordance with that license.
 * 
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
 * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE.
 */

#ifndef BPNG_XML_PARSER
#define BPNG_XML_PARSER 1

#include <poliqarp-config.h>

#include <libxml/tree.h>
#include <libxml/xmlreader.h>
#include <libxml/xpath.h>

#include <bp/common.h>

static const int xml_node_type_attribute = 2;
static const int xml_node_type_cdata = 4;
static const int xml_node_type_comment = 8;
static const int xml_node_type_document = 9;
static const int xml_node_type_document_fragment = 11;
static const int xml_node_type_document_type = 10;
static const int xml_node_type_element = 1;
static const int xml_node_type_end_element = 15;
static const int xml_node_type_end_entity = 16;
static const int xml_node_type_entity = 6;
static const int xml_node_type_entity_reference = 5;
static const int xml_node_type_none = 0;
static const int xml_node_type_notation = 12;
static const int xml_node_type_processing_instruction = 7;
static const int xml_node_type_significant_whitespace = 14;
static const int xml_node_type_text = 3;
static const int xml_node_type_whitespace = 13;
static const int xml_node_type_xml_declaration = 17;

typedef xmlTextReader xml_reader;
typedef xmlDoc xml_document;
typedef xmlNode xml_node;

static inline bool xml_is_empty(xml_reader *reader)
{
   return xmlTextReaderIsEmptyElement(reader);
}

static inline xml_node *xml_expand(xml_reader *reader)
{
   return xmlTextReaderExpand(reader);
}

/**
 * Cast the current node to a string.
 * @return a newly allocated string
 */
static inline char *xml_node_as_string(xml_node *node)
{
   char *result = (char*) xmlXPathCastNodeToString(node);
   if (result == NULL)
      bp_system_error("memory allocation error");
   return result;
}

xml_document *xml_document_new(const char *path);

void xml_document_free(xml_document *);

xml_reader *xml_reader_new(const char *path);

void xml_reader_free(xml_reader *);

static inline int xml_read(xml_reader *reader)
{
   return xmlTextReaderRead(reader);
}

/**
 * Return the depth of the current node.
 */
static inline int xml_get_depth(xml_reader *reader)
{
   return xmlTextReaderDepth(reader);
}

/**
 * Check if the current node has the attribute with the provided value.
 */
static inline bool xml_has_attr(xml_reader * reader,
   const char *ns, const char *name, const char *check_value)
{
   xmlChar *value = xmlTextReaderGetAttributeNs(reader, (const xmlChar *) name,
      (const xmlChar *) ns);
   if (value == NULL) {
      return check_value == NULL;
   } else {
      if (check_value == NULL)
         return false;
      bool result = xmlStrEqual(value, (const xmlChar*) check_value);
      free(value);
      return result;
   }
}

/**
 * @return a newly allocated string
 */
static inline char *xml_get_attr(xml_reader *reader,
   const char *ns, const char *name)
{
   return (char*) xmlTextReaderGetAttributeNs(reader,
      (const xmlChar *) name, (const xmlChar *) ns);
}

/**
 * @return a newly allocated string
 */
static inline char *xml_get_id(xml_reader *reader)
{
   return (char*) xmlTextReaderGetAttributeNs(reader,
      (const xmlChar *) "id", XML_XML_NAMESPACE);
}

static inline bool xml_has_ns(xml_reader *reader, const char *check_ns)
{
   const xmlChar *ns = xmlTextReaderConstNamespaceUri(reader);
   if (ns == NULL && check_ns != NULL)
      return false;
   if (ns != NULL && check_ns == NULL)
      return false;
   if (ns != NULL && check_ns != NULL) {
      if (!xmlStrEqual(ns, (const xmlChar*) check_ns))
         return false;
   }
   return true;
}

static inline const char *xml_get_tag(xml_reader *reader)
{
   return (const char*) xmlTextReaderConstName(reader);
}

static inline bool xml_has_tag(xml_reader *reader, const char *check_tag)
{
   const char *tag = (const char*) xmlTextReaderConstName(reader);
   return strcmp(tag, check_tag) == 0;
}

static inline bool xml_has_start_tag(xml_reader* reader)
{
   return xmlTextReaderNodeType(reader) == xml_node_type_element;
}

static inline bool xml_has_end_tag(xml_reader* reader)
{
   return xmlTextReaderNodeType(reader) == xml_node_type_end_element;
}

void xml_init();

void xml_cleanup();

/**
 * Normalize whitespace in the given string, in-place.
 *
 * @see http://www.w3.org/TR/xpath#function-normalize-space
 */
void xml_normalize_space(char *s);

#endif
