! --------------------------------------------------------
! 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
! --------------------------------------------------------
! Programa principal: percorre grades e timesteps gerando ventos e correntes.
! --------------------------------------------------------
program nc2txt
  use mod_kinds, only: sp, dp
  use mod_config, only: config_t, read_config
  use mod_grid, only: grid_t, read_grid
  use mod_netcdf, only: nc_ntime, read_field2d, read_field3d
  use mod_interp, only: bilinear, vinterp_levels, kelvin_to_celsius
  use mod_datetime, only: add_hours
  use mod_writer, only: write_winds, open_currents, write_current_level, close_currents
  use, intrinsic :: ieee_arithmetic, only: ieee_value, ieee_quiet_nan, ieee_is_nan
  implicit none

  type(config_t) :: cfg
  type(grid_t)   :: g
  character(len=512) :: config_path, grid_path, out_path, win_dir, cur_dir
  character(len=256) :: atm_u, atm_v, atm_t, var_u, var_v, var_t
  integer :: ig, t, nt, k, p
  real(sp) :: nan
  real(dp) :: layer

  if (command_argument_count() >= 1) then
    call get_command_argument(1, config_path)
  else
    config_path = 'config.nml'
  end if

  call read_config(trim(config_path), cfg)
  nan = ieee_value(1.0_sp, ieee_quiet_nan)
  call resolve_atm(cfg, atm_u, atm_v, atm_t, var_u, var_v, var_t)

  do ig = 1, cfg%ngrids
    grid_path = trim(cfg%grid_dir)//'/GRID_'//trim(cfg%grids(ig))//'/grid_'// &
         trim(cfg%grids(ig))//'.txt'
    call read_grid(trim(grid_path), trim(cfg%grids(ig)), g)
    win_dir = trim(cfg%grid_dir)//'/GRID_'//trim(cfg%grids(ig))//'/WINDS'
    cur_dir = trim(cfg%grid_dir)//'/GRID_'//trim(cfg%grids(ig))//'/CURRENTS'
    call execute_command_line('mkdir -p '//trim(win_dir)//' '//trim(cur_dir))

    call process_winds(cfg, g, atm_u, atm_v, atm_t, var_u, var_v, var_t, win_dir)
    call process_currents(cfg, g, cur_dir, nan)
  end do

contains

  subroutine resolve_atm(cfg, fu, fv, ft, vu, vv, vt)
    type(config_t),    intent(in)  :: cfg
    character(len=*),  intent(out) :: fu, fv, ft, vu, vv, vt
    if (len_trim(cfg%atm_file) > 0) then
      fu = trim(cfg%input_dir)//'/'//trim(cfg%atm_file)
      fv = fu; ft = fu
      vu = 'u10m'; vv = 'v10m'; vt = 'tp2m'
    else
      if (len_trim(cfg%atm_uas) == 0 .or. len_trim(cfg%atm_vas) == 0 .or. &
          len_trim(cfg%atm_tas) == 0) then
        stop 'ERRO: atm_file vazio exige atm_uas, atm_vas e atm_tas no namelist'
      end if
      fu = trim(cfg%input_dir)//'/'//trim(cfg%atm_uas)
      fv = trim(cfg%input_dir)//'/'//trim(cfg%atm_vas)
      ft = trim(cfg%input_dir)//'/'//trim(cfg%atm_tas)
      vu = 'uas'; vv = 'vas'; vt = 'tas'
    end if
  end subroutine resolve_atm

  subroutine process_winds(cfg, g, fu, fv, ft, vu, vv, vt, out_dir)
    type(config_t),   intent(in) :: cfg
    type(grid_t),     intent(in) :: g
    character(len=*), intent(in) :: fu, fv, ft, vu, vv, vt, out_dir
    real(dp), allocatable :: xu(:), yu(:), xv(:), yv(:), xt(:), yt(:)
    real(sp), allocatable :: du(:,:), dv(:,:), dt(:,:)
    real(sp), allocatable :: uo(:), vo(:), to(:)
    character(len=256) :: units
    character(len=8)   :: date_str
    character(len=2)   :: hh_str
    integer :: nt, t

    nt = nc_ntime(fu)
    if (cfg%n_timesteps > 0) nt = min(nt, cfg%n_timesteps)
    allocate(uo(g%npoints), vo(g%npoints), to(g%npoints))

    do t = 1, nt
      call read_field2d(fu, vu, t, xu, yu, du, units)
      call read_field2d(fv, vv, t, xv, yv, dv, units)
      call read_field2d(ft, vt, t, xt, yt, dt, units)
      call kelvin_to_celsius(dt)

      call bilinear(xu, yu, du, g%lon, g%lat, uo)
      call bilinear(xv, yv, dv, g%lon, g%lat, vo)
      call bilinear(xt, yt, dt, g%lon, g%lat, to)

      call add_hours(cfg%start_date, t - 1, date_str, hh_str)
      out_path = trim(out_dir)//'/winds_'//date_str//'_'//hh_str//'.txt'
      call write_winds(trim(out_path), g, uo, vo, to, cfg%fill_value)
      write(*,*) '[winds] ', trim(g%name), ' t=', t, ' -> ', trim(out_path)

      deallocate(xu, yu, du, xv, yv, dv, xt, yt, dt)
    end do
    deallocate(uo, vo, to)
  end subroutine process_winds

  subroutine process_currents(cfg, g, out_dir, nan)
    type(config_t),   intent(in) :: cfg
    type(grid_t),     intent(in) :: g
    character(len=*), intent(in) :: out_dir
    real(sp),         intent(in) :: nan
    real(dp), allocatable :: xu(:), yu(:), zu(:), xv(:), yv(:), zv(:)
    real(dp), allocatable :: xt(:), yt(:), zt(:), xs(:), ys(:), zs(:)
    real(sp), allocatable :: raw(:,:,:)
    real(sp), allocatable :: pu(:,:,:), pv(:,:,:), pt(:,:,:), ps(:,:,:)
    real(sp), allocatable :: uc(:), vc(:), tc(:), sc(:), wc(:)
    character(len=256) :: units, fu, fv, ft, fs
    character(len=8)   :: date_str
    character(len=2)   :: hh_str
    integer :: nt, t, k, p, unit

    fu = trim(cfg%input_dir)//'/'//trim(cfg%uo_file)
    fv = trim(cfg%input_dir)//'/'//trim(cfg%vo_file)
    ft = trim(cfg%input_dir)//'/'//trim(cfg%thetao_file)
    fs = trim(cfg%input_dir)//'/'//trim(cfg%so_file)

    nt = nc_ntime(ft)
    if (cfg%n_timesteps > 0) nt = min(nt, cfg%n_timesteps)
    allocate(uc(g%npoints), vc(g%npoints), tc(g%npoints), sc(g%npoints), &
         wc(g%npoints))

    do t = 1, nt
      call read_field3d(fu, 'uo', t, xu, yu, zu, raw, units)
      allocate(pu(size(xu), size(yu), cfg%nz))
      call vinterp_levels(zu, raw, cfg%z_levels(1:cfg%nz), pu)
      deallocate(raw)

      call read_field3d(fv, 'vo', t, xv, yv, zv, raw, units)
      allocate(pv(size(xv), size(yv), cfg%nz))
      call vinterp_levels(zv, raw, cfg%z_levels(1:cfg%nz), pv)
      deallocate(raw)

      call read_field3d(ft, 'thetao', t, xt, yt, zt, raw, units)
      allocate(pt(size(xt), size(yt), cfg%nz))
      call vinterp_levels(zt, raw, cfg%z_levels(1:cfg%nz), pt)
      deallocate(raw)

      call read_field3d(fs, 'so', t, xs, ys, zs, raw, units)
      allocate(ps(size(xs), size(ys), cfg%nz))
      call vinterp_levels(zs, raw, cfg%z_levels(1:cfg%nz), ps)
      deallocate(raw)

      call add_hours(cfg%start_date, t - 1, date_str, hh_str)
      out_path = trim(out_dir)//'/currents_'//date_str//'_'//hh_str//'.txt'
      call open_currents(trim(out_path), unit)

      do k = 1, cfg%nz
        call bilinear(xu, yu, pu(:,:,k), g%lon, g%lat, uc)
        call bilinear(xv, yv, pv(:,:,k), g%lon, g%lat, vc)
        call bilinear(xt, yt, pt(:,:,k), g%lon, g%lat, tc)
        call bilinear(xs, ys, ps(:,:,k), g%lon, g%lat, sc)
        do p = 1, g%npoints
          if (ieee_is_nan(tc(p)) .or. g%landmask(p) == 0) then
            uc(p) = nan; vc(p) = nan; tc(p) = nan; sc(p) = nan; wc(p) = nan
          else
            wc(p) = 0.0_sp
          end if
        end do
        call write_current_level(unit, g, k, cfg%z_levels(k), uc, vc, wc, tc, sc, &
             cfg%fill_value)
      end do

      call close_currents(unit)
      write(*,*) '[currents] ', trim(g%name), ' t=', t, ' -> ', trim(out_path)
      deallocate(pu, pv, pt, ps)
      deallocate(xu, yu, zu, xv, yv, zv, xt, yt, zt, xs, ys, zs)
    end do
    deallocate(uc, vc, tc, sc, wc)
  end subroutine process_currents

end program nc2txt
