### ----------------------------------------------------
### -- LibXML
### -- Licenses: Artistic-2.0
### -- Authors: David Warring
### -- File: lib/LibXML/Raw.rakumod
### ----------------------------------------------------
### -- Chunk 3 of 4
URI, xmlCharP $external-id --> htmlDoc) is native($XML2) is symbol('htmlNewDoc') {*}
    method new(Str :$URI, Str :$external-id) {
        New($URI, $external-id);
    }

    method dump(Bool:D :$format = True) is DEPRECATED("see issue #90") {
        my xmlAllocedStr $out .= new;
        my int32 $len;

        self.DumpFormat($out, $len, +$format);
        $out.Str;
    }
}

#| xmlNode of type: XML_DOCUMENT_FRAG_NODE
class xmlDocFrag is xmlNode is export {
    also does domNode[$?CLASS, XML_DOCUMENT_FRAG_NODE];

    our sub New(xmlDoc $doc --> xmlDocFrag) is native($XML2) is symbol('xmlNewDocFragment') {*}
    method new(xmlDoc :$doc, xmlNode :$nodes) {
        my xmlDocFrag:D $frag = New($doc);
        $frag.set-nodes($_) with $nodes;
        $frag;
    }
}

class xmlNotation is export {
    has xmlCharP $.name;
    has xmlCharP $.publicId;
    has xmlCharP $.systemId;

    our sub New(xmlCharP, xmlCharP, xmlCharP  --> xmlNotation) is native($BIND-XML2) is symbol('xml6_notation_create') {*}
    method new(Str:D :$name!, Str :$publicId, Str :$systemId) {
        New($name, $publicId, $systemId);
    }
    method Free is native($BIND-XML2) is symbol('xml6_notation_free') {*}
    method Copy(--> xmlNotation) is native($BIND-XML2) is symbol('xml6_notation_copy') {*}
    method UniqueKey(--> xmlAllocedStr) is native($BIND-XML2) is symbol('xml6_notation_unique_key') {*}
    method copy { $.Copy }
    multi method Str(xmlNotation:D:){
        my xmlBuffer32 $buf .= new;
        $buf.NotationDump(self);
        my Str $content = $buf.Str;
        $buf.Free;
        $content;
    }
}

#| An XML DTD, as defined by <!DOCTYPE ... There is actually one for
#| the internal subset and for the external subset (type: XML_DTD_NODE).
class xmlDtd is anyNode is export {
    BEGIN @ClassMap[XML_DTD_NODE] = $?CLASS;
    has xmlHashTable  $.notations; # Hash table for notations if any
    has xmlHashTable   $.elements; # Hash table for element declarations if any
    has xmlHashTable $.attributes; # Hash table for attribute declarations if any
    has xmlHashTable   $.entities; # Hash table for entities if any
    has xmlCharP     $.ExternalID; # External identifier for PUBLIC DTD
    has xmlCharP       $.SystemID; # URI for a SYSTEM or PUBLIC DTD
    has xmlHashTable  $.pentities; # Hash table for param entities if any

    method publicId { $!ExternalID }
    method systemId { $!SystemID }

    method Copy(--> xmlDtd) is native($XML2) is symbol('xmlCopyDtd') {*}
    method copy() { $.Copy }

