/* libxml++
 * Copyright (C) 2000  Ari Johnson
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */

/* Modified by Motama GmbH, Copyright (C) 2006-2007 Motama GmbH */


#ifndef NMM_XML_PARSER_HPP
#define NMM_XML_PARSER_HPP NMM_XML_PARSER_HPP

#include "nmm/nmm_prefix.hpp"

#include "XMLProperty.hpp"

#include <libxml/parser.h>
#include <libxml/encoding.h>

namespace NMM {
  
  class XMLParserCallback {
  public:
    virtual ~XMLParserCallback() {}
    virtual void start_document(void) {}
    virtual void end_document(void) {}
    virtual void start_element(const std::string &, const XMLPropertyHash &) {}
    virtual void end_element(const std::string &) {}
    virtual void characters(const std::string &) {}
    virtual void comment(const std::string &) {}
    virtual void warning(const std::string &) {}
    virtual void error(const std::string &) {}
    virtual void fatal_error(const std::string &) {}
  };

  template <class ParserCallback = XMLParserCallback>
  class XMLParser {
  private:
    XMLParserCallback *_parser_callback;
    xmlParserCtxtPtr _context;

    static xmlEntityPtr _get_entity(void *_parser, const xmlChar *n) {
      return xmlGetPredefinedEntity(n);
    }

    static void _start_document(void *_parser) {
      XMLParser *parser;

      parser = (XMLParser *) _parser;
      parser->start_document();
    }

    static void _end_document(void *_parser) {
      XMLParser *parser;

      parser = (XMLParser *) _parser;
      parser->end_document();
    }

    static void _start_element(void *_parser, const xmlChar *n,
                               const xmlChar **p) {
      XMLParser *parser;
      XMLPropertyHash properties;

      if(p) {
        const xmlChar **cur;

        for(cur = p; cur && *cur; cur++) {
          std::string name, value;

          name = std::string((const char *) *cur++);
          value = std::string((const char *) *cur);

          properties[name] = new XMLProperty(name, value);
        }
      }

      parser = (XMLParser *) _parser;
      parser->start_element(std::string((const char *) n), properties);
    }

    static void _end_element(void *_parser, const xmlChar *n) {
      XMLParser *parser;

      parser = (XMLParser *) _parser;
      parser->end_element(std::string((const char *) n));
    }

    static void _characters(void *_parser, const xmlChar *s, int len) {
      XMLParser *parser;

      parser = (XMLParser *) _parser;
      parser->characters(std::string((const char *) s, len));
    }

    static void _comment(void *_parser, const xmlChar *s) {
      XMLParser *parser;

      parser = (XMLParser *) _parser;
      parser->comment(std::string((const char *) s));
    }

    static void _warning(void *_parser, const char *fmt, ...) {
      XMLParser *parser;

#if (__GNUC__>=3)
      __builtin_va_list arg;
#else
      va_list arg;
#endif
    
      char buff[1024];

      va_start(arg, fmt);
      vsprintf(buff, fmt, arg);
      va_end(arg);

      parser = (XMLParser *) _parser;
      parser->warning(std::string(buff));
    }

    static void _error(void *_parser, const char *fmt, ...) {
      XMLParser *parser;

#if (__GNUC__>=3)
      __builtin_va_list arg;
#else
      va_list arg;
#endif

      char buff[1024];

      va_start(arg, fmt);
      vsprintf(buff, fmt, arg);
      va_end(arg);

      parser = (XMLParser *) _parser;
      parser->error(std::string(buff));
    }

    static void _fatal_error(void *_parser, const char *fmt, ...) {
      XMLParser *parser;

#if (__GNUC__>=3)
      __builtin_va_list arg;
#else
      va_list arg;
#endif

      char buff[1024];

      va_start(arg, fmt);
      vsprintf(buff, fmt, arg);
      va_end(arg);

      parser = (XMLParser *) _parser;
      parser->fatal_error(std::string(buff));
    }

    void start_document(void) {
      if(_parser_callback)
        _parser_callback->start_document();
    }

    void end_document(void) {
      if(_parser_callback)
        _parser_callback->end_document();
    }

    void start_element(const std::string &n, const XMLPropertyHash &p) {
      if(_parser_callback)
        _parser_callback->start_element(n, p);
    }

    void end_element(const std::string &n) {
      if(_parser_callback)
        _parser_callback->end_element(n);
    }

    void characters(const std::string &s) {
      if(_parser_callback)
        _parser_callback->characters(s);
    }

    void comment(const std::string &s) {
      if(_parser_callback)
        _parser_callback->comment(s);
    }

    void warning(const std::string &s) {
      if(_parser_callback)
        _parser_callback->warning(s);
    }

    void error(const std::string &s) {
      if(_parser_callback)
        _parser_callback->error(s);
    }

    void fatal_error(const std::string &s) {
      if(_parser_callback)
        _parser_callback->fatal_error(s);
    }
    
  public:
    XMLParser() {
      xmlSAXHandler sax_handler = {
        NULL,    // internalSubset
        NULL,    // isStandalone
        NULL,    // hasInternalSubset
        NULL,    // hasExternalSubset
        NULL,    // resolveEntity
        _get_entity,  // getEntity
        NULL,    // entityDecl
        NULL,    // notationDecl
        NULL,    // attributeDecl
        NULL,    // elementDecl
        NULL,    // unparsedEntityDecl
        NULL,    // setDocumentLocator
        _start_document,  // startDocument
        _end_document,  // endDocument
        _start_element,  // startElement
        _end_element,  // endElement
        NULL,    // reference
        _characters,  // characters
        NULL,    // ignorableWhitespace
        NULL,    // processingInstruction
        _comment,    // comment
        _warning,    // warning
        _error,    // error
        _fatal_error,  // fatalError
        NULL,    // getParameterEntity
        NULL,    // cdataBlock
        NULL    // externalSubset
      };

      _parser_callback = new ParserCallback;
      _context = xmlCreatePushParserCtxt(&sax_handler, this, NULL, 0, NULL);
    }

    ~XMLParser() {
      if(_parser_callback)
        delete _parser_callback;

      if(_context)
        xmlFreeParserCtxt(_context);
    }

    void parse_chunk(const std::string &s) {
      xmlParseChunk(_context, s.c_str(), s.length(), 0);
    }

    void finish(void) {
      xmlParseChunk(_context, NULL, 0, 1);
    }
  };
  
}

#include "nmm/nmm_suffix.hpp"

#endif
