
program swim

  use time, only : time_process_years


  implicit none

  ! Version string, the only place this is defined
  character(*), parameter :: version = "v2022.1"
  ! Modules in order of dependence
  character(*), dimension(19), parameter :: swim_modules = (/ &
    ! Space-time modules
    "time                 ", &  ! 1
    "hydrotope            ", &  ! 2
    "subbasin             ", &  ! 3
    "catchment            ", &  ! 4
    ! process modules
    "evapotranspiration   ", &  ! 5
    "soil                 ", &  ! 6
    "groundwater          ", &  ! 7
    "reservoir            ", &  ! 8
    "vegetation           ", &  ! 9
    "crop                 ", &  ! 10
    "landuse              ", &  ! 11
    "snow                 ", &  ! 12
    "nutrient             ", &  ! 13
    "management           ", &  ! 14
    "river                ", &  ! 15
    "erosion              ", &  ! 16
    ! utility modules
    "output               ", &  ! 17
    "input                ", &  ! 18
    "utilities            "  &  ! 19
  /)

  call initialise
  call time_process_years
  call terminate

contains

  subroutine initialise
    use catchment, only : &
      bSubcatch, &
      catchment_id, &
      catchment_initialise, &
      catchment_initialise_parameters, &
      da
    use crop, only : crop_initialise, crop_read_input, mcrdb
    use erosion, only : cvm, erosion_initialise
    use evapotranspiration, only : evapotranspiration_initialise, lat
    use groundwater, only : groundwater_correct_params, groundwater_initialise
    use hydrotope, only : &
      frar, &
      hydrotope_area, &
      hydrotope_ids, &
      hydrotope_initialise, &
      hydrotope_input_file_id, &
      hydrotope_subbasin_id, &
      meap, &
      mstruc
    use input, only : &
      get_config_fid, &
      input_initialise, &
#ifdef with_netcdf
      input_nc_initialise, &
#endif
      parse_commandline_arguments, &
      print_splash
    use landuse, only : LULC, landuse_initialise, nlut
    use management, only : management_initialise
    use nutrient, only : &
      bn1, &
      bn2, &
      bn3, &
      bnu1, &
      bnu2, &
      bp1, &
      bp2, &
      bp3, &
      bpu1, &
      bpu2, &
      nutrient_initialise
    use output, only : &
      output_initialise, &
      output_initialise_user_input, &
      output_print_variables, &
      output_dir
    use reservoir, only : reservoir_initialise
    use river, only : &
      chd, &
      chl, &
      chn, &
      chnn, &
      chs, &
      chss, &
      ovn, &
      river_initialise, &
      river_initialise_travel_time
    use snow, only : snow_initialise
    use soil, only : al, amp, avt, ffc, ml, ms, soil_initialise, tc, wft, wi
    use subbasin, only : &
      flu, &
      icodes, &
      inum1s, &
      mb, &
      mch, &
      mhyd, &
      neap, &
      sbar, &
      slope_length, &
      stp, &
      subbasin_ids, &
      subbasin_initialise, &
      subbasin_initialise_area, &
      subbasin_initialise_weather_gen, &
      subbasin_input_file_id, &
      subcatch_id, &
      subcatch_idx, &
      tp5, &
      tp6
    use time, only : iyr, nbyr, nc, ndayssim, time_initialise
    use utilities, only : log_info, identifier_max_length
    use vegetation, only : vegetation_initialise

    character(len=identifier_max_length) :: print_defaults
    logical :: print_output_variables

    call parse_commandline_arguments(version, print_output_variables, print_defaults)

    if (trim(print_defaults) /= '') call print_module_parameters(print_defaults)

    call print_splash(version)

    ! Utility modules
    call input_initialise
    call output_initialise_user_input

    ! Space-time modules
    call time_initialise
    call subbasin_initialise
    call hydrotope_initialise(mb, neap, sbar)
    call catchment_initialise(sbar, flu, subcatch_id, subcatch_idx)

    ! Process modules
    call landuse_initialise
    call river_initialise(mb, mch, mhyd, subbasin_input_file_id)
    call soil_initialise(mb, meap, mstruc, neap, bSubcatch, da, slope_length, stp, ovn, chs, flu, chn, chl, LULC%cn2a, LULC%cn2b, LULC%cn2c, LULC%cn2d)
    call crop_initialise(iyr, mb, meap, ms, nbyr)
    call erosion_initialise(mb, mcrdb, meap, subbasin_input_file_id, da, flu, stp, slope_length, tc, tp6, tp5, al, chl, chnn, chd, chss)
    call nutrient_initialise(mb, mcrdb, meap, ml)
    call evapotranspiration_initialise(mb, meap, subbasin_input_file_id)
    call vegetation_initialise(mb, nlut, mcrdb, meap, lat)
    call crop_read_input(bn1, bn2, bn3, bnu1, bnu2, bp1, bp2, bp3, bpu1, bpu2, cvm)
    call groundwater_initialise(mb, subbasin_input_file_id)
    call reservoir_initialise(icodes, inum1s, mb, mhyd)
    call snow_initialise(mb, meap, subbasin_input_file_id, mstruc)
    call management_initialise(get_config_fid(), frar, icodes, inum1s, iyr, &
      mb, mhyd, mstruc, ndayssim, nbyr, neap, sbar, output_dir)

    ! Preprocessing that depends on many modules input data (could likely be detangled)
    call catchment_initialise_parameters(mb, nbyr, sbar, subcatch_idx)
    call river_initialise_travel_time(mb)
    call subbasin_initialise_area(da)
    call groundwater_correct_params
    call subbasin_initialise_weather_gen(amp, avt, ffc, lat, nc, wft, wi)

    ! open ncdf climate files and initialise module