    our sub xmlIsXHTML(xmlCharP $systemID, xmlCharP $publicID --> int32) is native($XML2) {*}
    method IsXHTML returns int32 { xmlIsXHTML($!SystemID, $!ExternalID) }
    method getAttrDecl(xmlCharP $elem, xmlCharP $name --> xmlAttrDecl) is native($XML2) is symbol('xmlGetDtdAttrDesc') {*}
    method getElementDecl(xmlCharP --> xmlElementDecl) is native($XML2) is symbol('xmlGetDtdElementDesc') {*}
    method getEntity(xmlCharP --> xmlEntity) is native($BIND-XML2) is symbol('domGetEntityFromDtd') {*}
    method getParameterEntity(xmlCharP --> xmlEntity) is native($BIND-XML2) is symbol('domGetParameterEntityFromDtd') {*}
    method getNotation(xmlCharP --> xmlNotation) is native($XML2) is symbol('xmlGetDtdNotationDesc') {*}
    multi method new(:type($)! where 'internal', xmlDoc:D :$doc, Str :$name, Str :$external-id, Str :$system-id) {
        $doc.CreateIntSubset( $name, $external-id, $system-id);
    }
    multi method new(:type($)! where 'external', xmlDoc :$doc, Str :$name, Str :$external-id, Str :$system-id) {
        $doc.NewDtd( $name, $external-id, $system-id);
    }
    multi method new(|c) is default { fail c.raku }
    multi method parse(Str:D :$string!, xmlSAXHandler :$sax-handler, xmlEncodingStr:D :$enc!) {
        my Int $encoding = xmlParseCharEncoding($enc);
        my xmlParserInputBuffer:D $buffer .= new: :$enc, :$string;
        $sax-handler.IOParseDTD($buffer, $encoding);
    }
    multi method parse(Str :$external-id, Str :$system-id, xmlSAXHandler :$sax-handler) is default {
        $sax-handler.ParseDTD($external-id, $system-id);
    }
}

#| An Attribute declaration in a DTD (type: XML_ATTRIBUTE_DECL).
class xmlAttrDecl is anyNode is export {
    BEGIN @ClassMap[XML_ATTRIBUTE_DECL] = $?CLASS;
    has xmlAttrDecl     $.nexth; # next in hash table
    has int32           $.atype; # the attribute type
    has int32             $.def; # default mode (enum xmlAttributeDefault)
    has xmlCharP $.defaultValue; # or the default value
    has xmlEnumeration  $.values; # or the enumeration tree if any
    has xmlCharP       $.prefix; # the namespace prefix if any
    has xmlCharP         $.elem; # Element holding the attribute
}

#| An unit of storage for an entity, contains the string, the value and
#| the data needed for the linking in the hash table (type: XML_ENTITY_DECL).
class xmlEntity is anyNode is export {
    BEGIN @ClassMap[XML_ENTITY_DECL] = $?CLASS;
    has xmlCharP       $.orig; # content without ref substitution */
    has xmlCharP    $.content; # content or ndata if unparsed */
    has int32        $.length; # the content length */
    has int32         $.etype; # The entity type */
    
    has xmlCharP $.ExternalID; # External identifier for PUBLIC */
    has xmlCharP   $.SystemID; # URI for a SYSTEM or PUBLIC Entity */

    has xmlEntity     $.nexte; # unused */
    has xmlCharP        $.URI; # the full URI as computed */
    has int32         $.owner; # does the entity own the childrens */
    #
    # Todo: libxml2 2.10.0+ has made structural changes below this point
    # Remaining structure is version dependant
    has int32       $!checked; # was the entity content checked */
                               # this is also used to count entities
                               # references done from that entity
                               # and if it contains '<' */
    our sub GetPredefined(xmlCharP $name --> xmlEntity) is native($XML2) is symbol('xmlGetPredefinedEntity') { * }
    our sub Create(xmlCharP $name, int32 $type, xmlCharP $ext-id, xmlCharP $int-id, xmlCharP $value --> xmlEntity) is native($BIND-XML2) is symbol('xml6_entity_create') {*}
    method get-predefined(Str :$name!) {
        GetPredefined($name);
    }
    method Reference { nextsame unless self.etype == +XML_INTERNAL_PREDEFINED_ENTITY}
    method Unreference { nextsame unless self.etype == +XML_INTERNAL_PREDEFINED_ENTITY}
    method create(Str:D :$name!, Str:D :$content!, Int :$type = XML_INTERNAL_GENERAL_ENTITY, Str :$external-id, Str :$internal-id) {
        Create($name, $type, $external-id, $internal-id, $content );
    }
}

#| An XML Element declaration from a DTD (type: XML_ELEMENT_DECL).
class xmlElementDecl is anyNode is export {
    BEGIN @ClassMap[XML_ELEMENT_DECL] = $?CLASS;
    has int32                $.etype; # The type */
    has xmlElementContent  $.content; # the allowed element content */
    has xmlAttrDecl     $.attributes; # List of the declared attributes */
    has xmlCharP            $.prefix; # the namespace prefix if any */
    has xmlRegexp        $.contModel; # the validating regexp */
}

