! --------------------------------------------------------
! Instituto Nacional de Pesquisas Espaciais - INPE
! Coordenacao-Geral de Ciencias da Terra - CGCT
! Divisao de Impactos, Adaptacao e Vulnerabilidade - DIIAV
!
! Projeto: SisMOM
! Sistema de Monitoramento e Modelagem Oceanica e Costeira
! --------------------------------------------------------
! Leitura dos campos NetCDF (2D/3D), coordenadas e timesteps, com tratamento do fill.
! --------------------------------------------------------
module mod_netcdf
  use mod_kinds, only: sp, dp
  use netcdf
  use, intrinsic :: ieee_arithmetic, only: ieee_value, ieee_quiet_nan
  implicit none
  private
  public :: nc_ntime, read_field2d, read_field3d

  real(sp), parameter :: FILL_THRESHOLD = 1.0e19_sp

contains

  subroutine nc_check(status, ctx)
    integer,          intent(in) :: status
    character(len=*), intent(in) :: ctx
    if (status /= nf90_noerr) then
      write(*,*) 'ERRO NetCDF (', trim(ctx), '): ', trim(nf90_strerror(status))
      stop 1
    end if
  end subroutine nc_check

  function nc_ntime(path) result(nt)
    character(len=*), intent(in) :: path
    integer :: nt, ncid, dimid
    call nc_check(nf90_open(path, nf90_nowrite, ncid), 'open '//trim(path))
    call nc_check(nf90_inq_dimid(ncid, 'time', dimid), 'inq time '//trim(path))
    call nc_check(nf90_inquire_dimension(ncid, dimid, len=nt), 'dim time')
    call nc_check(nf90_close(ncid), 'close')
  end function nc_ntime

  subroutine get_units(ncid, varid, units)
    integer,          intent(in)  :: ncid
    integer,          intent(in)  :: varid
    character(len=*), intent(out) :: units
    integer :: status
    units = ''
    status = nf90_get_att(ncid, varid, 'units', units)
    if (status /= nf90_noerr) then
      status = nf90_get_att(ncid, varid, 'long_name', units)
      if (status /= nf90_noerr) units = ''
    end if
  end subroutine get_units

  subroutine coord_var(ncid, dimid, vals)
    integer,               intent(in)  :: ncid
    integer,               intent(in)  :: dimid
    real(dp), allocatable, intent(out) :: vals(:)
    integer :: n, vid
    character(len=nf90_max_name) :: dname
    call nc_check(nf90_inquire_dimension(ncid, dimid, name=dname, len=n), 'inq dim')
    allocate(vals(n))
    call nc_check(nf90_inq_varid(ncid, trim(dname), vid), 'inq coord '//trim(dname))
    call nc_check(nf90_get_var(ncid, vid, vals), 'get coord '//trim(dname))
  end subroutine coord_var

  subroutine read_field2d(path, var, t, x, y, data, units)
    character(len=*),      intent(in)  :: path, var
    integer,               intent(in)  :: t
    real(dp), allocatable, intent(out) :: x(:), y(:)
    real(sp), allocatable, intent(out) :: data(:,:)
    character(len=*),      intent(out) :: units
    integer :: ncid, vid, dimids(nf90_max_var_dims), ndims, nx, ny
    real(sp) :: nan

    call nc_check(nf90_open(path, nf90_nowrite, ncid), 'open '//trim(path))
    call nc_check(nf90_inq_varid(ncid, var, vid), 'inq var '//trim(var))
    call nc_check(nf90_inquire_variable(ncid, vid, ndims=ndims, dimids=dimids), 'inq var dims')
    call coord_var(ncid, dimids(1), x)
    call coord_var(ncid, dimids(2), y)
    nx = size(x); ny = size(y)
    allocate(data(nx, ny))
    call nc_check(nf90_get_var(ncid, vid, data, start=[1, 1, t], count=[nx, ny, 1]), &
         'get '//trim(var))
    call get_units(ncid, vid, units)
    call nc_check(nf90_close(ncid), 'close')
    nan = ieee_value(1.0_sp, ieee_quiet_nan)
    where (abs(data) > FILL_THRESHOLD) data = nan
  end subroutine read_field2d

  subroutine read_field3d(path, var, t, x, y, z, data, units)
    character(len=*),      intent(in)  :: path, var
    integer,               intent(in)  :: t
    real(dp), allocatable, intent(out) :: x(:), y(:), z(:)
    real(sp), allocatable, intent(out) :: data(:,:,:)
    character(len=*),      intent(out) :: units
    integer :: ncid, vid, dimids(nf90_max_var_dims), ndims, nx, ny, nz
    real(sp) :: nan

    call nc_check(nf90_open(path, nf90_nowrite, ncid), 'open '//trim(path))
    call nc_check(nf90_inq_varid(ncid, var, vid), 'inq var '//trim(var))
    call nc_check(nf90_inquire_variable(ncid, vid, ndims=ndims, dimids=dimids), 'inq var dims')
    call coord_var(ncid, dimids(1), x)
    call coord_var(ncid, dimids(2), y)
    call coord_var(ncid, dimids(3), z)
    nx = size(x); ny = size(y); nz = size(z)
    allocate(data(nx, ny, nz))
    call nc_check(nf90_get_var(ncid, vid, data, start=[1, 1, 1, t], &
         count=[nx, ny, nz, 1]), 'get '//trim(var))
    call get_units(ncid, vid, units)
    call nc_check(nf90_close(ncid), 'close')
    nan = ieee_value(1.0_sp, ieee_quiet_nan)
    where (abs(data) > FILL_THRESHOLD) data = nan
  end subroutine read_field3d

end module mod_netcdf
