!# This source file is part of code Pégase.3.0.1 (2019-02-21).
!# Copyright: Michel Fioc (Michel.Fioc@iap.fr), Sorbonne université, 
!# Institut d'astrophysique de Paris/CNRS, France.
!# 
!# Pégase.3.0.1 is governed by the CeCILL license under French law and abides 
!# by the rules of distribution of free software. You can use, modify and/or 
!# redistribute this software under the terms of the CeCILL license as circulated 
!# by CEA, CNRS and INRIA at "http://www.cecill.info". The text of this license
!# is also available in French and in English in directory "doc_dir/" of this
!# code.
!# 
!# As a counterpart to the access to the source code and to the rights to copy,
!# modify and redistribute it granted by the license, users are provided only
!# with a limited warranty, and the software's author, the holder of the
!# economic rights, and the successive licensors have only limited
!# liability. 
!# 
!# The fact that you are presently reading this means that you have had
!# knowledge of the CeCILL license and that you accept its terms.
!#====================================================================== 

module mod_read_spectra_output

  use mod_types

  implicit none
  private
  type :: struct_spectra_output
     character(std_string) :: version_id, version_date
     character(std_string) :: spectra_output
     logical :: RF_output, sublim_output
     real(CDR) :: time_step
     integer :: dim_output_age, dim_elem, dim_cont, dim_line
     integer :: dim_species_SFC, dim_species_DISM
     character(std_string), dimension(:), pointer :: elem_id => null()
     character(std_string), dimension(:), pointer :: species_id_SFC => null()
     character(std_string), dimension(:), pointer :: species_id_DISM => null()
     real(CDR), dimension(:), pointer :: lambda_cont => null()
     real(CDR), dimension(:), pointer :: lambda_line => null()
     character(std_string), dimension(:), pointer :: line_id => null()
     logical :: reserv_warn_present
     logical :: SF_warn_present
     real(CDR) :: SF_warn_age
     integer :: dim_infall_warn
     integer, dimension(:), pointer :: infall_warn_present => null()
     real(CDR), dimension(:), pointer :: infall_warn_age => null()
     logical :: outflow_warn_present
     real(CDR) :: outflow_warn_age
     real(CDR), dimension(:), pointer :: output_age => null()
     real(CDR), dimension(:), pointer :: convol_time => null()
     real(CDR), dimension(:), pointer :: cosmic_time => null()
     real(CDR), dimension(:), pointer :: redshift => null()
     real(CDR), dimension(:), pointer :: galaxy_mass => null()
     real(CDR), dimension(:), pointer :: WD_mass => null()
     real(CDR), dimension(:), pointer :: BHNS_mass => null()
     real(CDR), dimension(:), pointer :: inert_mass => null()
     real(CDR), dimension(:), pointer :: ISM_mass => null()
     real(CDR), dimension(:), pointer :: ISM_Z => null()
     real(CDR), dimension(:), pointer :: carb_abund => null()
     real(CDR), dimension(:), pointer :: sil_abund => null()
     real(CDR), dimension(:), pointer :: SF_rate => null()
     real(CDR), dimension(:), pointer :: CCSN_rate => null()
     real(CDR), dimension(:), pointer :: SNIa_rate => null()
     real(CDR), dimension(:), pointer :: ejec_rate_tot => null()
     real(CDR), dimension(:), pointer :: infall_rate => null()
     real(CDR), dimension(:), pointer :: outflow_rate => null()
     real(CDR), dimension(:), pointer :: ejec_cumul_mass => null()
     real(CDR), dimension(:), pointer :: SF_live_cumul_mass => null()
     real(CDR), dimension(:), pointer :: infall_cumul_mass => null()
     real(CDR), dimension(:), pointer :: outflow_cumul_mass => null()
     real(CDR), dimension(:), pointer :: live_stars_mass => null()
     real(CDR), dimension(:), pointer :: stel_Z_mass_avrg => null()
     real(CDR), dimension(:), pointer :: stel_Z_bol_avrg => null()
     real(CDR), dimension(:), pointer :: L_bol => null()
     real(CDR), dimension(:), pointer :: tau_V => null()
     real(CDR), dimension(:), pointer :: L_dust => null()
     real(CDR), dimension(:), pointer :: stel_age_mass_avrg => null()
     real(CDR), dimension(:), pointer :: stel_age_bol_avrg => null()
     real(CDR), dimension(:), pointer :: Lyman_cont_gas_abs => null()
     real(CDR), dimension(:), pointer :: Lyman_cont_dust_abs => null()
     real(CDR), dimension(:), pointer :: L_dust_SFC => null()
     real(CDR), dimension(:), pointer :: L_dust_DISM => null()
     real(DPR), dimension(:), pointer :: Lyman_cont_rate => null()
     logical, dimension(:), pointer :: opt_depth_warn_present => null()
     real(CDR), dimension(:), pointer :: opt_depth_warn_min_lambda => null()
     real(CDR), dimension(:), pointer :: opt_depth_warn_max_lambda => null()
     real(CDR), dimension(:,:), pointer :: ISM_abund => null()
     real(CDR), dimension(:,:), pointer :: lum_cont => null()
     real(CDR), dimension(:,:), pointer :: lum_stel_unatt => null()
     real(CDR), dimension(:,:), pointer :: lum_stel_SFC_unatt => null()
     real(CDR), dimension(:,:), pointer :: lum_stel_DISM_unatt => null()
     real(CDR), dimension(:,:), pointer :: lum_neb_cont => null()
     real(CDR), dimension(:,:), pointer :: lum_neb_cont_SFC_unatt => null()
     real(CDR), dimension(:,:), pointer :: lum_neb_cont_DISM_unatt => null()
     real(CDR), dimension(:,:,:), pointer :: lum_species_SFC => null()
     real(CDR), dimension(:,:,:), pointer :: lum_species_DISM => null()
     real(CDR), dimension(:,:,:), pointer :: sublim_lum_species_DISM => null()
     real(CDR), dimension(:,:,:), pointer :: sublim_lum_species_SFC => null()
     real(CDR), dimension(:,:), pointer :: cont_trans_SFC => null()
     real(CDR), dimension(:,:), pointer :: cont_trans_DISM_tot => null()
     real(CDR), dimension(:,:), pointer :: &
          cont_trans_DISM_incl => null()
     real(CDR), dimension(:,:), pointer :: RF_cont_SFC => null()
     real(CDR), dimension(:,:), pointer :: RF_cont_DISM => null()
     real(CDR), dimension(:,:), pointer :: L_line => null()
     real(CDR), dimension(:,:), pointer :: L_line_unatt => null()
     real(CDR), dimension(:,:), pointer :: L_line_SFC_unatt => null()
     real(CDR), dimension(:,:), pointer :: L_line_DISM_unatt => null()
     real(CDR), dimension(:,:), pointer :: line_trans_SFC => null()
     real(CDR), dimension(:,:), pointer :: line_trans_DISM_tot => null()
     real(CDR), dimension(:,:), pointer :: &
          line_trans_DISM_incl => null()
     real(CDR), dimension(:,:), pointer :: RF_line_SFC => null()
     real(CDR), dimension(:,:), pointer :: RF_line_DISM => null()
  end type struct_spectra_output

  public :: read_spectra_output, struct_spectra_output