# itemNodes are xmlNodeSet members; which can be either anyNode or xmlNs objects.
# These have distinct structs, but have the second field, 'type' in common
class itemNode is export {
    has Pointer $!pad; # first field depends on type
    has int32 $.type;
    # + other fields, which also depend on type

    method delegate {
        my $class := @ClassMap[$!type];
        nativecast($class, self);
    }
    method cast(Pointer:D $p) {
        my $type := nativecast(itemNode, $p).type;
        my $class := @ClassMap[$type];
        nativecast($class, $p);
    }
    our sub NodeType(Str --> int32) is native($BIND-XML2) is symbol('domNodeType') {*}
}

#| A node-set (an unordered collection of nodes without duplicates)
class xmlNodeSet is export {
    has int32             $.nodeNr; # number of nodes in the set
    has int32            $.nodeMax; # size of the array as allocated
    has CArray[itemNode] $.nodeTab; # array of nodes in no particular order

    our sub NewFromNode(anyNode --> xmlNodeSet) is export is native($XML2) is symbol('xmlXPathNodeSetCreate') {*}
    our sub NewFromList(itemNode, int32 --> xmlNodeSet) is native($BIND-XML2) is symbol('domCreateNodeSetFromList') {*}
    method !hash(int32 $deref --> xmlHashTable) is native($BIND-XML2) is symbol('xml6_hash_xpath_nodeset') {*}
    method Hash(:$deref) { self!hash(+$deref.so) }
    method Reference is native($BIND-XML2) is symbol('domReferenceNodeSet') {*}
    method Unreference is native($BIND-XML2) is symbol('domUnreferenceNodeSet') {*}
    method Free is native($XML2) is symbol('xmlXPathFreeNodeSet') {*}
    method delete(itemNode --> int32) is symbol('domDeleteNodeSetItem') is native($BIND-XML2) {*}
    method copy(--> xmlNodeSet) is symbol('domCopyNodeSet') is native($BIND-XML2) {*}
    method reverse(--> xmlNodeSet) is symbol('domReverseNodeSet') is native($BIND-XML2) {*}
    method push(itemNode, int32) is symbol('domPushNodeSet') is native($BIND-XML2) {*}
    method pop(--> itemNode) is symbol('domPopNodeSet') is native($BIND-XML2) {*}
    method hasSameNodes(xmlNodeSet --> int32) is symbol('xmlXPathHasSameNodes') is native($XML2) {*}
    method AT-POS(int32 --> itemNode) is symbol('domNodeSetAtPos') is native($BIND-XML2) {*}

    proto method new(|) {*}
    multi method new(itemNode:D :$node, :list($)! where .so, Bool :$keep-blanks = True) {
        NewFromList($node, +$keep-blanks);
    }
    multi method new(anyNode :$node) {
        NewFromNode($node);
    }
}

class xmlParserNodeInfoSeq is repr('CStruct') is export {
    has ulong                  $.maximum;
    has ulong                  $.length;
    has xmlParserNodeInfo      $.buffer;
}

#| An xmlValidCtxt is used for error reporting when validating.
class xmlValidCtxt is repr('CStruct') is export {
    has Pointer                $.userData;  # user specific data block
    has Pointer                $.error;     # the callback in case of errors
    has Pointer                $.warning;   # the callback in case of warnings

    # Node analysis stack used when validating within entities
    has xmlNode                $.node;      # Current parsed Node
    has int32                  $.nodeNr;    # Depth of the parsing stack
    has int32                  $.nodeMax;   # Max depth of the parsing stack
    has Pointer[xmlNode]       $.nodeTab;   # array of nodes

    has uint32                 $.finishDtd; # finished validating the Dtd ?
    has xmlDoc                 $.doc;       # the document
    has int32                  $.valid;     # temporary validity check result

    # state state used for non-determinist content validation
    has xmlValidState          $.vstate;    # current state
    has int32                  $.vstateNr;  # Depth of the validation stack
    has int32                  $.vstateMax; # Max depth of the validation stack
    has Pointer[xmlValidState] $.vstateTab; # array of validation state

