! $Id$
!
! Earth System Modeling Framework
! Copyright (c) 2002-2025, University Corporation for Atmospheric Research,
! Massachusetts Institute of Technology, Geophysical Fluid Dynamics
! Laboratory, University of Michigan, National Centers for Environmental
! Prediction, Los Alamos National Laboratory, Argonne National Laboratory,
! NASA Goddard Space Flight Center.
! Licensed under the University of Illinois-NCSA License.
!
!==============================================================================
#define ESMF_FILENAME "ESMF_ArraySpec.F90"
!==============================================================================
!
! ESMF ArraySpec Module
module ESMF_ArraySpecMod
!
!==============================================================================
!
! This file contains the ArraySpec class definition and all ArraySpec
! class methods.
!
!------------------------------------------------------------------------------
! INCLUDES
#include "ESMF.h"

!==============================================================================
!BOPI
! !MODULE: ESMF_ArraySpecMod - Manage data arrays uniformly between F90 and C++
!
! !DESCRIPTION:
!
! The code in this file implements the {\tt ESMF\_ArraySpec} class and
! associated functions and subroutines.
!
! C and C++ arrays are simple pointers to memory.
! Fortran arrays contain shape and stride definitions and are strongly
! typed. To enable interoperability between the languages the C++ code
! must be able to obtain this information from the Fortran description
! (which is called the "dope vector" in Fortran), either through a priori
! knowledge or through query.
!
!------------------------------------------------------------------------------
! !USES:
  use ESMF_UtilTypesMod     ! ESMF utility types
  use ESMF_InitMacrosMod    ! ESMF initializer macros
  use ESMF_BaseMod          ! ESMF base class
  use ESMF_LogErrMod        ! ESMF error handling
  use ESMF_IOUtilMod

  implicit none

!------------------------------------------------------------------------------
! !PRIVATE TYPES:
  private

!------------------------------------------------------------------------------
! ! ESMF_ArraySpec
!
! ! Data array specification, with no associated data buffer.
  type ESMF_ArraySpec
#ifndef ESMF_NO_SEQUENCE
  sequence
#endif
    private
    integer             :: rank       ! number of dimensions
    type(ESMF_TypeKind_Flag) :: typekind   ! fortran type and kind enum/integer
    ESMF_INIT_DECLARE
  end type


!------------------------------------------------------------------------------
! !PUBLIC TYPES:
  public ESMF_ArraySpec

!------------------------------------------------------------------------------

! !PUBLIC MEMBER FUNCTIONS:

! - ESMF-public methods:
  public operator(==)
  public operator(/=)
  public ESMF_ArraySpecGet
  public ESMF_ArraySpecPrint
  public ESMF_ArraySpecSet
  public ESMF_ArraySpecValidate

! - ESMF-internal methods:
  public ESMF_ArraySpecInit
  public ESMF_ArraySpecGetInit

!EOPI

!------------------------------------------------------------------------------
! The following line turns the CVS identifier string into a printable variable.
  character(*), parameter, private :: version = &
    '$Id$'

!==============================================================================


!==============================================================================
!
! INTERFACE BLOCKS
!
!==============================================================================
!BOP
! !IROUTINE:  ESMF_ArraySpecAssignment(=) - Assign an ArraySpec to another ArraySpec
!
! !INTERFACE:
! interface assignment(=)
!   arrayspec1 = arrayspec2
!
! !ARGUMENTS:
!   type(ESMF_ArraySpec) :: arrayspec1
!   type(ESMF_ArraySpec) :: arrayspec2
!
! !STATUS:
! \begin{itemize}
! \item\apiStatusCompatibleVersion{5.2.0r}
! \end{itemize}
!
! !DESCRIPTION:
!   Set {\tt arrayspec1} equal to {\tt arrayspec2}. This is the default 
!   Fortran assignment, which creates a complete, independent copy of 
!   {\tt arrayspec2} as {\tt arrayspec1}. If {\tt arrayspec2} is an 
!   invalid {\tt ESMF\_ArraySpec} object then {\tt arrayspec1} will be 
!   equally invalid after the assignment.
!
!   The arguments are:
!   \begin{description} 
!   \item[arrayspec1] 
!     The {\tt ESMF\_ArraySpec} to be set.
!   \item[arrayspec2] 
!     The {\tt ESMF\_ArraySpec} to be copied.
!   \end{description}
!
!EOP
! !PRIVATE MEMBER FUNCTIONS:
!   None, documentation only, to describe the behavior of the default 
!   Fortran assignment(=).
!
! end interface
! 
!------------------------------------------------------------------------------
!BOP
! !IROUTINE:  ESMF_ArraySpecOperator(==) - Test if ArraySpec 1 is equal to ArraySpec 2
!
! !INTERFACE:
  interface operator(==)
