"""
Simple implementation of the Level 1 DOM.

Namespaces and other minor Level 2 features are also supported.

parse("foo.xml")

parseString("<foo><bar/></foo>")

Todo:
=====
 * convenience methods for getting elements and text.
 * more testing
 * bring some of the writer and linearizer code into conformance with this
        interface
 * SAX 2 namespaces
"""

import sys
import xml.dom
from _typeshed import Incomplete, ReadableBuffer, SupportsRead, SupportsWrite
from typing import Literal, NoReturn, TypeVar, overload
from typing_extensions import Self
from xml.dom.minicompat import NodeList
from xml.dom.xmlbuilder import DocumentLS, DOMImplementationLS
from xml.sax.xmlreader import XMLReader

_N = TypeVar("_N", bound=Node)

def parse(
    file: str | SupportsRead[ReadableBuffer | str], parser: XMLReader | None = None, bufsize: int | None = None
) -> Document:
    """Parse a file into a DOM by filename or file object."""
    ...
def parseString(string: str | ReadableBuffer, parser: XMLReader | None = None) -> Document:
    """Parse a file into a DOM from a string."""
    ...
def getDOMImplementation(features=None) -> DOMImplementation | None: ...

class Node(xml.dom.Node):
    namespaceURI: str | None
    parentNode: Incomplete
    ownerDocument: Incomplete
    nextSibling: Incomplete
    previousSibling: Incomplete
    prefix: Incomplete
    @property
    def firstChild(self) -> Node | None:
        """First child node, or None."""
        ...
    @property
    def lastChild(self) -> Node | None:
        """Last child node, or None."""
        ...
    @property
    def localName(self) -> str | None:
        """Namespace-local name of this node."""
        ...
    def __bool__(self) -> Literal[True]: ...
    if sys.version_info >= (3, 9):
        @overload
        def toxml(self, encoding: str, standalone: bool | None = None) -> bytes: ...
        @overload
        def toxml(self, encoding: None = None, standalone: bool | None = None) -> str: ...
        @overload
        def toprettyxml(
            self,
            indent: str = "\t",
            newl: str = "\n",
            # Handle any case where encoding is not provided or where it is passed with None
            encoding: None = None,
            standalone: bool | None = None,
        ) -> str: ...
        @overload
        def toprettyxml(
            self,
            indent: str,
            newl: str,
            # Handle cases where encoding is passed as str *positionally*
            encoding: str,
            standalone: bool | None = None,
        ) -> bytes: ...
        @overload
        def toprettyxml(
            self,
            indent: str = "\t",
            newl: str = "\n",
            # Handle all cases where encoding is passed as a keyword argument; because standalone
            # comes after, it will also have to be a keyword arg if encoding is
            *,
            encoding: str,
            standalone: bool | None = None,
        ) -> bytes: ...
    else:
        @overload
        def toxml(self, encoding: str) -> bytes: ...
        @overload
        def toxml(self, encoding: None = None) -> str: ...
        @overload
        def toprettyxml(
            self,
            indent: str = "\t",
            newl: str = "\n",
            # Handle any case where encoding is not provided or where it is passed with None
            encoding: None = None,
        ) -> str: ...
        @overload
        def toprettyxml(
            self,
            indent: str,
            newl: str,
            # Handle cases where encoding is passed as str *positionally*
            encoding: str,
        ) -> bytes: ...
        @overload
        def toprettyxml(
            self,
            indent: str = "\t",
            newl: str = "\n",
            # Handle all cases where encoding is passed as a keyword argument
            *,
            encoding: str,
        ) -> bytes: ...

    def hasChildNodes(self) -> bool: ...
    def insertBefore(self, newChild, refChild): ...
    def appendChild(self, node: _N) -> _N: ...
    def replaceChild(self, newChild, oldChild): ...
    def removeChild(self, oldChild): ...
    def normalize(self) -> None: ...
    def cloneNode(self, deep): ...
    def isSupported(self, feature, version): ...
    def isSameNode(self, other): ...
    def getInterface(self, feature): ...
    def getUserData(self, key): ...
    def setUserData(self, key, data, handler): ...
    childNodes: Incomplete
    def unlink(self) -> None: ...
    def __enter__(self) -> Self: ...
    def __exit__(self, et, ev, tb) -> None: ...

