!======================================================================
!!
!! Routine for dealing with warnings that may crop up many times and 
!! which should be output only a limited number of times
!!
!! In f95 could have used a special type which was preinitialised...
!!
!! $Id: warnings_and_errors.f90,v 1.6 2004/02/26 19:02:19 salam Exp $
!======================================================================
module warnings_and_errors
  use types
  implicit none
  private

  integer, parameter :: base = 10000
  integer            :: n_warn_sources   = 0
  integer, parameter, public :: warn_id_INIT=0
  integer, parameter, public :: default_max_warn = 5

  interface wae_warn
     module procedure wae_warn_new, wae_warn_old
  end interface
  
  public :: wae_warn, wae_error, wae_setunit

  integer, parameter :: stddev_in = 0
  integer            :: stddev = stddev_in


contains
  !---------------------------------------------------------------------
  !! Routine to allow the output of a warning up to some maximum number of 
  !! times after which no such warning will be issues.
  !!
  !! On the first call for a given kind of warning, the warn_n should
  !! (must have save attribute in calling routine) should be the 
  !! maximum number of warnings that will be output
  !!
  subroutine wae_warn_new(warn_n, text, text2, text3, text4, intval, dbleval)
    integer, intent(inout)       :: warn_n
    character(len=*), intent(in) :: text
    character(len=*), intent(in), optional :: text2
    character(len=*), intent(in), optional :: text3
    character(len=*), intent(in), optional :: text4
    integer,          intent(in), optional :: intval
    real(kind(1d0)),  intent(in), optional :: dbleval
    !--------------------------------------

    if (warn_n > 0) then
       warn_n = warn_n - 1
       write(stddev,'(a)', advance='no') 'WARNING in '
       write(stddev,'(a)')  text
       if (present(text2)) write(stddev,'(a)') text2
       if (present(text3)) write(stddev,'(a)') text3
       if (present(text4)) write(stddev,'(a)') text4
       if (present(intval))  write(stddev,*) intval
       if (present(dbleval)) write(stddev,*) dbleval
       if (warn_n == 0) write(stddev,'(a)') &
            &'----- No more such warnings will be issued ------'
    end if
  end subroutine wae_warn_new


  !---------------------------------------------------------------------
  !! Routine to allow the output of a warning up to some maximum number of 
  !! times after which no such warning will be issues.
  !!
  !! On the first call for a given kind of warning, the warn_id should be 
  !! equal to warn_id_INIT; warn_id should have the SAVE attribute to
  !! allow wae_warn to keep track of the number of warnings of this type
  !!
  subroutine wae_warn_old(max_warn, warn_id, &
       &text, text2, text3, intval, dbleval)
    integer, intent(in)          :: max_warn
    integer, intent(inout)       :: warn_id
    character(len=*), intent(in) :: text
    character(len=*), intent(in), optional :: text2
    character(len=*), intent(in), optional :: text3
    integer,          intent(in), optional :: intval
    real(kind(1d0)),  intent(in), optional :: dbleval
    !--------------------------------------
    integer :: warn_index, nwarn

    !-- generate a new warn_id
    if (warn_id <= 0) then
       n_warn_sources = n_warn_sources + 1
       warn_id = n_warn_sources * base
    end if
    
    warn_index = warn_id / base
    nwarn      = warn_id - warn_index*base

    if (nwarn < max_warn) then
       if (max_warn > base-2) call wae_error('wae_warn',&
            & 'max_warn exceeded maximum allowed value; message was', text)
       !-- does this make any sense at all (GPS 8/1/03)?
       if (warn_id > huge(warn_id)) call wae_error('wae_warn',&
            & 'exceeded max capicity for distinct warnings; message was', text)
    
       warn_id = warn_id + 1
       write(stddev,'(a)', advance='no') 'WARNING in '
       write(stddev,'(a)')  text
       if (present(text2)) write(stddev,'(a)') text2
       if (present(text3)) write(stddev,'(a)') text3
       if (present(intval))  write(stddev,*) intval
       if (present(dbleval)) write(stddev,*) dbleval
       !-- if there is only an 1 warning to be written then
       !   avoid cluttering screen with too many messages
       if (nwarn == max_warn - 1 .and. max_warn > 1) write(stddev,'(a)') &
            &'----- No more such warnings will be issued ------'
    end if
  end subroutine wae_warn_old

  !======================================================================
  !! Report an error and then crash (by attempting floating point exception)
  !======================================================================
  subroutine wae_error(text1, text2, text3, text4, intval, dbleval)
    character(len=*), intent(in) :: text1
    character(len=*), intent(in), optional :: text2
    character(len=*), intent(in), optional :: text3
    character(len=*), intent(in), optional :: text4
    integer,          intent(in), optional :: intval
    real(kind(1d0)),  intent(in), optional :: dbleval
    !real :: a,b

    write(stddev,*)
    write(stddev,'(a)') '============================================================='
    write(stddev,'(a)', advance='no') 'FATAL ERROR in '
    write(stddev,'(a)') text1
    if (present(text2))   write(stddev,'(a)') text2
    if (present(text3))   write(stddev,'(a)') text3
    if (present(text4))   write(stddev,'(a)') text4
    if (present(intval))  write(stddev,*) intval
    if (present(dbleval)) write(stddev,*) dbleval

    write(stddev,*)
    error stop 1
  end subroutine wae_error
  
  !!
  !! Set the unit for all output by the warning and error routines
  !!
  subroutine wae_setunit(unit)
    integer, intent(in) :: unit
    stddev = unit
  end subroutine wae_setunit
  
end module warnings_and_errors