#ifdef with_netcdf
    call input_nc_initialise(iyr, mb)
#endif

    call output_initialise( &
      hydrotope_ids, hydrotope_area, hydrotope_subbasin_id, subbasin_ids, neap, &
      subcatch_id, catchment_id, hydrotope_input_file_id, subbasin_input_file_id)

    call log_info('swim', 'MAX number of soil layers =', int=ml)
    call log_info('swim', 'MAX number of simulation years =', int=nbyr)
    call log_info('swim', 'Number of years of simulation =', int=nbyr)
    call log_info('swim', 'First year =', int=iyr)
    call log_info('swim', 'Number of subbasins =', int=mb)
    call log_info('swim', 'Number of soil types =', int=ms)
    call log_info('swim', 'Number of crops in crop DB =', int=mcrdb)
    call log_info('swim', 'Number of land use types =', int=nlut)
    call log_info('swim', 'Program codes & Routing structure files - READ!')

    if (print_output_variables) call output_print_variables

  end subroutine initialise

  subroutine terminate
    use input, only : &
#ifdef with_netcdf
      input_nc_close, &
#endif
      get_config_fid
    use management, only : bWAM_Module, management_write_user_output
    use subbasin, only : mb
    use time, only : nDaysSim
    use catchment, only : dealloc_catchment
    use crop, only : crop_deallocate
    use erosion, only : dealloc_erosion
    use evapotranspiration, only : dealloc_evapotranspiration
    use groundwater, only : dealloc_groundwater
    use hydrotope, only : dealloc_hydrotope
    use nutrient, only : dealloc_nutrient
    use output, only : dealloc_output
    use reservoir, only : dealloc_reservoir
    use river, only : dealloc_river
    use snow, only : dealloc_snow
    use soil, only : dealloc_soil
    use subbasin, only : subbasin_close
    use vegetation, only : dealloc_vegetation

    if (bWAM_Module) call management_write_user_output(mb, ndayssim)

    close(get_config_fid())

    call dealloc_catchment
    call crop_deallocate
    call dealloc_erosion
    call dealloc_evapotranspiration
    call dealloc_groundwater
    call dealloc_hydrotope
    call dealloc_nutrient
    call dealloc_output
    call dealloc_reservoir
    call dealloc_river
    call dealloc_snow
    call dealloc_soil
    call subbasin_close
    call dealloc_vegetation

    !**** Close netcdf files
#ifdef with_netcdf
    call input_nc_close
#endif

    return
  end subroutine terminate

  subroutine print_module_parameters(module_name)
    ! Print modules' current parameters
    ! This subroutine has to be placed in the SWIM program unit as
    ! namelists cant be parsed as arguments
    use time, only : time_parameters
    use hydrotope, only : hydrotope_parameters
    use subbasin, only : subbasin_parameters
    use catchment, only : catchment_parameters
    use evapotranspiration, only : evapotranspiration_parameters
    use soil, only : soil_parameters
    use groundwater, only : groundwater_parameters
    use reservoir, only : reservoir_parameters
    use vegetation, only : vegetation_parameters
    use crop, only : crop_parameters
    use landuse, only : landuse_parameters
    use snow, only : snow_parameters
    use nutrient, only : nutrient_parameters
    use management, only : management_parameters
    use river, only : river_parameters
    use erosion, only : erosion_parameters
    use input, only : &
      input_parameters, &
#ifdef with_netcdf
      nc_climate_parameters, &
#endif
      print_help
    use output, only : output_parameters

    character(len=*), intent(in) :: module_name
    logical :: pparams(size(swim_modules))

    pparams = swim_modules == module_name
    if (module_name == "all") pparams(:) = .True.
    if (.not. any(pparams)) &
      call print_help("Not a valid module name in -d "//trim(module_name))

    if (pparams(1)) write(*, nml=time_parameters)
    if (pparams(2)) write(*, nml=hydrotope_parameters)
    if (pparams(3)) write(*, nml=subbasin_parameters)
    if (pparams(4)) write(*, nml=catchment_parameters)
    if (pparams(5)) write(*, nml=evapotranspiration_parameters)
    if (pparams(6)) write(*, nml=soil_parameters)
    if (pparams(7)) write(*, nml=groundwater_parameters)
    if (pparams(8)) write(*, nml=reservoir_parameters)
    if (pparams(9)) write(*, nml=vegetation_parameters)
    if (pparams(10)) write(*, nml=crop_parameters)
    if (pparams(11)) write(*, nml=landuse_parameters)
    if (pparams(12)) write(*, nml=snow_parameters)
    if (pparams(13)) write(*, nml=nutrient_parameters)
    if (pparams(14)) write(*, nml=management_parameters)
    if (pparams(15)) write(*, nml=river_parameters)
    if (pparams(16)) write(*, nml=erosion_parameters)
    if (pparams(17)) write(*, nml=output_parameters)
    if (pparams(18)) then
      write(*, nml=input_parameters)
#ifdef with_netcdf
      write(*, nml=nc_climate_parameters)
#endif
    end if
    stop

  end subroutine print_module_parameters

end program swim
