module cubeadm_timing
  use gkernel_types
  use gkernel_interfaces
  use cube_types
  use cubeio_timing
  !
  public :: cubeadm_timing_init, cubeadm_timing_prepro2process, cubeadm_timing_process2postpro
  public :: cubeadm_timing_final, cubeadm_timing_collect_io, cubeadm_timing_variables
  private
  !
  type command_timing_t
    type(cputime_t) :: total    ! Total execution time
    type(cputime_t) :: prepro   !
    type(cputime_t) :: process  ! Time spent processing the data
    type(cputime_t) :: postpro  !
    type(cputime_t) :: ioread   ! Reading data from disk
    type(cputime_t) :: iowrit   ! Writing data to disk
  end type command_timing_t
  !
  type(command_timing_t) :: timing  ! Main structure
  type(cputime_t) :: diff  ! Structure holding the time diff since last call
  !
contains
  !
  subroutine cubeadm_timing_init()
    !---------------------------------------------------------------------
    ! (Re)initialize the internal timing structure.
    !---------------------------------------------------------------------
    !
    call gag_cputime_init(timing%total)
    call gag_cputime_init(timing%prepro)
    call gag_cputime_init(timing%process)
    call gag_cputime_init(timing%postpro)
    call gag_cputime_init(timing%ioread)
    call gag_cputime_init(timing%iowrit)
    !
    call gag_cputime_init(diff)
  end subroutine cubeadm_timing_init
  !
  subroutine cubeadm_timing_prepro2process()
    !---------------------------------------------------------------------
    ! Log the preprocessing time since last call to init
    !---------------------------------------------------------------------
    call gag_cputime_add(timing%prepro,diff)
  end subroutine cubeadm_timing_prepro2process
  !
  subroutine cubeadm_timing_process2postpro()
    !---------------------------------------------------------------------
    ! Log the processing time since last call to preprocessing
    !---------------------------------------------------------------------
    call gag_cputime_add(timing%process,diff)
  end subroutine cubeadm_timing_process2postpro
  !
  subroutine cubeadm_timing_final(caller)
    !---------------------------------------------------------------------
    ! Finalize the internal timing structure.
    !---------------------------------------------------------------------
    character(len=*), intent(in) :: caller
    !
    ! Log the postprocessing time since last call to processing
    call gag_cputime_add(timing%postpro,diff)
    !
    ! Get total time for the command
    call gag_cputime_get(timing%total)
    !
    ! Feedback
    call cubeadm_timing_feedback(caller)
  end subroutine cubeadm_timing_final
  !
  subroutine cubeadm_timing_collect_io(cub)
    !---------------------------------------------------------------------
    !
    !---------------------------------------------------------------------
    type(cube_t), intent(in) :: cub
    !
    ! ZZZ Should loop on cubeio_naccess
    timing%ioread%curr%elapsed = timing%ioread%curr%elapsed +  &
                                 cub%tuple%cube(1)%time%read%curr%elapsed + &
                                 cub%tuple%cube(2)%time%read%curr%elapsed
    timing%iowrit%curr%elapsed = timing%iowrit%curr%elapsed +  &
                                 cub%tuple%cube(1)%time%writ%curr%elapsed + &
                                 cub%tuple%cube(2)%time%writ%curr%elapsed
  end subroutine cubeadm_timing_collect_io
  !
  subroutine cubeadm_timing_feedback(rname)
    use cubetools_format
    use cubeadm_messaging
    use cubeadm_setup
    !---------------------------------------------------------------------
    !
    !---------------------------------------------------------------------
    character(len=*), intent(in) :: rname
    ! Local
    character(len=message_length) :: mess
    !
    ! Show only if requested
    if (.not.cubset%timing%command)  return
    !
    print *,''
    !
    write(mess,'(A)')  '--- Total ------------------------------------------------'
    call cubeadm_message(seve%r,rname,mess)
    !
    write(mess,10)  &
      'Time elapsed on command '//trim(rname)//':',cubetools_format_time(timing%total%diff%elapsed)
    call cubeadm_message(seve%r,rname,mess)
    !
    write(mess,'(A)')  '--- Subdivision per processing parts ---------------------'
    call cubeadm_message(seve%r,rname,mess)
    !
    write(mess,10)  &
      'Time spent in preprocessing:',cubetools_format_time(timing%prepro%curr%elapsed)
    call cubeadm_message(seve%r,rname,mess)
    !
    write(mess,10)  &
      'Time spent processing the data:',cubetools_format_time(timing%process%curr%elapsed)
    call cubeadm_message(seve%r,rname,mess)
    !
    write(mess,10)  &
      'Time spent in postprocessing:',cubetools_format_time(timing%postpro%curr%elapsed)
    call cubeadm_message(seve%r,rname,mess)
    !
    write(mess,'(A)')  '--- Subdivision per IO kind ------------------------------'
    call cubeadm_message(seve%r,rname,mess)
    !
    write(mess,10)  &
      'Time elapsed reading cube(s):',cubetools_format_time(timing%ioread%curr%elapsed)
    call cubeadm_message(seve%r,rname,mess)
    !
    write(mess,10)  &
      'Time elapsed writing cube(s):',cubetools_format_time(timing%iowrit%curr%elapsed)
    call cubeadm_message(seve%r,rname,mess)
    !
    write(mess,'(A)')  '----------------------------------------------------------'
    call cubeadm_message(seve%r,rname,mess)
    !
  10 format(A,T49,A)
  end subroutine cubeadm_timing_feedback
  !
  subroutine cubeadm_timing_variables(error)
    !-------------------------------------------------------------------
    ! (Re)create the structure TIMING%
    !-------------------------------------------------------------------
    logical, intent(inout) :: error
    !
    call sic_defstructure('TIMING%',.true.,error)
    if (error) return
    ! System times
    call sic_def_dble('TIMING%TOTAL',timing%total%diff%elapsed,0,1,.true.,error)
    call sic_def_dble('TIMING%PREPRO',timing%prepro%curr%elapsed,0,1,.true.,error)
    call sic_def_dble('TIMING%PROCESS',timing%process%curr%elapsed,0,1,.true.,error)
    call sic_def_dble('TIMING%POSTPRO',timing%postpro%curr%elapsed,0,1,.true.,error)
    call sic_def_dble('TIMING%IOREAD',timing%ioread%curr%elapsed,0,1,.true.,error)
    call sic_def_dble('TIMING%IOWRIT',timing%iowrit%curr%elapsed,0,1,.true.,error)
    if (error) return
  end subroutine cubeadm_timing_variables
  !
end module cubeadm_timing