    # Regex preocessing
    has xmlAutomata            $.am;        # the automata
    has xmlAutomataState       $.state;     # used to build the automata

    our sub New(--> xmlValidCtxt) is native($XML2) is symbol('xmlNewValidCtxt') {*}
    method ValidateDtd(xmlDoc, xmlDtd --> int32) is native($XML2) is symbol('xmlValidateDtd') {*}
    method ValidateDocument(xmlDoc --> int32) is native($XML2) is symbol('xmlValidateDocument') {*}
    method ValidateElement(xmlDoc, xmlElem --> int32) is native($XML2) is symbol('xmlValidateElement') {*}
    method ValidateOneAttribute(xmlDoc, xmlElem, xmlAttr, xmlCharP --> int32) is native($XML2) is symbol('xmlValidateOneAttribute') {*}
    method SetStructuredErrorFunc( &error-func (xmlValidCtxt $, xmlError $)) is native($XML2) is symbol('xmlSetStructuredErrorFunc') {*};
    method Free is symbol('xmlFreeValidCtxt') is native($XML2) {*}
    method new { New() }
    multi method validate(xmlDoc:D :$doc!, xmlDtd:D :$dtd!) {
        self.ValidateDtd($doc, $dtd);
    }
    multi method validate(xmlDoc:D :$doc!, xmlElem:D :$elem, xmlAttr:D :$attr) {
        self.ValidateOneAttribute($doc, $elem, $attr, $attr.domGetNodeValue.str);
    }
    multi method validate(xmlDoc:D :$doc!, xmlElem:D :$elem) {
        self.ValidateElement($doc, $elem);
    }
    multi method validate(xmlDoc:D :$doc!) {
        self.ValidateDocument($doc);
    }
}

#| The parser context.
class xmlParserCtxt is export {
    has xmlSAXHandler          $.sax           # The SAX handler
        is rw-ptr(method xml6_parser_ctx_set_sax( xmlSAXHandler ) is native($BIND-XML2) {*} );
    has Pointer                $.userData;     # For SAX interface only, used by DOM build
    has xmlDoc                 $.myDoc         # the document being built
       is rw-ptr(method set-myDoc( xmlDoc ) is symbol('xml6_parser_ctx_set_myDoc') is native($BIND-XML2) {*} );
    has int32                  $.wellFormed;   # is the document well formed
    has int32                  $.replaceEntities;     # shall we replace entities ?
    has xmlCharP               $.version;      #  the XML version string
    has xmlCharP               $.encoding;     # the declared encoding, if any
    has int32                  $.standalone;   # standalone document
    has int32                  $.html;         # an HTML(1)/Docbook(2) document
                                               # 3 is HTML after <head>
                                               # 10 is HTML after <body>
    # Input stream stack
    has xmlParserInput         $.input;        # Current input stream
    has int32                  $.inputNr;      # Number of current input streams
    has int32                  $.inputMax;     # Max number of input streams
    has Pointer[xmlParserInput]       $.inputTab;     # stack of inputs

    # Node analysis stack only used for DOM building
    has xmlNode                $.node;         # Current parsed Node
    has int32                  $.nodeNr;       # Depth of the parsing stack
    has int32                  $.nodeMax;      # Max depth of the parsing stack
    has Pointer[xmlNode]       $.nodeTab;      # array of nodes
    
    has int32                  $.record_info;  # Whether node info should be kept
    HAS xmlParserNodeInfoSeq   $.node_seq;     # info about each node parsed

    has int32                  $.errNo;        # error code

    has int32                  $.hasExternalSubset;     # reference and external subset
    has int32                  $.hasPErefs;    # the internal subset has PE refs
    has int32                  $.external;     # are we parsing an external entity
    has int32                  $.valid;        # is the document valid
    has int32                  $.validate;     # shall we try to validate ?
    HAS xmlValidCtxt           $.vctxt;        #  The validity context

    has int32                  $.instate;      # current type of input
    has int32                  $.token;        # next char look-ahead

    has Str                    $.directory;    # the data directory

