Module m_dom_types

  use m_dom_strings

  implicit none

  private

  !-------------------------------------------------------   
  ! A GENERIC NODE
  !-------------------------------------------------------   
  type, public :: fnode
     type(string)         :: nodeName    
     type(string)         :: nodeValue   
!!!     character(len=200)    :: nodeName  = ""
!!!     character(len=200)    :: nodeValue = ""
     integer              :: nc              = 0 
     integer              :: nodeType        = 0
     type(fnode), pointer :: parentNode      => null()
     type(fnodeList), pointer :: childNodes  => null()  ! New
     type(fnode), pointer :: firstChild      => null()
     type(fnode), pointer :: lastChild       => null()
     type(fnode), pointer :: previousSibling => null()
     type(fnode), pointer :: nextSibling     => null()
     type(fnode), pointer :: ownerDocument   => null()
     type(fnamedNodeMap), pointer :: attributes => null()
  end type fnode

  !-----------------------------------------------------------
  !  ONE WAY TO IMPLEMENT A NAMEDNODEMAP  (dictionary)
  !-----------------------------------------------------------

  ! Linked list of name/node pairs, with overall length variable

  type, public :: fnamedNode
     type(string)                   :: name
!!!     character(len=100)            :: name
     type(fnode), pointer          :: node => null()
     type(fnamedNode), pointer     :: next => null()
  end type fnamedNode

  type, public :: fnamedNodeMap
     integer :: length = 0
     type(fnamedNode), pointer  :: head => null()
     type(fnamedNode), pointer  :: tail => null()
  end type fnamedNodeMap

  !-----------------------------------------------------------
  !  ONE WAY TO IMPLEMENT A NODELIST 
  !-----------------------------------------------------------

  type, public :: flistNode
     type(fnode), pointer          :: node => null()
     type(flistNode), pointer      :: next => null()
  end type flistNode

  type, public :: fnodeList
     integer                      :: length = 0
     type(flistNode), pointer     :: head => null()
     type(flistNode), pointer     :: tail => null()
  end type fnodeList

!========================================================================
  integer, save, private          :: allocated_nodes = 0
!========================================================================

  !-------------------------------------------------------   
  ! NODETYPES
  !-------------------------------------------------------   
  integer, parameter, public :: ELEMENT_NODE                = 1
  integer, parameter, public :: ATTRIBUTE_NODE              = 2
  integer, parameter, public :: TEXT_NODE                   = 3
  integer, parameter, public :: CDATA_SECTION_NODE          = 4
  integer, parameter, public :: ENTITY_REFERENCE_NODE       = 5
  integer, parameter, public :: ENTITY_NODE                 = 6
  integer, parameter, public :: PROCESSING_INSTRUCTION_NODE = 7
  integer, parameter, public :: COMMENT_NODE                = 8
  integer, parameter, public :: DOCUMENT_NODE               = 9
  integer, parameter, public :: DOCUMENT_TYPE_NODE          = 10
  integer, parameter, public :: DOCUMENT_FRAGMENT_NODE      = 11
  integer, parameter, public :: NOTATION_NODE               = 12

  public :: node_class
  public :: createNode
  public :: destroyNode
  public :: destroyNamedNodeMap
  public :: destroyNodeList
  public :: getNumberofAllocatedNodes

CONTAINS

  function getNumberofAllocatedNodes() result(n)
    integer   :: n

    n = allocated_nodes
  end function getNumberofAllocatedNodes

!--------------------------------------------------------------
  function createNode() result(node)
    type(fnode), pointer  :: node

    allocate(node)
    allocated_nodes = allocated_nodes + 1

  end function createNode
!--------------------------------------------------------------

  function node_class(nodetype) result(class)
    integer, intent(in) :: nodetype
    character(len=10)  ::     class

    select case(nodetype)
    case(ELEMENT_NODE)
       class = "element"
    case(ATTRIBUTE_NODE)
       class = "attribute"
    case(TEXT_NODE)
       class = "text"
    case(COMMENT_NODE)
       class = "comment"
    case(DOCUMENT_NODE)
       class = "document"
    end select
  end function node_class

  subroutine destroyNamedNodeMap(nodemap)
    type(fnamedNodeMap), pointer :: nodemap

    type(fnamednode), pointer  :: nnp
    type(fnode), pointer       :: ghost
    
    if (.not. associated(nodemap)) return
    nnp => nodemap%head
    do while (associated(nnp))
       call unstring(nnp%name)
       ghost => nnp%node
       nnp => nnp%next
       call destroyNode(ghost)      ! We might not want to really destroy
    enddo
  end subroutine destroyNamedNodeMap

  subroutine destroyNodeList(nodelist)
    type(fnodeList), pointer :: nodelist

    type(flistnode), pointer   :: p
    type(fnode), pointer       :: ghost
    
    if (.not. associated(nodelist)) return
    p => nodelist%head
    do while (associated(p))
       ghost => p%node
       p => p%next
       call destroyNode(ghost)      ! We might not want to really destroy
    enddo
  end subroutine destroyNodeList

  recursive subroutine destroyNode(node)
    type(fnode), pointer  :: node
    
    type(fnode), pointer  :: np, ghost
    
    np => node
    do while (associated(np))
       if (associated(np%firstChild)) then
          call destroyNode(np%firstChild)
       endif
       if (associated(np%attributes)) call destroyNamedNodeMap(np%attributes)
       call unstring(np%nodeName)
       call unstring(np%nodeValue)
       if (associated(np%previousSibling)) & 
                np%previousSibling%nextSibling => np%nextSibling
       if (associated(np%nextSibling)) & 
                np%nextSibling%previousSibling => np%previousSibling
       if (associated(np%parentNode)) then
          if (associated(np%parentNode%firstChild,np)) &
               np%parentNode%firstChild => null()
          if (associated(np%parentNode%lastChild,np)) &
               np%parentNode%lastChild => null()
       endif
       if (associated(np,node)) then    
          deallocate(np)
          allocated_nodes = allocated_nodes - 1
          EXIT                           ! do not destroy siblings
       else
          ghost => np
          np => np%nextSibling
          deallocate(ghost)
          allocated_nodes = allocated_nodes - 1
       endif
    enddo
    node => null()     ! superfluous ?
  end subroutine destroyNode

end module m_dom_types

