! Copyright (c) 2024-2025, The Regents of the University of California and Sourcery Institute
! Terms of use are as specified in LICENSE.txt

#include "julienne-assert-macros.h"
#include "language-support.F90"

submodule(julienne_test_result_m) julienne_test_result_s
#if ASSERTIONS
  use julienne_assert_m, only : call_julienne_assert_
#endif
  use julienne_multi_image_m, only : internal_this_image, internal_num_images, internal_sync_all, internal_co_sum_integer
  implicit none

contains

    module procedure construct_from_string
      test_result%description_ = description
      if (present(diagnosis)) test_result%diagnosis_ = diagnosis
    end procedure

    module procedure construct_from_character
      test_result%description_ = description
      if (present(diagnosis)) test_result%diagnosis_ = diagnosis
    end procedure

#if HAVE_MULTI_IMAGE_SUPPORT

    module procedure co_characterize

      logical i_passed
      integer, parameter :: skips=1, passes=2
      integer tally(skips:passes)
      character(len=*), parameter :: indent = "   "

      associate(i_skipped => .not. allocated(self%diagnosis_))

        if (i_skipped) then
          i_passed = .false.
        else
          i_passed = self%diagnosis_%test_passed()
        end if

        tally = [merge(1,0,i_skipped), merge(1,0,i_passed)]
        call internal_co_sum_integer(tally)

        associate(me => internal_this_image(), images => internal_num_images(), &
                  images_skipped => tally(skips), images_passed => tally(passes))
          call_julienne_assert(any(images_skipped == [0,images]))
          if (i_skipped) then
            if (me==1) print '(a)', indent // "SKIPS  on " // trim(self%description_%string()) // "."
          else
            if (images_passed < images .and. i_passed) then
              ! a failure on any image becomes a failure on all images
              self%diagnosis_ = test_diagnosis_t(test_passed=.false., diagnostics_string="peer image failure")
            end if
            if (me==1) print '(a)', indent // merge("passes on ", "FAILS  on ", self%diagnosis_%test_passed()) // trim(self%description_%string()) // "."
#if ! ASYNCHRONOUS_DIAGNOSTICS
            call internal_sync_all ! ensure image 1 prints test outcome before any failure diagnostics print
#endif
            if (.not. i_passed) then
              associate(image => string_t(me))
                print '(a)', indent // indent // "diagnostics on image " // image%string() // ": " // self%diagnosis_%diagnostics_string()
              end associate
            end if
#if ! ASYNCHRONOUS_DIAGNOSTICS
            call internal_sync_all ! ensure all images print failure diagnostics, if any, for a given test before any image moves on to the next test
#endif
          end if
        end associate
      end associate
    end procedure

#else

    module procedure co_characterize

      character(len=*), parameter :: indent = "   "

      if (.not. allocated(self%diagnosis_)) then
        print '(a)', indent // "SKIPS  on " // trim(self%description_%string()) // "."
      else
        associate(test_passed => self%diagnosis_%test_passed())
          print '(a)', indent // merge("passes on ", "FAILS  on ", test_passed) // trim(self%description_%string()) // "."
          if (.not. test_passed) print '(a)', indent //indent // "diagnostics: " // self%diagnosis_%diagnostics_string()
        end associate
      end if
    end procedure
#endif

    module procedure passed
      if (.not. allocated(self%diagnosis_)) then
        test_passed = .false.
      else
        test_passed = self%diagnosis_%test_passed()
      end if
    end procedure

    module procedure skipped
      test_skipped = .not. allocated(self%diagnosis_)
    end procedure

    module procedure description_contains_string
      substring_found = self%description_contains_characters(substring%string())
    end procedure

    module procedure description_contains_characters
      substring_found = index(self%description_%string(), substring) /= 0
    end procedure

end submodule julienne_test_result_s
