//
// Copyright (c) 2008, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//    11 Nov 08  Brian Frank  Creation
//

**
** XML document encapsulates the root element and document type.
**
@Js class XDoc : XNode
{

//////////////////////////////////////////////////////////////////////////
// Construction
//////////////////////////////////////////////////////////////////////////

  **
  ** Construct with optional root elem.
  **
  new make(XElem? root := null)
  {
    if (root != null)
      this.root = root
    else
      this.root = XElem("undefined")
  }

//////////////////////////////////////////////////////////////////////////
// Identity
//////////////////////////////////////////////////////////////////////////

  **
  ** Return the `XNodeType.doc`.
  **
  override XNodeType nodeType() { return XNodeType.doc }

  **
  ** Document type declaration or null if undefined.
  **
  XDocType? docType

  **
  ** Return string representation.
  **
  override Str toStr()
  {
    return "<?xml version='1.0'?>"
  }

//////////////////////////////////////////////////////////////////////////
// Children
//////////////////////////////////////////////////////////////////////////

  **
  ** Root element.
  **
  XElem root
  {
    set
    {
      if (it.parent != null) throw ArgErr("Node already parented: $it")
      it.parent = this
      this.&root = it
    }
  }

  **
  ** Get any processing instructions declared before the
  ** root element.  Processing instructions after the root
  ** are not supported.
  **
  XNode[] pis() { return piList.ro }

  **
  ** Add a node to the document.  If the node is an XElem then it
  ** is defined as the `root` element, otherwise the child must be
  ** a `XPi`.  Return this.
  **
  @Operator This add(Obj child)
  {
    if (child is XElem) { root = child; return this }
    pi := (XPi)child
    if (pi.parent != null) throw ArgErr("Node already parented: $pi")
    pi.parent = this
    piList = piList.rw
    piList.add(pi)
    return this
  }

  **
  ** Remove the processing instruction by reference.
  **
  XPi? removePi(XPi pi)
  {
    if (piList.isEmpty) return null
    if (piList.removeSame(pi) !== pi) return null
    pi.parent = null
    return pi
  }

//////////////////////////////////////////////////////////////////////////
// Write
//////////////////////////////////////////////////////////////////////////

  **
  ** Write this node to the output stream.
  **
  override Void write(OutStream out)
  {
    out.writeChars("<?xml version='1.0' encoding='${out.charset}'?>\n")
    if (docType != null) out.writeChars(docType.toStr).writeChar('\n')
    piList.each |XPi pi| { pi.write(out); out.writeChar('\n') }
    root.write(out)
    out.writeChar('\n')
  }

//////////////////////////////////////////////////////////////////////////
// Fields
//////////////////////////////////////////////////////////////////////////

  internal const static XPi[] noPis := XPi[,]

  internal XPi[] piList :=  noPis

}