class DocumentFragment(Node):
    nodeType: int
    nodeName: str
    nodeValue: Incomplete
    attributes: Incomplete
    parentNode: Incomplete
    childNodes: Incomplete
    def __init__(self) -> None: ...

class Attr(Node):
    name: str
    nodeType: int
    attributes: Incomplete
    specified: bool
    ownerElement: Incomplete
    namespaceURI: str | None
    childNodes: Incomplete
    nodeName: Incomplete
    nodeValue: str
    value: str
    prefix: Incomplete
    def __init__(
        self, qName: str, namespaceURI: str | None = None, localName: str | None = None, prefix: Incomplete | None = None
    ) -> None: ...
    def unlink(self) -> None: ...
    @property
    def isId(self) -> bool:
        """True if this attribute is an ID."""
        ...
    @property
    def schemaType(self):
        """Schema type for this attribute."""
        ...

class NamedNodeMap:
    """
    The attribute list is a transient interface to the underlying
    dictionaries.  Mutations here will change the underlying element's
    dictionary.

    Ordering is imposed artificially and does not reflect the order of
    attributes as found in an input document.
    """
    def __init__(self, attrs, attrsNS, ownerElement) -> None: ...
    def item(self, index): ...
    def items(self): ...
    def itemsNS(self): ...
    def __contains__(self, key): ...
    def keys(self): ...
    def keysNS(self): ...
    def values(self): ...
    def get(self, name: str, value: Incomplete | None = None): ...
    def __len__(self) -> int: ...
    def __eq__(self, other: object) -> bool: ...
    def __ge__(self, other: NamedNodeMap) -> bool: ...
    def __gt__(self, other: NamedNodeMap) -> bool: ...
    def __le__(self, other: NamedNodeMap) -> bool: ...
    def __lt__(self, other: NamedNodeMap) -> bool: ...
    def __getitem__(self, attname_or_tuple: tuple[str, str | None] | str): ...
    def __setitem__(self, attname: str, value: Attr | str) -> None: ...
    def getNamedItem(self, name: str) -> Attr | None: ...
    def getNamedItemNS(self, namespaceURI: str, localName: str | None) -> Attr | None: ...
    def removeNamedItem(self, name: str) -> Attr: ...
    def removeNamedItemNS(self, namespaceURI: str, localName: str | None): ...
    def setNamedItem(self, node: Attr) -> Attr: ...
    def setNamedItemNS(self, node: Attr) -> Attr: ...
    def __delitem__(self, attname_or_tuple: tuple[str, str | None] | str) -> None: ...
    @property
    def length(self) -> int:
        """Number of nodes in the NamedNodeMap."""
        ...

AttributeList = NamedNodeMap

class TypeInfo:
    namespace: Incomplete | None
    name: str
    def __init__(self, namespace: Incomplete | None, name: str) -> None: ...

class Element(Node):
    nodeType: int
    nodeValue: Incomplete
    schemaType: Incomplete
    parentNode: Incomplete
    tagName: str
    nodeName: str
    prefix: Incomplete
    namespaceURI: str | None
    childNodes: Incomplete
    nextSibling: Incomplete
    def __init__(
        self, tagName, namespaceURI: str | None = None, prefix: Incomplete | None = None, localName: Incomplete | None = None
    ) -> None: ...
    def unlink(self) -> None: ...
    def getAttribute(self, attname: str) -> str:
        """
        Returns the value of the specified attribute.

        Returns the value of the element's attribute named attname as
        a string. An empty string is returned if the element does not
        have such an attribute. Note that an empty string may also be
        returned as an explicitly given attribute value, use the
        hasAttribute method to distinguish these two cases.
        """
        ...
    def getAttributeNS(self, namespaceURI: str, localName): ...
    def setAttribute(self, attname: str, value: str) -> None: ...
    def setAttributeNS(self, namespaceURI: str, qualifiedName: str, value) -> None: ...
    def getAttributeNode(self, attrname: str): ...
    def getAttributeNodeNS(self, namespaceURI: str, localName): ...
    def setAttributeNode(self, attr): ...
    setAttributeNodeNS: Incomplete
    def removeAttribute(self, name: str) -> None: ...
    def removeAttributeNS(self, namespaceURI: str, localName) -> None: ...
    def removeAttributeNode(self, node): ...
    removeAttributeNodeNS: Incomplete
    def hasAttribute(self, name: str) -> bool:
        """
        Checks whether the element has an attribute with the specified name.

        Returns True if the element has an attribute with the specified name.
        Otherwise, returns False.
        """
        ...
    def hasAttributeNS(self, namespaceURI: str, localName) -> bool: ...
    def getElementsByTagName(self, name: str) -> NodeList[Element]:
        """
        Returns all descendant elements with the given tag name.

        Returns the list of all descendant elements (not direct children
        only) with the specified tag name.
        """
        ...
    def getElementsByTagNameNS(self, namespaceURI: str, localName: str) -> NodeList[Element]: ...
    def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None:
        """
        Write an XML element to a file-like object

        Write the element to the writer object that must provide
        a write method (e.g. a file or StringIO object).
        """
        ...
    def hasAttributes(self) -> bool: ...
    def setIdAttribute(self, name) -> None: ...
    def setIdAttributeNS(self, namespaceURI: str, localName) -> None: ...
    def setIdAttributeNode(self, idAttr) -> None: ...
    @property
    def attributes(self) -> NamedNodeMap:
        """NamedNodeMap of attributes on the element."""
        ...