    # Node name stack
    has xmlCharP               $.name;         # Current parsed Node
    has int32                  $.nameNr;       # Depth of the parsing stack
    has int32                  $.nameMax;      # Max depth of the parsing stack
    has Pointer[xmlCharP]      $.nameTab;      # array of nodes

    has long                   $.nbChars;      # number of xmlChar processed
    has long                   $.checkIndex;   # used by progressive parsing lookup
    has int32                  $.keepBlanks;   # ugly but ...
    has int32                  $.disableSAX;   #  SAX callbacks are disabled
    has int32                  $.inSubset;     #  Parsing is in int 1/ext 2 subset
    has xmlCharP               $.intSubName;   # name of subset
    has xmlCharP               $.extSubURI;    # URI of external subset
    has xmlCharP               $.extSubSystem; # SYSTEM ID of external subset

    # xml:space values
    has Pointer[int32]         $.space;        # Should the parser preserve spaces
    has int32                  $.spaceNr;      # Depth of the parsing stack
    has int32                  $.spaceMax;     # Max depth of the parsing stack
    has Pointer[int32]         $.spaceTab;     # array of space infos

    has int32                  $.depth;        # to prevent entity substitution loops
    has xmlParserInput         $.entity;       # used to check entities boundaries
    has int32                  $.charset;      # encoding of the in-memory content
                                               # actually an xmlEncodingStr
    has int32                  $.nodelen;      # Those two fields are there to
    has int32                  $.nodemem;      # Speed up large node parsing
    has int32                  $.pedantic;     # signal pedantic warnings
    has Pointer                $._private;     # For user data, libxml won't touch it

    has int32                  $.loadsubset;   # should the external subset be loaded
    has int32                  $.linenumbers is rw;     # set line number in element content
    has Pointer                $.catalogs;     # document's own catalog
    has int32                  $.recovery;     # run in recovery mode
    has int32                  $.progressive;  # is this a progressive parsing
    has xmlDict                $.dict;         # dictionary for the parser 
    has Pointer[xmlCharP]      $.atts;         # array for the attributes callbacks
    has int32                  $.maxatts;      # the size of the array
    has int32                  $.docdict;      # use strings from dict to build tree

    # pre-interned strings
    has xmlCharP               $.str_xml;
    has xmlCharP               $.str_xmlns;
    has xmlCharP               $.str_xml_ns;

    # Everything below is used only by the new SAX mode
    has int32                  $.sax2;         # operating in the new SAX mode
    has int32                  $.nsNr;         # the number of inherited namespaces
    has int32                  $.nsMax;        # the size of the arrays
    has Pointer[xmlCharP]      $.nsTab;        # the array of prefix/namespace name
    has Pointer[int32]         $.attallocs;    #  which attribute were allocated
    has Pointer[Pointer]       $.pushTab;      # array of data for push
    has xmlHashTable           $.attsDefault;  # defaulted attributes if any
    has xmlHashTable           $.attsSpecial;  # non-CDATA attributes if any
    has int32                  $.nsWellFormed; # is the document XML Namespace okay
    has int32                  $.options;      # Extra options

    # These fields are needed only for streaming parsing so far
    has int32                  $.dictNames;    # Use dictionary names for the tree
    has int32                  $.freeElemsNr;  # number of freed element nodes
    has xmlNode                $.freeElems;    # List of freed element nodes
    has int32                  $.freeAttrsNr;  # number of freed attributes nodes
    has xmlAttr                $.freeAttrs;    # List of freed attributes nodes

    # the complete error information for the last error.
    HAS xmlError               $.lastError;
    has int32                  $.parseMode;    # the parser mode
    has ulong                  $.nbentities;   # number of entities references
    has ulong                  $.sizeentities; # size of parsed entities

    # for use by HTML non-recursive parser
    has xmlParserNodeInfo      $.nodeInfo;     # Current NodeInfo
    has int32                  $.nodeInfoNr;   # Depth of the parsing stack
    has int32                  $.nodeInfoMax;  # Max depth of the parsing stack
    has xmlParserNodeInfo      $.nodeInfoTab;  # array of nodeInfos