contains

!#======================================================================

  subroutine read_spectra_output(file_name, data)

    use mod_directories, only : spectra_dir
    use mod_file_access

    implicit none
    character(len=*), intent(in) :: file_name
    type(struct_spectra_output), intent(out) :: data
!#......................................................................
    integer :: unit, i_time, i_cont, i_line, i_species, i_reserv, &
         dim_output_age, dim_elem, dim_cont, dim_line
    integer :: dim_species_SFC, dim_species_DISM
!#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

    call open_file(unit, path_file(spectra_dir, file_name))

    read(unit,*) data % version_id
    read(unit, *) data % version_date

    call skip_comment_lines(unit)

    read(unit,*) data % spectra_output
    if (data % spectra_output == "detailed") then
       read(unit,*) data % RF_output
       read(unit,*) data % sublim_output
    else
       data % RF_output = .false.
       data % sublim_output = .false.
    endif

    read(unit,*) data % time_step

    read(unit,*) data % dim_output_age
    dim_output_age = data % dim_output_age

    read(unit,*) data % dim_elem
    dim_elem = data % dim_elem

    if (associated(data % elem_id)) deallocate(data % elem_id)
    allocate(data % elem_id(dim_elem))
    read(unit,*) data % elem_id(1:dim_elem)

    read(unit,*) data % dim_species_SFC, data % dim_species_DISM
    dim_species_SFC = data % dim_species_SFC
    dim_species_DISM = data % dim_species_DISM

    if (associated(data % species_id_SFC)) &
         deallocate(data % species_id_SFC)
    allocate(data % species_id_SFC(dim_species_SFC))
    read(unit,*) data % species_id_SFC(1:dim_species_SFC)

    if (associated(data % species_id_DISM)) &
         deallocate(data % species_id_DISM)
    allocate(data % species_id_DISM(dim_species_DISM))
    read(unit,*) data % species_id_DISM(1:dim_species_DISM)

    read(unit,*) data % dim_cont
    dim_cont = data % dim_cont

    read(unit,*) data % dim_line
    dim_line = data % dim_line

    if (associated(data % lambda_cont)) deallocate(data % lambda_cont)
    allocate(data % lambda_cont(dim_cont))

    if (associated(data % lambda_line)) deallocate(data % lambda_line)
    allocate(data % lambda_line(dim_line))

    if (associated(data % line_id)) deallocate(data % line_id)
    allocate(data % line_id(dim_line))

    read(unit,*)
    read(unit,*) data % lambda_cont(1:dim_cont)
    read(unit,*)
    do i_line = 1, dim_line
       read(unit,*) data % line_id(i_line), data % lambda_line(i_line)
    enddo

    read(unit,*) data % reserv_warn_present

    read(unit,*) data % SF_warn_present
    if (data % SF_warn_present) read(unit,*) data % SF_warn_age

    read(unit,*) data % dim_infall_warn
    if (associated(data % infall_warn_present)) &
         deallocate(data % infall_warn_present)
    allocate(data % infall_warn_present(data % dim_infall_warn))
    if (associated(data % infall_warn_age)) &
         deallocate(data % infall_warn_age)
    allocate(data % infall_warn_age(data % dim_infall_warn))
    do i_reserv = 1, data % dim_infall_warn
       read(unit,*) data % infall_warn_present(i_reserv), &
            data % infall_warn_age(i_reserv)
    enddo

    read(unit,*) data % outflow_warn_present
    if (data % outflow_warn_present) read(unit,*) data % outflow_warn_age

    if (associated(data % output_age)) deallocate(data % output_age)
    allocate(data % output_age(dim_output_age))

    if (associated(data % convol_time)) deallocate(data % convol_time)
    allocate(data % convol_time(dim_output_age))

    if (associated(data % cosmic_time)) deallocate(data % cosmic_time)
    allocate(data % cosmic_time(dim_output_age))

    if (associated(data % redshift)) deallocate(data % redshift)
    allocate(data % redshift(dim_output_age))

    if (associated(data % galaxy_mass)) deallocate(data % galaxy_mass)
    allocate(data % galaxy_mass(dim_output_age))

    if (associated(data % WD_mass)) deallocate(data % WD_mass)
    allocate(data % WD_mass(dim_output_age))

    if (associated(data % BHNS_mass)) deallocate(data % BHNS_mass)
    allocate(data % BHNS_mass(dim_output_age))

    if (associated(data % inert_mass)) deallocate(data % inert_mass)
    allocate(data % inert_mass(dim_output_age))

    if (associated(data % ISM_mass)) deallocate(data % ISM_mass)
    allocate(data % ISM_mass(dim_output_age))

    if (associated(data % ISM_Z)) deallocate(data % ISM_Z)
    allocate(data % ISM_Z(dim_output_age))

    if (associated(data % carb_abund)) deallocate(data % carb_abund)
    allocate(data % carb_abund(dim_output_age))

    if (associated(data % sil_abund)) deallocate(data % sil_abund)
    allocate(data % sil_abund(dim_output_age))

    if (associated(data % SF_rate)) deallocate(data % SF_rate)
    allocate(data % SF_rate(dim_output_age))

    if (associated(data % CCSN_rate)) deallocate(data % CCSN_rate)
    allocate(data % CCSN_rate(dim_output_age))

    if (associated(data % SNIa_rate)) deallocate(data % SNIa_rate)
    allocate(data % SNIa_rate(dim_output_age))

    if (associated(data % ejec_rate_tot)) deallocate(data % ejec_rate_tot)
    allocate(data % ejec_rate_tot(dim_output_age))

    if (associated(data % infall_rate)) deallocate(data % infall_rate)
    allocate(data % infall_rate(dim_output_age))

    if (associated(data % outflow_rate)) deallocate(data % outflow_rate)
    allocate(data % outflow_rate(dim_output_age))

    if (associated(data % ejec_cumul_mass)) deallocate(data % ejec_cumul_mass)
    allocate(data % ejec_cumul_mass(dim_output_age))

    if (associated(data % SF_live_cumul_mass)) deallocate(data % SF_live_cumul_mass)
    allocate(data % SF_live_cumul_mass(dim_output_age))

    if (associated(data % infall_cumul_mass)) deallocate(data % infall_cumul_mass)
    allocate(data % infall_cumul_mass(dim_output_age))

    if (associated(data % outflow_cumul_mass)) &
         deallocate(data % outflow_cumul_mass)
    allocate(data % outflow_cumul_mass(dim_output_age))

    if (associated(data % live_stars_mass)) deallocate(data % live_stars_mass)
    allocate(data % live_stars_mass(dim_output_age))

    if (associated(data % stel_Z_mass_avrg)) deallocate(data % stel_Z_mass_avrg)
    allocate(data % stel_Z_mass_avrg(dim_output_age))

    if (associated(data % stel_Z_bol_avrg)) deallocate(data % stel_Z_bol_avrg)
    allocate(data % stel_Z_bol_avrg(dim_output_age))

    if (associated(data % L_bol)) deallocate(data % L_bol)
    allocate(data % L_bol(dim_output_age))

    if (associated(data % tau_V)) deallocate(data % tau_V)
    allocate(data % tau_V(dim_output_age))

    if (associated(data % L_dust)) deallocate(data % L_dust)
    allocate(data % L_dust(dim_output_age))

    if (associated(data % stel_age_mass_avrg)) &
         deallocate(data % stel_age_mass_avrg)
    allocate(data % stel_age_mass_avrg(dim_output_age))

    if (associated(data % stel_age_bol_avrg)) &
         deallocate(data % stel_age_bol_avrg)
    allocate(data % stel_age_bol_avrg(dim_output_age))

    if (associated(data % Lyman_cont_gas_abs)) deallocate(data % Lyman_cont_gas_abs)
    allocate(data % Lyman_cont_gas_abs(dim_output_age))

    if (associated(data % Lyman_cont_dust_abs)) deallocate(data % Lyman_cont_dust_abs)
    allocate(data % Lyman_cont_dust_abs(dim_output_age))

    if (associated(data % L_dust_SFC)) deallocate(data % L_dust_SFC)
    allocate(data % L_dust_SFC(dim_output_age))

    if (associated(data % L_dust_DISM)) deallocate(data % L_dust_DISM)
    allocate(data % L_dust_DISM(dim_output_age))

    if (associated(data % Lyman_cont_rate)) deallocate(data % Lyman_cont_rate)
    allocate(data % Lyman_cont_rate(dim_output_age))

    if (associated(data % ISM_abund)) deallocate(data % ISM_abund)
    allocate(data % ISM_abund(dim_output_age, dim_elem))

    if (associated(data % lum_cont)) deallocate(data % lum_cont)
    allocate(data % lum_cont(dim_output_age, dim_cont))

    if (associated(data % lum_stel_SFC_unatt)) deallocate(data % lum_stel_SFC_unatt)
    allocate(data % lum_stel_SFC_unatt(dim_output_age, dim_cont))

    if (associated(data % lum_stel_DISM_unatt)) deallocate(data % lum_stel_DISM_unatt)
    allocate(data % lum_stel_DISM_unatt(dim_output_age, dim_cont))

    if (associated(data % lum_neb_cont_SFC_unatt)) deallocate(data % lum_neb_cont_SFC_unatt)
    allocate(data % lum_neb_cont_SFC_unatt(dim_output_age, dim_cont))

    if (associated(data % lum_neb_cont_DISM_unatt)) deallocate(data % lum_neb_cont_DISM_unatt)
    allocate(data % lum_neb_cont_DISM_unatt(dim_output_age, dim_cont))

    if (associated(data % lum_species_SFC)) deallocate(data % lum_species_SFC)
    allocate(data % lum_species_SFC(dim_species_SFC, dim_output_age, dim_cont))

    if (associated(data % lum_species_DISM)) deallocate(data % lum_species_DISM)
    allocate(data % lum_species_DISM(dim_species_DISM, dim_output_age, dim_cont))

    if (data % sublim_output) then
       if (associated(data % sublim_lum_species_SFC)) deallocate(data % sublim_lum_species_SFC)
       allocate(data % sublim_lum_species_SFC(dim_species_SFC, dim_output_age, dim_cont))

       if (associated(data % sublim_lum_species_DISM)) deallocate(data % sublim_lum_species_DISM)
       allocate(data % sublim_lum_species_DISM(dim_species_SFC, dim_output_age, dim_cont))
    endif

    if (data % RF_output) then
       if (associated(data % RF_cont_SFC)) &
            deallocate(data % RF_cont_SFC)
       allocate(data % RF_cont_SFC(dim_output_age, dim_cont))

       if (associated(data % RF_cont_DISM)) &
            deallocate(data % RF_cont_DISM)
       allocate(data % RF_cont_DISM(dim_output_age, dim_cont))
    endif

    if (associated(data % L_line)) deallocate(data % L_line)
    allocate(data % L_line(dim_output_age, dim_line))

    if (associated(data % L_line_SFC_unatt)) deallocate(data % L_line_SFC_unatt)
    allocate(data % L_line_SFC_unatt(dim_output_age, dim_line))

    if (associated(data % L_line_DISM_unatt)) deallocate(data % L_line_DISM_unatt)
    allocate(data % L_line_DISM_unatt(dim_output_age, dim_line))

    if (data % RF_output) then
       if (associated(data % RF_line_SFC)) &
            deallocate(data % RF_line_SFC)
       allocate(data % RF_line_SFC(dim_output_age, dim_line))

       if (associated(data % RF_line_DISM)) &
            deallocate(data % RF_line_DISM)
       allocate(data % RF_line_DISM(dim_output_age, dim_line))
    endif

    do i_time = 1, dim_output_age
       read(unit,*) data % output_age(i_time)
       read(unit,*) data % convol_time(i_time)
       read(unit,*) data % cosmic_time(i_time)
       read(unit,*) data % redshift(i_time)
       read(unit,*) data % galaxy_mass(i_time)
       read(unit,*) data % live_stars_mass(i_time)
       read(unit,*) data % WD_mass(i_time)
       read(unit,*) data % BHNS_mass(i_time)
       read(unit,*) data % inert_mass(i_time)
       read(unit,*) data % ISM_mass(i_time)
       read(unit,*) data % ISM_Z(i_time)
       read(unit,*) data % stel_Z_mass_avrg(i_time)
       read(unit,*) data % stel_Z_bol_avrg(i_time)
       read(unit,*) data % carb_abund(i_time)
       read(unit,*) data % sil_abund(i_time)
       read(unit,*) data % ISM_abund(i_time,:)
       read(unit,*) data % L_bol(i_time)
       read(unit,*) data % tau_V(i_time)
       read(unit,*) data % L_dust(i_time)
       read(unit,*) data % SF_rate(i_time)
       read(unit,*) data % Lyman_cont_rate(i_time)
       read(unit,*) data % CCSN_rate(i_time)
       read(unit,*) data % SNIa_rate(i_time)
       read(unit,*) data % stel_age_mass_avrg(i_time)
       read(unit,*) data % stel_age_bol_avrg(i_time)
       read(unit,*) data % Lyman_cont_gas_abs(i_time)
       read(unit,*) data % Lyman_cont_dust_abs(i_time)
       read(unit,*) data % ejec_rate_tot(i_time)
       read(unit,*) data % infall_rate(i_time)
       read(unit,*) data % outflow_rate(i_time)
       read(unit,*) data % ejec_cumul_mass(i_time)
       read(unit,*) data % SF_live_cumul_mass(i_time)
       read(unit,*) data % infall_cumul_mass(i_time)
       read(unit,*) data % outflow_cumul_mass(i_time)

       read(unit,*) data % L_dust_SFC(i_time)
       read(unit,*) data % L_dust_DISM(i_time)

       if (associated(data % opt_depth_warn_present)) deallocate(data % opt_depth_warn_present)
       allocate(data % opt_depth_warn_present(dim_output_age))
       if (associated(data % opt_depth_warn_min_lambda)) &
            deallocate(data % opt_depth_warn_min_lambda)
       allocate(data % opt_depth_warn_min_lambda(dim_output_age))
       if (associated(data % opt_depth_warn_max_lambda)) &
            deallocate(data % opt_depth_warn_max_lambda)
       allocate(data % opt_depth_warn_max_lambda(dim_output_age))

       read(unit,*) data % opt_depth_warn_present(i_time)
       if (data % opt_depth_warn_present(i_time)) then
          read(unit,*) data % opt_depth_warn_min_lambda(i_time)
          read(unit,*) data % opt_depth_warn_max_lambda(i_time)
       endif

       if (data % spectra_output == "detailed") then
          read(unit,*)
          do i_cont = 1, dim_cont
             if (data % RF_output) then
                read(unit,*) data % lum_cont(i_time, i_cont), &
                     data % lum_stel_SFC_unatt(i_time, i_cont), &
                     data % lum_stel_DISM_unatt(i_time, i_cont), &
                     data % lum_neb_cont_SFC_unatt(i_time, i_cont), &
                     data % lum_neb_cont_DISM_unatt(i_time, i_cont), &
                     data % RF_cont_SFC(i_time, i_cont), &
                     data % RF_cont_DISM(i_time, i_cont)
             else
                read(unit,*) data % lum_cont(i_time, i_cont), &
                     data % lum_stel_SFC_unatt(i_time, i_cont), &
                     data % lum_stel_DISM_unatt(i_time, i_cont), &
                     data % lum_neb_cont_SFC_unatt(i_time, i_cont), &
                     data % lum_neb_cont_DISM_unatt(i_time, i_cont)
             endif
          enddo
          read(unit,*)
          do i_cont = 1, dim_cont
             read(unit,*) (data % lum_species_SFC(i_species, i_time, i_cont), &
                  i_species = 1, dim_species_SFC), &
                  (data % lum_species_DISM(i_species, i_time, i_cont), &
                  i_species = 1, dim_species_DISM)
          enddo
          if (data % sublim_output) then
             read(unit,*)
             do i_cont = 1, dim_cont
                read(unit,*) &
                     (data % sublim_lum_species_SFC(i_species, i_time, i_cont), &
                     i_species = 1, dim_species_SFC), &
                     (data % sublim_lum_species_DISM(i_species, i_time, i_cont), &
                     i_species = 1, dim_species_DISM)
             enddo
          endif

          read(unit,*)
          do i_line = 1, dim_line
             if (data % RF_output) then
                read(unit,*) data % L_line(i_time, i_line), &
                     data % L_line_SFC_unatt(i_time, i_line), &
                     data % L_line_DISM_unatt(i_time, i_line), &
                     data % RF_line_SFC(i_time, i_line), &
                     data % RF_line_DISM(i_time, i_line)
             else
                read(unit,*) data % L_line(i_time, i_line), &
                     data % L_line_SFC_unatt(i_time, i_line), &
                     data % L_line_DISM_unatt(i_time, i_line)
             endif
          enddo
       else if (data % spectra_output == "basic") then
          read(unit,*)
          read(unit,*) data % lum_cont(i_time, 1:dim_cont)
          read(unit,*)
          read(unit,*) data % L_line(i_time, 1:dim_line)
       else
          write(*, "(a)") "Wrong value for `spectra_output`. Stopped."
          stop
       endif
    enddo
    call close_file(unit)

  end subroutine read_spectra_output

end module mod_read_spectra_output
