module cubeadm_timing
  use gkernel_types
  use gkernel_interfaces
  use cubetools_parameters
  use cubedag_messaging
  !---------------------------------------------------------------------
  ! Module to manage timing of commands
  ! FixME: rename cubeadm_* symbols to cubedag_* everywhere
  !---------------------------------------------------------------------
  !
  public :: cubeadm_timing_init, cubeadm_timing_prepro2process, cubeadm_timing_process2postpro
  public :: cubeadm_timing_final, cubeadm_timing_collect, cubeadm_timing_variables
  public :: gag_cputime_add
  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
    type(cputime_t) :: trans    ! Transpositions (from memory-or-disk to memory-or-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(timing%trans)
    !
    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(ioread,iowrit,trans)
    !---------------------------------------------------------------------
    !
    !---------------------------------------------------------------------
    real(kind=8), intent(in) :: ioread,iowrit,trans
    !
    timing%ioread%curr%elapsed = timing%ioread%curr%elapsed + ioread
    timing%iowrit%curr%elapsed = timing%iowrit%curr%elapsed + iowrit
    timing%trans%curr%elapsed  = timing%trans%curr%elapsed  + trans
  end subroutine cubeadm_timing_collect
  !
  subroutine cubeadm_timing_feedback(rname)
    use cubetools_format
    use cubetools_setup_types
    !---------------------------------------------------------------------
    !
    !---------------------------------------------------------------------
    character(len=*), intent(in) :: rname
    ! Local
    character(len=message_length) :: mess
    type(cube_setup_api_t) :: setup
    logical :: showtiming
    !
    ! Show only if requested
    call setup%timing%command%get(showtiming)
    if (.not.showtiming)  return
    !
    print *,''
    !
    write(mess,'(A)')  '--- Total ------------------------------------------------'
    call cubedag_message(seve%r,rname,mess)
    !
    write(mess,10)  &
      'Time elapsed on command '//trim(rname)//':',cubetools_format_time(timing%total%diff%elapsed)
    call cubedag_message(seve%r,rname,mess)
    !
    write(mess,'(A)')  '--- Subdivision per processing parts ---------------------'
    call cubedag_message(seve%r,rname,mess)
    !
    write(mess,10)  &
      'Time spent in preprocessing:',cubetools_format_time(timing%prepro%curr%elapsed)
    call cubedag_message(seve%r,rname,mess)
    !
    write(mess,10)  &
      'Time spent processing the data:',cubetools_format_time(timing%process%curr%elapsed)
    call cubedag_message(seve%r,rname,mess)
    !
    write(mess,10)  &
      'Time spent in postprocessing:',cubetools_format_time(timing%postpro%curr%elapsed)
    call cubedag_message(seve%r,rname,mess)
    !
    write(mess,'(A)')  '--- Subdivision per disk access --------------------------'
    call cubedag_message(seve%r,rname,mess)
    !
    write(mess,10)  &
      'Time elapsed reading cube(s):',cubetools_format_time(timing%ioread%curr%elapsed)
    call cubedag_message(seve%r,rname,mess)
    !
    write(mess,10)  &
      'Time elapsed writing cube(s):',cubetools_format_time(timing%iowrit%curr%elapsed)
    call cubedag_message(seve%r,rname,mess)
    !
    write(mess,'(A)')  '--- Transpositions ---------------------------------------'
    call cubedag_message(seve%r,rname,mess)
    !
    write(mess,10)  &
      'Time elapsed transposing cube(s):',cubetools_format_time(timing%trans%curr%elapsed)
    call cubedag_message(seve%r,rname,mess)
    !
    write(mess,'(A)')  '----------------------------------------------------------'
    call cubedag_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
  !
  subroutine gag_cputime_add(sum,last)
    !---------------------------------------------------------------------
    !   Get the times since last call for 'last', and add the times to
    ! the structure 'sum'. Note that the reference times in 'sum' have
    ! not much meaning in this context.
    ! ---
    !   This subroutine is helpful to add non-contiguous time counts,
    ! while the official cputime_t is made to count times from a reference
    ! or from last call. Should this be merged in gsys?
    !---------------------------------------------------------------------
    type(cputime_t), intent(inout) :: sum
    type(cputime_t), intent(inout) :: last
    !
    call gag_cputime_get(last)
    sum%curr%elapsed = sum%curr%elapsed + last%diff%elapsed
    sum%curr%user    = sum%curr%user    + last%diff%user
    sum%curr%system  = sum%curr%system  + last%diff%system
  end subroutine gag_cputime_add
  !
end module cubeadm_timing
