module m_handlers

  use FoX_common
  use FoX_sax

  implicit none
  private

  ! This shows how you might use a SAX processor
  ! to extract character data from within certain nodes.

  ! It will print out the character content of all scalar
  ! nodes inside parameter nodes.

  ! It will also remember the value of the scalar within the 
  ! parameter node called DM.EnergyTolerance

  ! Note that when dealing with character data, we need to 
  ! concatenate all the character handling events in question:
  ! we do this using some of the character handling routines 
  ! provided as a convenience by FoX.

  public :: characters_handler
  public :: endDocument_handler
  public :: endElement_handler
  public :: startDocument_handler
  public :: startElement_handler

  logical :: inScalar
  logical :: inParameter
  logical :: etolFound
  real :: etol
  character, pointer :: c(:) => null()

  public :: etol

contains

  subroutine characters_handler(chunk)
    character(len=*), intent(in) :: chunk
    character, pointer :: c2(:)
    if (inScalar.and.inParameter) then
      print*, "Found some scalar parameter data:"
      print*, trim(chunk)
      if (etolFound) then
        if (associated(c)) then
          c2 => concat(c, chunk)
          deallocate(c)
          c => c2
        else
          c => alloc(chunk)
        endif
      endif
    endif

  end subroutine characters_handler

  subroutine endElement_handler(URI, localname, name)
    character(len=*), intent(in)     :: URI
    character(len=*), intent(in)     :: localname
    character(len=*), intent(in)     :: name

    ! Note if we are leaving a scalar or parameter element.
    if (URI=="http://www.xml-cml.org/schema" &
      .and. localName=="scalar") then
      inScalar = .false.
      if (etolFound) then
        ! pull the data out of the concatenated string:
        etol = vs_real(c)
      endif
    elseif (URI=="http://www.xml-cml.org/schema" &
      .and. localName=="parameter") then
      inParameter = .false.
      etolFound = .false.
    endif
  end subroutine endElement_handler
  
  subroutine startDocument_handler
    ! Initialize state variables
    inScalar = .false.
    inParameter = .false.
  end subroutine startDocument_handler
  
  subroutine endDocument_handler
    if (associated(c)) deallocate(c)
  end subroutine endDocument_handler
  
  subroutine startElement_handler(URI, localname, name, attributes)
    character(len=*), intent(in)   :: URI
    character(len=*), intent(in)   :: localname
    character(len=*), intent(in)   :: name
    type(dictionary_t), intent(in) :: attributes

    integer :: i

    ! Note if we are entering a scalar or parameter element.
    if (URI=="http://www.xml-cml.org/schema" &
      .and. localName=="scalar") then
      inScalar = .true.
    elseif (URI=="http://www.xml-cml.org/schema" &
      .and. localName=="parameter") then
      inParameter = .true.
      ! Loop over the attributes, looking for the name ...
      do i = 1, getLength(attributes)
        if (getQName(attributes, i)=="name") then
          ! And if the attribute is called name, check if the name
          ! is what we are interested in.
          print*, "name=", getValue(attributes, i)
          etolfound = getValue(attributes, i)=="DM.EnergyTolerance"
        endif
      enddo
    endif
  end subroutine startElement_handler

end module m_handlers

program sax_example

  use FoX_sax

  use m_handlers ! above

  implicit none

  integer :: iostat
  type(xml_t)  :: fxml

  call open_xml_file(fxml, "h2o.xml", iostat=iostat)
  if (iostat /= 0) then
    write(*,*) "Cannot open file."
    stop
  endif

  call parse(fxml,&
    characters_handler=characters_handler, &
    endDocument_handler=endDocument_handler, & 
    endElement_handler=endElement_handler, & 
    startDocument_handler=startDocument_handler, & 
    startElement_handler=startElement_handler)

  call close_xml_t(fxml)

  print*, "The energy tolerance requested was:", etol

end program sax_example