!   if (arrayspec1 == arrayspec2) then ... endif
!                OR
!   result = (arrayspec1 == arrayspec2)
!
! !RETURN VALUE:
!   logical :: result
!
! !ARGUMENTS:
!   type(ESMF_ArraySpec), intent(in) :: arrayspec1
!   type(ESMF_ArraySpec), intent(in) :: arrayspec2
!
! !STATUS:
! \begin{itemize}
! \item\apiStatusCompatibleVersion{5.2.0r}
! \end{itemize}
!
! !DESCRIPTION:
!   Overloads the (==) operator for the {\tt ESMF\_ArraySpec} class to return 
!   {\tt .true.} if {\tt arrayspec1} and {\tt arrayspec2} specify the same
!   type, kind and rank, and {\tt .false.} otherwise.
!
!   The arguments are:
!   \begin{description}
!   \item[arrayspec1]
!     First {\tt ESMF\_ArraySpec} in comparison.
!   \item[arrayspec2]
!     Second {\tt ESMF\_ArraySpec} in comparison.
!   \end{description}
!
!EOP
! !PRIVATE MEMBER FUNCTIONS:
    module procedure ESMF_ArraySpecEQ   ! internal implementation
!
  end interface
!
!------------------------------------------------------------------------------
!BOP
! !IROUTINE:  ESMF_ArraySpecOperator(/=) - Test if ArraySpec 1 is not equal to ArraySpec 2
!
! !INTERFACE:
  interface operator(/=)
!   if (arrayspec1 /= arrayspec2) then ... endif
!                OR
!   result = (arrayspec1 /= arrayspec2)
!
! !RETURN VALUE:
!   logical :: result
!
! !ARGUMENTS:
!   type(ESMF_ArraySpec), intent(in) :: arrayspec1
!   type(ESMF_ArraySpec), intent(in) :: arrayspec2
!
! !STATUS:
! \begin{itemize}
! \item\apiStatusCompatibleVersion{5.2.0r}
! \end{itemize}
!
! !DESCRIPTION:
!   Overloads the (/=) operator for the {\tt ESMF\_ArraySpec} class to return 
!   {\tt .true.} if {\tt arrayspec1} and {\tt arrayspec2} do not specify the
!   same type, kind or rank, and {\tt .false.} otherwise.
!
!   The arguments are:
!   \begin{description}
!   \item[arrayspec1]
!     First {\tt ESMF\_ArraySpec} in comparison.
!   \item[arrayspec2]
!     Second {\tt ESMF\_ArraySpec} in comparison.
!   \end{description}
! 
!EOP
! !PRIVATE MEMBER FUNCTIONS:
    module procedure ESMF_ArraySpecNE   ! internal implementation
!
  end interface
!------------------------------------------------------------------------------


!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

contains

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


! -------------------------- ESMF-internal method -----------------------------
#undef  ESMF_METHOD
#define ESMF_METHOD "ESMF_ArraySpecEQ()"
!BOPI
! !IROUTINE: ESMF_ArraySpecEQ - Test if ArraySpec 1 is equal to ArraySpec 2
!
! !INTERFACE:
  impure elemental function ESMF_ArraySpecEQ(arrayspec1, arrayspec2)
!
! !RETURN VALUE:
    logical :: ESMF_ArraySpecEQ
!
! !ARGUMENTS:
    type(ESMF_ArraySpec), intent(in) :: arrayspec1
    type(ESMF_ArraySpec), intent(in) :: arrayspec2
