! Module  : passband_m
! Purpose : define passband_t type, representing a photometric passband
!
! Copyright 2021-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 passband_m

   ! Uses

   use forum_m

   use file_m
   use cubint_m
   use stat_m

   use ISO_FORTRAN_ENV

   ! No implicit typing

   implicit none

   ! Derived-type definitions

   type passband_t
      private
      type(cubint_t) :: cubint
      real(RD)       :: F_0
      real(RD)       :: Delta
      real(RD)       :: lam_min
      real(RD)       :: lam_max
   contains
      private
      procedure, public :: get_lam_min
      procedure, public :: get_lam_max
      procedure, public :: passthru
      procedure, public :: read
      procedure, public :: write
   end type passband_t

   ! Interfaces

   interface passband_t
      module procedure passband_t_
      module procedure passband_t_cubint_
   end interface passband_t

   ! Access specifiers

   private

   public :: passband_t

   ! Procedures

contains

   function passband_t_(S, lam, F_0) result(passband)

      real(RD), intent(in) :: S(:)
      real(RD), intent(in) :: lam(:)
      real(RD), intent(in) :: F_0
      type(passband_t)     :: passband

      @:CHECK_BOUNDS(SIZE(lam), SIZE(S))

      ! Construct passband from the supplied data

      passband = passband_t_cubint_(cubint_t(lam, S, 'SPLINE'), F_0)

      ! Finish

      return

   end function passband_t_

   !****

   function passband_t_cubint_(cubint, F_0) result(passband)

      type(cubint_t), intent(in) :: cubint
      real(RD), intent(in)       :: F_0
      type(passband_t)           :: passband
      
      ! Construct the passband_t from the cubint_t

      passband%cubint = cubint

      call cubint%get_x_min(passband%lam_min)
      call cubint%get_x_max(passband%lam_max)

      passband%F_0 = F_0
      passband%Delta = cubint%integ(passband%lam_max) - &
                       cubint%integ(passband%lam_min)

      ! Finish

      return

   end function passband_t_cubint_

   !****

   subroutine get_lam_min(self, lam_min)

      class(passband_t), intent(in) :: self
      real(RD), intent(out)         :: lam_min

      ! Get the wavelength-minimum

      lam_min = self%lam_min

      ! Finish

      return

   end subroutine get_lam_min

   !****

   subroutine get_lam_max(self, lam_max)

      class(passband_t), intent(in) :: self
      real(RD), intent(out)         :: lam_max

      ! Get the wavelength-maximum

      lam_max = self%lam_max

      ! Finish

      return

   end subroutine get_lam_max

   !****

   function passthru(self, lam) result (P)

      class(passband_t), intent(in) :: self
      real(RD), intent(in)          :: lam(:)
      real(RD), allocatable         :: P(:)

      integer               :: n_lam
      real(RD), allocatable :: G(:)

      ! Evaluate the passthru P = int_a^b S(lam) dlam / (D*F_0) for each interval lam[i,i+1]

      n_lam = SIZE(lam)

      G = self%cubint%integ(lam)

      P = (G(2:n_lam) - G(1:n_lam-1))/(self%F_0*self%Delta)

      ! Finish

      return

   end function passthru

   !****

   subroutine read(self, hdf5io, stat)

      class(passband_t), intent(out) :: self
      type(hdf5io_t), intent(inout)  :: hdf5io
      integer, intent(out), optional :: stat

      integer :: revision

      ! Read the passband_t

      call check_type(hdf5io, 'passband_t', stat)
      if (PRESENT(stat)) then
         if (stat /= STAT_OK) return
      end if

      call hdf5io%read_attr('REVISION', revision)

      select case(revision)
      case(1)
         call read_rev1_(self, hdf5io, stat)
      case default
         if (PRESENT(stat)) then
            stat = STAT_INVALID_GROUP_REVISION
            return
         else
            @:ABORT('invalid group revision')
         end if
      end select

      ! Finish

      return

   contains

      subroutine read_rev1_(self, hdf5io, stat)

         class(passband_t), intent(out) :: self
         type(hdf5io_t), intent(inout)  :: hdf5io
         integer, intent(out), optional :: stat

         real(RD)       :: F_0
         type(hdf5io_t) :: hdf5io_cubint
         type(cubint_t) :: cubint

         call hdf5io%read_attr('F_0', F_0)

         hdf5io_cubint = hdf5io_t(hdf5io, 'cubint')
         call cubint%read(hdf5io_cubint, stat)
         call hdf5io_cubint%final()
         if (PRESENT(stat)) then
            if (stat /= STAT_OK) return
         end if

         select type(self)
         type is(passband_t)
            self = passband_t_cubint_(cubint, F_0)
         class default
            @:ABORT('invalid type')
         end select

         if (PRESENT(stat)) stat = STAT_OK

         return

      end subroutine read_rev1_

   end subroutine read

   !****

   subroutine write(self, hdf5io, stat)

      class(passband_t), intent(in)  :: self
      type(hdf5io_t), intent(inout)  :: hdf5io
      integer, intent(out), optional :: stat

      type(hdf5io_t) :: hdf5io_cubint

      ! Write the passband_t

      call hdf5io%write_attr('TYPE', 'passband_t')
      call hdf5io%write_attr('REVISION', 1)

      call hdf5io%write_attr('F_0', self%F_0)

      hdf5io_cubint = hdf5io_t(hdf5io, 'cubint')
      call self%cubint%write(hdf5io_cubint, stat)
      call hdf5io_cubint%final()
      if (PRESENT(stat)) then
         if (stat /= STAT_OK) return
      end if

      ! Finish

      if (PRESENT(stat)) stat = STAT_OK

      return

   end subroutine write
   
end module passband_m
