! Module   : specint_to_specgrid
! Purpose  : Create specgrid files from specint files
!
! Copyright 2021-2023 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_specgrid

   ! Uses

   use forum_m
   use msg_m

   use ISO_FORTRAN_ENV

   ! No implicit typing

   implicit none

   ! Parameters

   integer, parameter :: NAME_LEN = 1024
   logical, parameter :: VERBOSE = .FALSE.

   ! Derived-type definitions

   type smeta_t
      character(NAME_LEN)   :: file_name
      real(RD), allocatable :: x_vec(:)
      real(RD)              :: lam_min
      real(RD)              :: lam_max
   end type smeta_t

   ! Variables

   character(:), allocatable :: manifest_file_name
   character(:), allocatable :: specgrid_file_name
   character(:), allocatable :: label

   type(specgrid_t) :: specgrid
   type(hdf5io_t)   :: hdf5io
   
   ! Read command-line arguments

   @:ASSERT(n_arg() == 3, 'Syntax: specint_to_specgrid manifest_file_name specgrid_file_name label')

   call get_arg(1, manifest_file_name)
   call get_arg(2, specgrid_file_name)
   call get_arg(3, label)

   ! Create the grid

   call create_grid(manifest_file_name,  specgrid)

   ! Write the grid

   hdf5io = hdf5io_t(specgrid_file_name, CREATE_FILE)

   call specgrid%write(hdf5io)

   call hdf5io%write_attr('label', label)

   call hdf5io%final()

   ! Finish

contains

   subroutine create_grid(manifest_file_name, specgrid)

      character(*), intent(in)      :: manifest_file_name
      type(specgrid_t), intent(out) :: specgrid

      type(smeta_t), allocatable :: smetas(:)
      character(:), allocatable  :: labels(:)

      ! Create the grid

      call read_smetas(manifest_file_name, smetas, labels)

      call build_grid(smetas, labels, specgrid)

      ! Finish

      return

   end subroutine create_grid

   !****

   subroutine read_smetas(manifest_file_name, smetas, labels)

      character(*), intent(in)                :: manifest_file_name
      type(smeta_t), allocatable, intent(out) :: smetas(:)
      character(:), allocatable, intent(out)  :: labels(:)

      integer        :: unit
      integer        :: n_smetas
      integer        :: i
      type(hdf5io_t) :: hdf5io
      type(hdf5io_t) :: hdf5io_labels
      integer        :: rank
      integer        :: j

      ! Open the manifest file & count lines

      open(NEWUNIT=unit, STATUS='OLD', FILE=manifest_file_name)

      n_smetas = 0

      count_loop: do
         read(unit, *, END=100)
         n_smetas = n_smetas + 1
      end do count_loop
      
100   continue

      ! Read the spectrum metadata

      rewind(unit)

      allocate(smetas(n_smetas))

      write(OUTPUT_UNIT, 110) 'Reading spectrum metadata...'
110   format(A)

      smeta_loop : do i = 1, n_smetas

         read(unit, 110) smetas(i)%file_name

         if (VERBOSE) then
            write(OUTPUT_UNIT, 120) TRIM(smetas(i)%file_name)
120         format(2X,A)
         end if

         ! Open the HDF5 file

         hdf5io = hdf5io_t(smetas(i)%file_name, OPEN_FILE_RO)

         ! Read axis labels/values

         hdf5io_labels = hdf5io_t(hdf5io, 'labels')

         if (i == 1) then

            labels = hdf5io_labels%attr_names()

            associate (s => sort_indices(labels))
              labels = labels(s)
            end associate

            rank = SIZE(labels)

         endif

         allocate(smetas(i)%x_vec(rank))

         do j = 1, rank
            call hdf5io_labels%read_attr(labels(j), smetas(i)%x_vec(j))
         end do

         call hdf5io_labels%final()

         ! Read wavelength range

         call hdf5io%read_attr('lam_min', smetas(i)%lam_min)
         call hdf5io%read_attr('lam_max', smetas(i)%lam_max)

         ! Close the file
         
         call hdf5io%final()

      end do smeta_loop

      close(unit)

      write(OUTPUT_UNIT, 130) 'Number of spectra:', n_smetas
130   format(2X,A,1X,I0)

      ! Finish

      return

   end subroutine read_smetas

   !****

   subroutine build_grid(smetas, labels, specgrid)

      type(smeta_t), allocatable, intent(inout) :: smetas(:)
      character(*), intent(in)                  :: labels(:)
      type(specgrid_t), intent(out)             :: specgrid

      integer                          :: n_smetas
      integer                          :: rank
      real(RD), allocatable            :: x_vecs(:,:)
      logical, allocatable             :: mask(:)
      integer                          :: i
      type(vgrid_t)                    :: vgrid
      character(NAME_LEN), allocatable :: file_names(:)
      character(NAME_LEN), allocatable :: group_names(:)
      type(hdf5_specsource_t)          :: specsource
      
      ! Create the vgrid

      ! First, get the list of vertex coordinates

      n_smetas = SIZE(smetas)
      rank = SIZE(labels)

      allocate(x_vecs(rank,n_smetas))

      do i = 1, n_smetas
         x_vecs(:,i) = smetas(i)%x_vec
      enddo

      ! Optimize it

      write(OUTPUT_UNIT, 100) 'Optimizing...'
100   format(A)

      allocate(mask(n_smetas))

      call optimize(x_vecs, mask)

      smetas = PACK(smetas, mask)

      n_smetas = SIZE(smetas)

      deallocate(x_vecs)
      allocate(x_vecs(rank,n_smetas))

      do i = 1, n_smetas
         x_vecs(:,i) = smetas(i)%x_vec
      enddo

      write(OUTPUT_UNIT, 110) 'Number of spectra:', n_smetas
110   format(2X,A,1X,I0)

      ! Construct the vgrid

      vgrid = vgrid_t(x_vecs, labels)

      ! Set up the file/group name lists

      file_names = smetas%file_name

      allocate(group_names(n_smetas))
      group_names = '/'

      ! Create specsource

      specsource = hdf5_specsource_t(file_names, group_names, MAXVAL(smetas%lam_min), MINVAL(smetas%lam_max))

      ! Create specgrid

      write(OUTPUT_UNIT, 100) 'Building specgrid...'

      specgrid = specgrid_t(specsource, vgrid)

      call specgrid%set_cache_limit(0)

      ! Finish

      return

   end subroutine build_grid

end program specint_to_specgrid