    has int32                  $.input_id;     # we need to label inputs
    has ulong                  $.sizeentcopy;  # volume of entity copy

    our sub New(--> xmlParserCtxt) is native($XML2) is symbol('xmlNewParserCtxt') {*};
    method new() {
        New()
    }
    method ReadDoc(Str $xml, Str $uri, xmlEncodingStr $enc, int32 $flags --> xmlDoc) is native($XML2) is symbol('xmlCtxtReadDoc') {*};
    method ReadFile(Str $xml, xmlEncodingStr $enc, int32 $flags --> xmlDoc) is native($XML2) is symbol('xmlCtxtReadFile') {*};
    method ReadFd(int32 $fd, xmlCharP $uri, xmlEncodingStr $enc, int32 $flags --> xmlDoc) is native($XML2) is symbol('xmlCtxtReadFd') {*};
    method UseOptions(int32 --> int32) is native($XML2) is symbol('xmlCtxtUseOptions') { * }
    method NewInputStream(xmlParserInputBuffer, int32 $enc --> xmlParserInput) is native($XML2) is symbol('xmlNewIOInputStream') is export {*}
    method NewInputFile(Str --> xmlParserInput) is native($XML2) is export is symbol('xmlNewInputFromFile') {*}
    method LoadDtd(xmlCharP $ext-id, xmlCharP $sys-id --> xmlParserInput) is native($BIND-XML2) is symbol('xml6_parser_ctx_load_dtd') {*}
    #++ available since libxml2 v2.14.0
    method ParseDtd(xmlParserInput, xmlCharP $ext-id, xmlCharP $int-id --> xmlDtd) is native($XML2) is export is symbol('xmlCtxtParseDtd') {*}
    method ParseContent(xmlParserInput, xmlNode, int32 $has-text-decl --> xmlNode) is native($XML2) is export is symbol('xmlCtxtParseContent') {*}
    #-- available since libxml2 v2.14.0
    # deprecated
    method SetStructuredErrorFunc( &error-func (xmlParserCtxt $, xmlError $)) is native($XML2) is symbol('xmlSetStructuredErrorFunc') {*};
    # recommended libxml 2.13.0+
    method SetErrorHandler(&error-func (xmlParserCtxt $, xmlError $)) is native($XML2) is symbol('xmlCtxtSetErrorHandler') {*};
    method GetLastError(--> xmlError) is native($XML2) is symbol('xmlCtxtGetLastError') is native($XML2) {*}
    method Close(--> int32) is native($BIND-XML2) is symbol('xml6_parser_ctx_close') {*}
    method ParserError(Str $msg) is native($XML2) is symbol('xmlParserError') {*}
    method StopParser is native($XML2) is symbol('xmlStopParser') { * }
    method Reference is native($BIND-XML2) is symbol('xml6_parser_ctx_add_reference') {*}
    method remove-reference(--> int32) is native($BIND-XML2) is symbol('xml6_parser_ctx_remove_reference') {*}
    method Free is native($XML2) is symbol('xmlFreeParserCtxt') { * }
    method Unreference {
        with self {
            .Free if .remove-reference;
        }
    }

    # SAX2 Handler callbacks
    #-- Document Properties --#
    method xmlSAX2GetPublicId(--> Str) is native($XML2) {*};
    method xmlSAX2GetSystemId(--> Str) is native($XML2) {*};
    method xmlSAX2SetDocumentLocator(xmlSAXLocator $loc) is native($XML2) {*};
    method xmlSAX2GetLineNumber(--> int32) is native($XML2) {*};
    method xmlSAX2GetColumnNumber(--> int32) is native($XML2) {*};
    method xmlSAX2IsStandalone(--> int32) is native($XML2) {*};
    method xmlSAX2HasInternalSubset(--> int32) is native($XML2) {*};
    method xmlSAX2HasExternalSubset(--> int32) is native($XML2) {*};
    method xmlSAX2InternalSubset(Str $name , Str $ext-id, Str $int-id--> int32) is native($XML2) {*};
    method xmlSAX2ExternalSubset(Str $name , Str $ext-id, Str $int-id--> int32) is native($XML2) {*};
    method xmlParserWarning(Str $msg ) is native($XML2) {*};
    method xmlParserError(Str $msg ) is native($XML2) {*};