!
! !DESCRIPTION:
!   This method overloads the (==) operator for the {\tt ESMF\_ArraySpec} class.
!   See "interface operator(==)" above for complete description.
!
!EOPI
!------------------------------------------------------------------------------
    integer :: localrc

    ! Initialize output value in case of error
    ESMF_ArraySpecEQ = .false.

    ! check inputs
    ESMF_INIT_CHECK_SHALLOW(ESMF_ArraySpecGetInit, arrayspec1, localrc)
    ESMF_INIT_CHECK_SHALLOW(ESMF_ArraySpecGetInit, arrayspec2, localrc)

    if ((arrayspec1%rank == arrayspec2%rank) .and. &
      arrayspec1%typekind == arrayspec2%typekind) &
      ESMF_ArraySpecEQ = .true.

  end function ESMF_ArraySpecEQ
!------------------------------------------------------------------------------


! -------------------------- ESMF-internal method -----------------------------
#undef  ESMF_METHOD
#define ESMF_METHOD "ESMF_ArraySpecNE()"
!BOPI
! !IROUTINE: ESMF_ArraySpecNE - Test if ArraySpec 1 is not equal to ArraySpec 2
!
! !INTERFACE:
  impure elemental function ESMF_ArraySpecNE(arrayspec1, arrayspec2)
!
! !RETURN VALUE:
    logical :: ESMF_ArraySpecNE
!
! !ARGUMENTS:
    type(ESMF_ArraySpec), intent(in) :: arrayspec1
    type(ESMF_ArraySpec), intent(in) :: arrayspec2
!
! !DESCRIPTION:
!   This method overloads the (/=) operator for the {\tt ESMF\_ArraySpec} class.
!   See "interface operator(/=)" above for complete description.
!
!EOPI
!------------------------------------------------------------------------------
    ESMF_ArraySpecNE = .not.ESMF_ArraySpecEQ(arrayspec1, arrayspec2)

  end function ESMF_ArraySpecNE
!------------------------------------------------------------------------------


! -------------------------- ESMF-public method -------------------------------
#undef  ESMF_METHOD
#define ESMF_METHOD "ESMF_ArraySpecGet"
!BOP
! !IROUTINE: ESMF_ArraySpecGet - Get values from an ArraySpec
!
! !INTERFACE:
  subroutine ESMF_ArraySpecGet(arrayspec, keywordEnforcer, rank, typekind, rc)
!
! !ARGUMENTS:
    type(ESMF_ArraySpec),     intent(in)            :: arrayspec
type(ESMF_KeywordEnforcer), optional:: keywordEnforcer ! must use keywords below
    integer,                  intent(out), optional :: rank
    type(ESMF_TypeKind_Flag), intent(out), optional :: typekind
    integer,                  intent(out), optional :: rc
!
! !STATUS:
! \begin{itemize}
! \item\apiStatusCompatibleVersion{5.2.0r}
! \end{itemize}
!
! !DESCRIPTION:
!   Returns information about the contents of an {\tt ESMF\_ArraySpec}.
!
!   The arguments are:
!   \begin{description}
!   \item[arrayspec]
!     The {\tt ESMF\_ArraySpec} to query.
!   \item[{[rank]}]
!     Array rank (dimensionality -- 1D, 2D, etc). Maximum possible is 7D.
!   \item[{[typekind]}]
!     Array typekind.  See section \ref{const:typekind} for valid values.
!   \item[{[rc]}]
!     Return code; equals {\tt ESMF\_SUCCESS} if there are no errors.
!   \end{description}
!
!EOP
!------------------------------------------------------------------------------
    ! initialize return code; assume routine not implemented
    if (present(rc)) rc = ESMF_RC_NOT_IMPL
    
    ! Check init status of arguments
    ESMF_INIT_CHECK_SHALLOW(ESMF_ArraySpecGetInit, arrayspec, rc)

    ! Get arrayspec contents
    if(present(rank)) rank = arrayspec%rank
    if(present(typekind)) typekind = arrayspec%typekind

    ! return successfully
    if (present(rc)) rc = ESMF_SUCCESS

  end subroutine ESMF_ArraySpecGet
!------------------------------------------------------------------------------