class Childless:
    """
    Mixin that makes childless-ness easy to implement and avoids
    the complexity of the Node methods that deal with children.
    """
    attributes: Incomplete
    childNodes: Incomplete
    firstChild: Incomplete
    lastChild: Incomplete
    def appendChild(self, node) -> NoReturn: ...
    def hasChildNodes(self) -> bool: ...
    def insertBefore(self, newChild, refChild) -> NoReturn: ...
    def removeChild(self, oldChild) -> NoReturn: ...
    def normalize(self) -> None: ...
    def replaceChild(self, newChild, oldChild) -> NoReturn: ...

class ProcessingInstruction(Childless, Node):
    nodeType: int
    target: Incomplete
    data: Incomplete
    def __init__(self, target, data) -> None: ...
    nodeValue: Incomplete
    nodeName: Incomplete
    def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ...

class CharacterData(Childless, Node):
    ownerDocument: Incomplete
    previousSibling: Incomplete
    def __init__(self) -> None: ...
    def __len__(self) -> int: ...
    data: str
    nodeValue: Incomplete
    def substringData(self, offset: int, count: int) -> str: ...
    def appendData(self, arg: str) -> None: ...
    def insertData(self, offset: int, arg: str) -> None: ...
    def deleteData(self, offset: int, count: int) -> None: ...
    def replaceData(self, offset: int, count: int, arg: str) -> None: ...
    @property
    def length(self) -> int:
        """Length of the string data."""
        ...

class Text(CharacterData):
    nodeType: int
    nodeName: str
    attributes: Incomplete
    data: Incomplete
    def splitText(self, offset: int) -> Self: ...
    def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ...
    def replaceWholeText(self, content) -> Self | None: ...
    @property
    def isWhitespaceInElementContent(self) -> bool:
        """True iff this text node contains only whitespace and is in element content."""
        ...
    @property
    def wholeText(self) -> str:
        """The text of all logically-adjacent text nodes."""
        ...

class Comment(CharacterData):
    nodeType: int
    nodeName: str
    def __init__(self, data) -> None: ...
    def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ...

class CDATASection(Text):
    nodeType: int
    nodeName: str
    def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ...

class ReadOnlySequentialNamedNodeMap:
    def __init__(self, seq=()) -> None: ...
    def __len__(self) -> int: ...
    def getNamedItem(self, name): ...
    def getNamedItemNS(self, namespaceURI: str, localName): ...
    def __getitem__(self, name_or_tuple): ...
    def item(self, index): ...
    def removeNamedItem(self, name) -> None: ...
    def removeNamedItemNS(self, namespaceURI: str, localName) -> None: ...
    def setNamedItem(self, node) -> None: ...
    def setNamedItemNS(self, node) -> None: ...
    @property
    def length(self) -> int:
        """Number of entries in the NamedNodeMap."""
        ...

class Identified:
    """Mix-in class that supports the publicId and systemId attributes."""
    publicId: Incomplete
    systemId: Incomplete

class DocumentType(Identified, Childless, Node):
    nodeType: int
    nodeValue: Incomplete
    name: Incomplete
    internalSubset: Incomplete
    entities: Incomplete
    notations: Incomplete
    nodeName: Incomplete
    def __init__(self, qualifiedName: str) -> None: ...
    def cloneNode(self, deep): ...
    def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ...

