#include "libxml.h"
#include "ruby_xml_dtd.h"

VALUE cXMLDtd;

void
ruby_xml_dtd_free(ruby_xml_dtd *rxdtd) {
  if (rxdtd->dtd != NULL) {
    xmlFreeDtd(rxdtd->dtd);
    rxdtd->dtd = NULL;
  }

  free(rxdtd);
}

static void
ruby_xml_dtd_mark(ruby_xml_dtd *rxdtd) {
  return;
  //if (rxdtd == NULL) return;
  //if (!NIL_P(rxd->xmlver)) rb_gc_mark(rxd->xmlver);
}

/*
 * call-seq:
 *    XML::Dtd.new("public system") => dtd
 *    XML::Dtd.new("public", "system") => dtd
 * 
 * Create a new Dtd from the specified public and system
 * identifiers.
 */
VALUE
ruby_xml_dtd_initialize(int argc, VALUE *argv, VALUE class) {
  ruby_xml_dtd *rxdtd;
  VALUE external, system, dtd_string;
  xmlParserInputBufferPtr buffer;
  xmlCharEncoding enc = XML_CHAR_ENCODING_NONE;
  xmlChar *new_string;

  // 1 argument -- string               --> parsujeme jako dtd
  // 2 argumenty -- public, system      --> bude se hledat
  switch (argc) {
  case 2:
    rb_scan_args(argc, argv, "20", &external, &system);

    Check_Type(external, T_STRING);
    Check_Type(system,   T_STRING);
    rxdtd = ALLOC(ruby_xml_dtd);
    rxdtd->dtd = xmlParseDTD( (xmlChar*)StringValuePtr(external),
                              (xmlChar*)StringValuePtr(system) );
    if (rxdtd->dtd == NULL) {
      free(rxdtd);
      return(Qfalse);
    }

    xmlSetTreeDoc( (xmlNodePtr)rxdtd->dtd, NULL );
    return( Data_Wrap_Struct(cXMLDtd, ruby_xml_dtd_mark, ruby_xml_dtd_free, rxdtd) );
    break;

/*
SV *
new(CLASS, external, system)
        char * CLASS
        char * external
        char * system
    ALIAS:
        parse_uri = 1
    PREINIT:
        xmlDtdPtr dtd = NULL;
    CODE:
        LibXML_error = sv_2mortal(newSVpv("", 0));
        dtd = xmlParseDTD((const xmlChar*)external, (const xmlChar*)system);
        if ( dtd == NULL ) {
            XSRETURN_UNDEF;
        }
        xmlSetTreeDoc((xmlNodePtr)dtd, NULL);
        RETVAL = PmmNodeToSv( (xmlNodePtr) dtd, NULL );
    OUTPUT:
        RETVAL
*/

  case 1:

    rb_scan_args(argc, argv, "10", &dtd_string);
    buffer = xmlAllocParserInputBuffer(enc);
    //if ( !buffer) return Qnil
    new_string = xmlStrdup((xmlChar*)StringValuePtr(dtd_string));
    xmlParserInputBufferPush(buffer, xmlStrlen(new_string), (const char*)new_string);

    rxdtd = ALLOC(ruby_xml_dtd);
    rxdtd->dtd = xmlIOParseDTD(NULL, buffer, enc);

    // NOTE: For some reason freeing this InputBuffer causes a segfault! 
    // xmlFreeParserInputBuffer(buffer); 
    xmlFree(new_string);

    return( Data_Wrap_Struct(cXMLDtd, ruby_xml_dtd_mark, ruby_xml_dtd_free, rxdtd) );

    break;
/*
SV * parse_string(CLASS, str, ...)
        char * CLASS
        char * str
    PREINIT:
        STRLEN n_a;
        xmlDtdPtr res;
        SV * encoding_sv;
        xmlParserInputBufferPtr buffer;
        xmlCharEncoding enc = XML_CHAR_ENCODING_NONE;
        xmlChar * new_string;
        STRLEN len;
    CODE:
        LibXML_init_error();
        if (items > 2) {
            encoding_sv = ST(2);
            if (items > 3) {
                croak("parse_string: too many parameters");
            }
            // warn("getting encoding...\n"); 
            enc = xmlParseCharEncoding(SvPV(encoding_sv, n_a));
            if (enc == XML_CHAR_ENCODING_ERROR) {
                croak("Parse of encoding %s failed: %s", SvPV(encoding_sv, n_a), SvPV(LibXML_error, n_a));
            }
        }
        buffer = xmlAllocParserInputBuffer(enc);
        // buffer = xmlParserInputBufferCreateMem(str, xmlStrlen(str), enc); 
        if ( !buffer)
            croak("cant create buffer!\n" );

        new_string = xmlStrdup((const xmlChar*)str);
        xmlParserInputBufferPush(buffer, xmlStrlen(new_string), (const char*)new_string);

        res = xmlIOParseDTD(NULL, buffer, enc);

        // NOTE: For some reason freeing this InputBuffer causes a segfault! 
        // xmlFreeParserInputBuffer(buffer); 
        xmlFree(new_string);

        sv_2mortal( LibXML_error );
        LibXML_croak_error();

        if (res == NULL) {
            croak("no DTD parsed!");
        }
        RETVAL = PmmNodeToSv((xmlNodePtr)res, NULL);
    OUTPUT:
        RETVAL
 */
    
  default:
    rb_raise(rb_eArgError, "wrong number of arguments (need 1 or 2)");
  }

  //docobj = ruby_xml_document_new2(cXMLDocument, xmlver);
  return Qnil;
}

// Rdoc needs to know 
#ifdef RDOC_NEVER_DEFINED
  mXML = rb_define_module("XML");
#endif

void
ruby_init_xml_dtd(void) {
  cXMLDtd = rb_define_class_under(mXML, "Dtd", rb_cObject);
  rb_define_singleton_method(cXMLDtd, "new", ruby_xml_dtd_initialize, -1);
  //rb_define_method(cXMLDocument, "xinclude", ruby_xml_document_xinclude, 0);
}

