! Module   : specint_to_photint
! Purpose  : Create a specint file by rebinning / subsetting an existing
!            specint file
!
! 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'

program specint_to_specint

   ! Uses

   use forum_m, PI_ => PI
   use msg_m

   use ISO_FORTRAN_ENV

   ! No implicit typing

   implicit none

   ! Variables

   character(:), allocatable :: specint_file_name_in
   character(:), allocatable :: specint_file_name
   real(RD)                  :: lam_min
   real(RD)                  :: lam_max
   real(RD)                  :: dlam
   real(RD)                  :: R
   character(:), allocatable :: just
   logical                   :: lam_min_set
   logical                   :: lam_max_set
   logical                   :: dlam_set
   logical                   :: R_set
   logical                   :: just_set

   integer                           :: status
   type(hdf5io_t)                    :: hdf5io
   class(specint_t), allocatable     :: specint_in
   character(TYPE_LEN)               :: type
   type(hdf5io_t)                    :: hdf5io_labels
   character(LABEL_LEN), allocatable :: labels(:)
   integer                           :: n_labels
   real(RD), allocatable             :: values(:)
   integer                           :: j
   real(RD)                          :: lam_min_in
   real(RD)                          :: lam_max_in
   class(range_t), allocatable       :: range
   class(specint_t), allocatable     :: specint

   ! Read command-line arguments

   @:ASSERT(n_arg() >= 2, 'Syntax: specint_to_specint specint_file_name_in specint_file_name [lam_min=...] [lam_max=...] [R=...|dlam=...] [just=...]')

   call get_arg(1, specint_file_name_in)
   call get_arg(2, specint_file_name)

   call get_arg('lam_min', lam_min, status, first=3)
   lam_min_set = status == 0

   call get_arg('lam_max', lam_max, status, first=3)
   lam_max_set = status == 0
   
   call get_arg('dlam', dlam, status, first=3)
   dlam_set = status == 0
   
   call get_arg('R', R, status, first=3)
   R_set = status == 0
   
   call get_arg('just', just, status, first=3)
   just_set = status == 0

   ! Read data from the specint file

   hdf5io = hdf5io_t(specint_file_name_in, OPEN_FILE_RO)

   call hdf5io%read_attr('TYPE', type)
   allocate(specint_in, MOLD=specint_t(type))

   call specint_in%read(hdf5io)

   hdf5io_labels = hdf5io_t(hdf5io, 'labels')

   labels = hdf5io_labels%dset_names()
   n_labels = SIZE(labels)

   allocate(values(n_labels))

   do j = 1, n_labels
      call hdf5io_labels%read_dset(labels(j), values(j))
   end do

   call hdf5io_labels%final()
   call hdf5io%final()

   ! Check/set wavelength bounds

   call specint_in%get_lam_min(lam_min_in)
   call specint_in%get_lam_max(lam_max_in)

   if (lam_min_set) then
      @:ASSERT(lam_min >= lam_min_in, 'lam_min must be >= lam_min_in')
   else
      lam_min = lam_min_in
   endif

   if (lam_min_set) then
      @:ASSERT(lam_min >= lam_min_in, 'lam_min must be >= lam_min_in')
   else
      lam_min = lam_min_in
   endif

   if (.NOT. just_set) just = 'C'

   ! Check/set resolution parameters

   @:ASSERT(.NOT. (R_set .AND. dlam_set),'R and dlam cannot be set together')

   ! Create the new spectrum

   if (R_set .OR. dlam_set) then

      ! Set up the new wavelength axis

      if (R_set) then
         range = log_range(lam_min, lam_max, R, just)
      else
         range = lin_range(lam_min, lam_max, R, just)
      endif

      ! Create the spectrum by interpolation onto the new wavelength
      ! axis

      call specint_in%rebin(range, specint)

   else

      ! Create the spectrum by subsetting

      call specint_in%subset(lam_min, lam_max, specint)

   endif

   ! Write data to the specint file

   hdf5io = hdf5io_t(specint_file_name, CREATE_FILE)

   call specint%write(hdf5io)

   hdf5io_labels = hdf5io_t(hdf5io, 'labels')

   do j = 1, n_labels
      call hdf5io_labels%write_dset(labels(j), values(j))
   end do

   call hdf5io%final()

   ! Finish

contains

   function lin_range(lam_min, lam_max, dlam, just) result(range)

      real(RD), intent(in)     :: lam_min
      real(RD), intent(in)     :: lam_max
      real(RD), intent(in)     :: dlam
      character(*), intent(in) :: just
      type(lin_range_t)        :: range

      integer  :: n
      real(RD) :: x_0

      ! Determine number of points

      n = FLOOR((lam_max - lam_min)/dlam) + 1

      ! Determine start value

      select case (just)
      case ('L')
         x_0 = lam_min
      case ('R')
         x_0 = lam_max - dlam*(n-1)
      case ('C')
         x_0 = 0.5_RD*(lam_min + lam_max - dlam*(n-1))
      case default
         @:ABORT('invalid just')
      end select

      x_0 = MAX(x_0, lam_min)

      ! Set up range

      range = lin_range_t(x_0, dlam, n)

      ! Finish

      return

   end function lin_range

   !****
   
   function log_range(lam_min, lam_max, R, just) result(range)

      real(RD), intent(in)     :: lam_min
      real(RD), intent(in)     :: lam_max
      real(RD), intent(in)     :: R
      character(*), intent(in) :: just
      type(log_range_t)        :: range

      real(RD) :: dx
      integer  :: n
      real(RD) :: x_0

      ! Determine number of points

      dx = 1._RD/R

      n = FLOOR((LOG(lam_max) - LOG(lam_min))/dx) + 1

      ! Determine start value

      select case (just)
      case ('L')
         x_0 = LOG(lam_min)
      case ('R')
         x_0 = LOG(lam_max) - dx*(n-1)
      case ('C')
         x_0 = 0.5_RD*(LOG(lam_min) + LOG(lam_max) - dx*(n-1))
      case default
         @:ABORT('invalid just')
      end select

      x_0 = MAX(x_0, LOG(lam_min))

      ! Set up range

      range = log_range_t(x_0, dx, n)

      ! Finish

      return

   end function log_range
      
end program specint_to_specint