    #-- Entities --#
    method xmlSAX2GetEntity(Str $name --> xmlEntity) is native($XML2) {*};
    method xmlSAX2GetParameterEntity(Str $name --> xmlEntity) is native($XML2) {*};
    method xmlSAX2ResolveEntity(Str $public-id, Str $system-id --> xmlParserInput) is native($XML2) {*};

    #-- Declarations --#
    method xmlSAX2EntityDecl(Str $name, int32 $type, Str $public-id, Str $system-id, Str $content --> xmlParserInput) is native($XML2) {*};
    method xmlSAX2AttributeDecl(Str $elem, Str $fullname, int32 $type, int32 $def, Str $default-value, xmlEnumeration $tree) is native($XML2) {*};
    method xmlSAX2ElementDecl(Str $name, int32 $type, xmlElementContent $content) is native($XML2) {*};
    method xmlSAX2NotationDecl(Str $name, Str $public-id, Str $system-id) is native($XML2) {*};
    method xmlSAX2UnparsedEntityDecl(Str $name, Str $public-id, Str $system-id, Str $notation-name) is native($XML2) {*};

    #-- Content --#
    method xmlSAX2StartDocument() is native($XML2) {*};
    method xmlSAX2EndDocument() is native($XML2) {*};
    method xmlSAX2StartElement(Str $name, CArray $atts) is native($XML2) {*};
    method xmlSAX2EndElement(Str $name) is native($XML2) {*};
    method xmlSAX2StartElementNs(Str $local-name, Str $prefix, Str $uri, int32 $num-namespaces, CArray[Str] $namespaces, int32 $num-attributes, int32 $num-defaulted, CArray[Str] $attributes) is native($XML2) {*};
    method xmlSAX2EndElementNs(Str $local-name, Str $prefix, Str $uri) is native($XML2) {*};
    method xmlSAX2Reference(Str $name) is native($XML2) {*};
    method xmlSAX2Characters(Blob $chars, int32 $len) is native($XML2) {*};
    method xmlSAX2IgnorableWhitespace(Blob $chars, int32 $len) is native($XML2) {*};
    method xmlSAX2ProcessingInstruction(Str $target, Str $data) is native($XML2) {*};
    method xmlSAX2Comment(Str $value) is native($XML2) {*};
    method xmlSAX2CDataBlock(Blob $chars, int32 $len) is native($XML2) {*};
}

#| XML file parser context
class xmlFileParserCtxt is xmlParserCtxt is repr('CStruct') is export {

    our sub New(Str $file --> xmlFileParserCtxt) is native($XML2) is symbol('xmlCreateFileParserCtxt') {*};
    method ParseDocument(--> int32) is native($XML2) is symbol('xmlParseDocument') {*}
    method new(Str() :$file!) { New($file) }
}

#| an incremental XML push parser context. Determines encoding and reads data in binary chunks
class xmlPushParserCtxt is xmlParserCtxt is repr('CStruct') is export {

    our sub New(xmlSAXHandler $sax-handler, Pointer $user-data, Blob $chunk, int32 $size, Str $path --> xmlPushParserCtxt) is native($XML2) is symbol('xmlCreatePushParserCtxt') {*};
    method new(Blob :$chunk!, :$size = +$chunk, xmlSAXHandler :$sax-handler, Pointer :$user-data, Str :$path) {
        New($sax-handler, $user-data, $chunk, $size, $path);
    }
    method ParseChunk(Blob $chunk, int32 $size, int32 $terminate --> int32) is native($XML2) is symbol('xmlParseChunk') {*};
};

#| a vanilla HTML parser context - can be used to read files or strings
class htmlParserCtxt is xmlParserCtxt is repr('CStruct') is export {

    method myDoc is rw { nativecast(htmlDoc, callsame) }
    method UseOptions(int32 --> int32) is native($XML2) is symbol('htmlCtxtUseOptions') { * }

