! Module  : hdf5_specsource_m
! Purpose : Define hdf5_specsource_t type, for representing a
!           source of specint_t stored in HDF5 files/groups
!
! Copyright 2022 Rich Townsend & The MSG Team
!
! This file is part of MSG. MSG is free software: you can redistribute
! it and/or modify it under the terms of the GNU General Public
! License as published by the Free Software Foundation, version 3.
!
! MSG is distributed in the hope that it will be useful, but WITHOUT
! ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
! or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
! License for more details.
!
! You should have received a copy of the GNU General Public License
! along with this program.  If not, see <http://www.gnu.org/licenses/>.

#:include 'forum.inc'

module hdf5_specsource_m

   ! Uses

   use forum_m

   use file_m
   use specint_m
   use specsource_m
   use stat_m

   use ISO_FORTRAN_ENV

   ! No implicit typing

   implicit none

   ! Derived-type definitions

   type, extends(specsource_t) :: hdf5_specsource_t
      private
      ! Character lengths hard-coded to work around gfortran bug
      character(256), allocatable :: file_names(:)
      character(256), allocatable :: group_names(:)
      real(RD)                    :: lam_min
      real(RD)                    :: lam_max
      integer                     :: n
   contains
      private
      procedure, public :: get_n
      procedure, public :: get_lam_min
      procedure, public :: get_lam_max
      procedure, public :: fetch
   end type hdf5_specsource_t

   ! Interfaces

   interface hdf5_specsource_t
      module procedure hdf5_specsource_t_
   end interface hdf5_specsource_t

   ! Access specifiers

   private

   public :: hdf5_specsource_t

contains

   function hdf5_specsource_t_(file_names, group_names, lam_min, lam_max) result(specsource)

      character(*), intent(in) :: file_names(:)
      character(*), intent(in) :: group_names(:)
      real(RD), intent(in)     :: lam_min
      real(RD), intent(in)     :: lam_max
      type(hdf5_specsource_t)  :: specsource

      @:CHECK_BOUNDS(SIZE(group_names), SIZE(file_names))

      ! Construct specsource with a list of file and group names

      specsource%file_names = file_names
      specsource%group_names = group_names

      specsource%lam_min = lam_min
      specsource%lam_max = lam_max

      specsource%n = SIZE(file_names)

      ! Finish

      return

   end function hdf5_specsource_t_

   !****

   subroutine get_n(self, n)

      class(hdf5_specsource_t), intent(in) :: self
      integer, intent(out)                 :: n

      ! Get n

      n = self%n

      ! Finish

      return

   end subroutine get_n

   !****

   #:for name, type in (('lam_min', 'real(RD)'), &
                        ('lam_max', 'real(RD)'))
                        
      subroutine get_${name}$(self, ${name}$)

         class(hdf5_specsource_t), intent(in) :: self
         ${type}$, intent(out)                :: ${name}$

         ! Get ${name}$

         ${name}$ = self%${name}$

         ! Finish

         return

      end subroutine get_${name}$

   #:endfor

   !****

   subroutine fetch(self, i, specint, stat)

      class(hdf5_specsource_t), intent(in)       :: self
      integer, intent(in)                        :: i
      class(specint_t), allocatable, intent(out) :: specint
      integer, intent(out), optional             :: stat

      type(hdf5io_t)      :: hdf5io
      type(hdf5io_t)      :: hdf5io_specint
      character(TYPE_LEN) :: type

      ! Fetch the specint from the file

      if (i < 0 .OR. i > self%n) then
         if (PRESENT(stat)) then
            stat = STAT_INVALID_ARGUMENT
            return
         else
            @:ABORT('invalid argument')
         end if
      end if

      call open_file(self%file_names(i), hdf5io, stat)
      if (PRESENT(stat)) then
         if (stat /= STAT_OK) return
      end if

      hdf5io_specint = hdf5io_t(hdf5io, self%group_names(i))

      call hdf5io_specint%read_attr('TYPE', type)
      allocate(specint, MOLD=specint_t(type))

      call specint%read(hdf5io_specint, stat)

      call hdf5io_specint%final()
      call hdf5io%final()

      ! Finish

      return

   end subroutine fetch

end module hdf5_specsource_m