! -------------------------- ESMF-public method -------------------------------
#undef  ESMF_METHOD
#define ESMF_METHOD "ESMF_ArraySpecPrint"
!BOP
! !IROUTINE: ESMF_ArraySpecPrint - Print ArraySpec information

! !INTERFACE:
  subroutine ESMF_ArraySpecPrint(arrayspec, keywordEnforcer, rc)
!
! !ARGUMENTS:
    type(ESMF_ArraySpec), intent(in)            :: arrayspec
type(ESMF_KeywordEnforcer), optional:: keywordEnforcer ! must use keywords below
    integer,              intent(out), optional :: rc
!
! !STATUS:
! \begin{itemize}
! \item\apiStatusCompatibleVersion{5.2.0r}
! \end{itemize}
!
! !DESCRIPTION:
!     Print ArraySpec internals. \\
!
!     The arguments are:
!     \begin{description}
!     \item[arrayspec] 
!         Specified {\tt ESMF\_ArraySpec} object.
!     \item[{[rc]}]
!         Return code; equals {\tt ESMF\_SUCCESS} if there are no errors.
!     \end{description}
!
!EOP
! !REQUIREMENTS:  SSSn.n, GGGn.n
!------------------------------------------------------------------------------
    ! Assume failure until success
    if (present(rc)) rc = ESMF_RC_NOT_IMPL
    
    ! Check init status of arguments
    ESMF_INIT_CHECK_SHALLOW(ESMF_ArraySpecGetInit, arrayspec, rc)

    write(ESMF_UtilIOStdout, *) "ArraySpec Print Begins =====>"
    write(ESMF_UtilIOStdout, *) "   rank = ", arrayspec%rank    
    write(ESMF_UtilIOStdout, *) "   typekind = ", arrayspec%typekind
    write(ESMF_UtilIOStdout, *) "ArraySpec Print Ends   =====>"

    ! return successfully
    if (present(rc)) rc = ESMF_SUCCESS

  end subroutine ESMF_ArraySpecPrint
!------------------------------------------------------------------------------


! -------------------------- ESMF-public method -------------------------------
#undef  ESMF_METHOD
#define ESMF_METHOD "ESMF_ArraySpecSet"
!BOP
! !IROUTINE: ESMF_ArraySpecSet - Set values for an ArraySpec
!
! !INTERFACE:
  subroutine ESMF_ArraySpecSet(arrayspec, rank, typekind, keywordEnforcer, rc)
!
! !ARGUMENTS:
    type(ESMF_ArraySpec),     intent(out)           :: arrayspec
    integer,                  intent(in)            :: rank
    type(ESMF_TypeKind_Flag), intent(in)            :: typekind
type(ESMF_KeywordEnforcer), optional:: keywordEnforcer ! must use keywords below
    integer,                  intent(out), optional :: rc
!
! !STATUS:
! \begin{itemize}
! \item\apiStatusCompatibleVersion{5.2.0r}
! \end{itemize}
!
! !DESCRIPTION:
!   Creates a description of the data -- the typekind, the rank,
!   and the dimensionality.
!
!   The arguments are:
!   \begin{description}
!   \item[arrayspec]
!     The {\tt ESMF\_ArraySpec} to set.
!   \item[rank]
!     Array rank (dimensionality -- 1D, 2D, etc). Maximum allowed is 7D.
!   \item[typekind]
!     Array typekind.  See section \ref{const:typekind} for valid values.
!   \item[{[rc]}]
!     Return code; equals {\tt ESMF\_SUCCESS} if there are no errors.
!   \end{description}
!
!EOP
!------------------------------------------------------------------------------
    ! initialize return code; assume routine not implemented
    if (present(rc)) rc = ESMF_RC_NOT_IMPL

    ! mark output as uninitialized    
    ESMF_INIT_SET_DELETED(arrayspec)

    ! set rank
    arrayspec%rank = rank
    if (rank < 1 .or. rank > ESMF_MAXDIM) then
      ! not a valid rank value
      call ESMF_LogSetError(rcToCheck=ESMF_RC_OBJ_BAD, &
        msg="bad value for rank", &
        ESMF_CONTEXT, rcToReturn=rc)
      return  ! bail out
    endif

    ! set typekind (do not need check because parameterized type)
    arrayspec%typekind = typekind
    
    ! mark output as successfully initialized
    ESMF_INIT_SET_DEFINED(arrayspec)

    ! return successfully
    if (present(rc)) rc = ESMF_SUCCESS

  end subroutine ESMF_ArraySpecSet