    our sub New(--> htmlParserCtxt) is native($XML2) is symbol('htmlNewParserCtxt') {*};
    method new {New() }
    method ReadDoc(Str $xml, Str $uri, xmlEncodingStr $enc, int32 $flags --> htmlDoc) is native($XML2) is symbol('htmlCtxtReadDoc') {*};
    method ReadFile(Str $xml, Str $uri, xmlEncodingStr $enc, int32 $flags --> htmlDoc) is native($XML2) is symbol('htmlCtxtReadFile') {*}
    method ReadFd(int32 $fd, xmlCharP $uri, xmlEncodingStr $enc, int32 $flags --> htmlDoc) is native($XML2) is symbol('htmlCtxtReadFd') {*};
};

#| HTML file parser context
class htmlFileParserCtxt is htmlParserCtxt is repr('CStruct') is export {

    our sub New(Str $file, xmlEncodingStr $enc --> htmlFileParserCtxt) is native($XML2) is symbol('htmlCreateFileParserCtxt') {*};
    method ParseDocument(--> int32) is native($XML2) is symbol('htmlParseDocument') {*}
    method new(Str() :$file!, xmlEncodingStr :$enc) { New($file, $enc) }
}

#| an incremental HTMLpush parser context. Determines encoding and reads data in binary chunks
class htmlPushParserCtxt is htmlParserCtxt is repr('CStruct') is export {

    our sub New(xmlSAXHandler $sax-handler, Pointer $user-data, Blob $chunk, int32 $size, Str $path, int32 $encoding --> htmlPushParserCtxt) is native($XML2) is symbol('htmlCreatePushParserCtxt') {*};
    method new(Blob :$chunk!, :$size = +$chunk, xmlSAXHandler :$sax-handler, Pointer :$user-data, Str :$path, xmlEncodingStr :$enc) {
        my UInt $encoding = do with $enc { xmlParseCharEncoding($_) } else { 0 };
        New($sax-handler, $user-data, $chunk, $size, $path, $encoding);
    }
    method ParseChunk(Blob $chunk, int32 $size, int32 $terminate --> int32) is native($XML2) is symbol('htmlParseChunk') { *};
};

#| a parser context for an XML in-memory document.
class xmlMemoryParserCtxt is xmlParserCtxt is repr('CStruct') is export {
    our sub New(Blob $buf, int32 $len --> xmlMemoryParserCtxt) is native($XML2) is symbol('xmlCreateMemoryParserCtxt') {*}
    method ParseDocument(--> int32) is native($XML2) is symbol('xmlParseDocument') {*}
    multi method new( Str() :$string! ) {
        my Blob $buf = ($string || ' ').encode;
        self.new: :$buf;
    }
    multi method new( Blob() :$buf!, UInt :$bytes = $buf.bytes --> xmlMemoryParserCtxt:D) {
         New($buf, $bytes);
    }
}

class htmlMemoryParserCtxt is htmlParserCtxt is repr('CStruct') is export {
    sub NewBuf(Blob:D, int32, xmlEncodingStr --> htmlMemoryParserCtxt) is native($BIND-XML2) is symbol('xml6_parser_ctx_html_create_buf') {*}
    sub NewStr(xmlCharP:D, xmlEncodingStr --> htmlMemoryParserCtxt) is native($BIND-XML2) is symbol('xml6_parser_ctx_html_create_str') {*}
    method ParseDocument(--> int32) is native($XML2) is symbol('htmlParseDocument') {*}
    multi method new( Blob() :$buf!, xmlEncodingStr :$enc = 'UTF-8') {
        NewBuf($buf, $buf.bytes, $enc);
    }
    multi method new( Str() :$string! ) {
        NewStr($string, 'UTF-8');
    }
}

multi method GetLastError(xmlParserCtxt:D $ctx) { $ctx.GetLastError() // $.GetLastError()  }
multi method GetLastError { xmlError::Last()  }

## Input callbacks

module xmlInputCallbacks is export {
    our sub Pop(--> int32) is native($XML2) is symbol('xmlPopInputCallbacks') {*}
    our sub Register(
        &match (Str --> int32),
        &open (Str --> Pointer),
        &read (Pointer, CArray[uint8], int32 --> int32