class Entity(Identified, Node):
    attributes: Incomplete
    nodeType: int
    nodeValue: Incomplete
    actualEncoding: Incomplete
    encoding: Incomplete
    version: Incomplete
    nodeName: Incomplete
    notationName: Incomplete
    childNodes: Incomplete
    def __init__(self, name, publicId, systemId, notation) -> None: ...
    def appendChild(self, newChild) -> NoReturn: ...
    def insertBefore(self, newChild, refChild) -> NoReturn: ...
    def removeChild(self, oldChild) -> NoReturn: ...
    def replaceChild(self, newChild, oldChild) -> NoReturn: ...

class Notation(Identified, Childless, Node):
    nodeType: int
    nodeValue: Incomplete
    nodeName: Incomplete
    def __init__(self, name, publicId, systemId) -> None: ...

class DOMImplementation(DOMImplementationLS):
    def hasFeature(self, feature: str, version: str | None) -> bool: ...
    def createDocument(self, namespaceURI: str | None, qualifiedName: str | None, doctype: DocumentType | None) -> Document: ...
    def createDocumentType(self, qualifiedName: str | None, publicId: str, systemId: str) -> DocumentType: ...
    def getInterface(self, feature: str) -> Self | None: ...

class ElementInfo:
    """
    Object that represents content-model information for an element.

    This implementation is not expected to be used in practice; DOM
    builders should provide implementations which do the right thing
    using information available to it.
    """
    tagName: Incomplete
    def __init__(self, name) -> None: ...
    def getAttributeType(self, aname): ...
    def getAttributeTypeNS(self, namespaceURI: str, localName): ...
    def isElementContent(self): ...
    def isEmpty(self):
        """
        Returns true iff this element is declared to have an EMPTY
        content model.
        """
        ...
    def isId(self, aname):
        """Returns true iff the named attribute is a DTD-style ID."""
        ...
    def isIdNS(self, namespaceURI: str, localName):
        """Returns true iff the identified attribute is a DTD-style ID."""
        ...

class Document(Node, DocumentLS):
    implementation: Incomplete
    nodeType: int
    nodeName: str
    nodeValue: Incomplete
    attributes: Incomplete
    parentNode: Incomplete
    previousSibling: Incomplete
    nextSibling: Incomplete
    actualEncoding: Incomplete
    encoding: str | None
    standalone: bool | None
    version: Incomplete
    strictErrorChecking: bool
    errorHandler: Incomplete
    documentURI: Incomplete
    doctype: DocumentType | None
    childNodes: Incomplete
    def __init__(self) -> None: ...
    def appendChild(self, node: _N) -> _N: ...
    documentElement: Incomplete
    def removeChild(self, oldChild): ...
    def unlink(self) -> None: ...
    def cloneNode(self, deep): ...
    def createDocumentFragment(self) -> DocumentFragment: ...
    def createElement(self, tagName: str) -> Element: ...
    def createTextNode(self, data: str) -> Text: ...
    def createCDATASection(self, data: str) -> CDATASection: ...
    def createComment(self, data: str) -> Comment: ...
    def createProcessingInstruction(self, target, data): ...
    def createAttribute(self, qName) -> Attr: ...
    def createElementNS(self, namespaceURI: str, qualifiedName: str): ...
    def createAttributeNS(self, namespaceURI: str, qualifiedName: str) -> Attr: ...
    def getElementById(self, id: str) -> Element | None: ...
    def getElementsByTagName(self, name: str) -> NodeList[Element]: ...
    def getElementsByTagNameNS(self, namespaceURI: str, localName: str) -> NodeList[Element]: ...
    def isSupported(self, feature: str, version: str | None) -> bool: ...
    def importNode(self, node, deep): ...
    if sys.version_info >= (3, 9):
        def writexml(
            self,
            writer: SupportsWrite[str],
            indent: str = "",
            addindent: str = "",
            newl: str = "",
            encoding: str | None = None,
            standalone: bool | None = None,
        ) -> None: ...
    else:
        def writexml(
            self,
            writer: SupportsWrite[str],
            indent: str = "",
            addindent: str = "",
            newl: str = "",
            encoding: Incomplete | None = None,
        ) -> None: ...

    def renameNode(self, n, namespaceURI: str, name): ...