!------------------------------------------------------------------------------


! -------------------------- ESMF-public method -------------------------------
#undef  ESMF_METHOD
#define ESMF_METHOD "ESMF_ArraySpecValidate()"
!BOP
! !IROUTINE: ESMF_ArraySpecValidate - Validate ArraySpec internals

! !INTERFACE:
  subroutine ESMF_ArraySpecValidate(arrayspec, keywordEnforcer, rc)
!
! !ARGUMENTS:
    type(ESMF_ArraySpec), intent(in)            :: arrayspec
type(ESMF_KeywordEnforcer), optional:: keywordEnforcer ! must use keywords below
    integer,              intent(out), optional :: rc  
!
! !STATUS:
! \begin{itemize}
! \item\apiStatusCompatibleVersion{5.2.0r}
! \end{itemize}
!
! !DESCRIPTION:
!   Validates that the {\tt arrayspec} is internally consistent.
!   The method returns an error code if problems are found.  
!
!   The arguments are:
!   \begin{description}
!   \item[arrayspec] 
!     Specified {\tt ESMF\_ArraySpec} object.
!   \item[{[rc]}] 
!     Return code; equals {\tt ESMF\_SUCCESS} if there are no errors.
!   \end{description}
!
!EOP
!------------------------------------------------------------------------------
    ! Assume failure until success
    if (present(rc)) rc = ESMF_RC_NOT_IMPL
    
    ! Check init status of arguments
    ESMF_INIT_CHECK_SHALLOW(ESMF_ArraySpecGetInit, arrayspec, rc)

    ! return successfully
    if (present(rc)) rc = ESMF_SUCCESS

  end subroutine ESMF_ArraySpecValidate
!------------------------------------------------------------------------------


! -------------------------- ESMF-internal method -----------------------------
#undef  ESMF_METHOD
#define ESMF_METHOD "ESMF_ArraySpecInit()"
!BOPI
! !IROUTINE: ESMF_ArraySpecInit - Init ArraySpec internals

! !INTERFACE:
  subroutine ESMF_ArraySpecInit(arrayspec)
!
! !ARGUMENTS:
    type(ESMF_ArraySpec), intent(out)             :: arrayspec
!         
!
! !DESCRIPTION:
!      Initialize ArraySpec internals.
!
!     The arguments are:
!     \begin{description}
!     \item[arrayspec] 
!          Specified {\tt ESMF\_ArraySpec} object.
!     \end{description}
!
!EOPI
!------------------------------------------------------------------------------
    ESMF_INIT_SET_DEFINED(arrayspec)
  end subroutine ESMF_ArraySpecInit
!------------------------------------------------------------------------------


! -------------------------- ESMF-internal method -----------------------------
#undef  ESMF_METHOD
#define ESMF_METHOD "ESMF_ArraySpecGetInit"
!BOPI
! !IROUTINE: ESMF_ArraySpecGetInit - Internal access routine for init code
!
! !INTERFACE:
  function ESMF_ArraySpecGetInit(arrayspec) 
!
! !RETURN VALUE:
    ESMF_INIT_TYPE :: ESMF_ArraySpecGetInit   
!
! !ARGUMENTS:
    type(ESMF_ArraySpec), intent(in), optional :: arrayspec
!
! !DESCRIPTION:
!   Access init code.
!
!   The arguments are:
!   \begin{description}
!   \item [arrayspec]
!     ArraySpec object.
!   \end{description}
!
!EOPI
!------------------------------------------------------------------------------
    if (present(arrayspec)) then
      ESMF_ArraySpecGetInit = ESMF_INIT_GET(arrayspec)
    else
      ESMF_ArraySpecGetInit = ESMF_INIT_DEFINED
    endif

  end function ESMF_ArraySpecGetInit
!------------------------------------------------------------------------------


end module ESMF_ArraySpecMod
