! (C) Copyright 2014- ECMWF.
! (C) Copyright 2022- NVIDIA.
!
! This software is licensed under the terms of the Apache Licence Version 2.0
! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
! In applying this licence, ECMWF does not waive the privileges and immunities
! granted to it by virtue of its status as an intergovernmental organisation
! nor does it submit to any jurisdiction.
!

MODULE TPM_HICFFT

  !   Author.
  !   -------
  !     George Mozdzynski
  !
  !   Modifications.
  !   --------------
  !     Original      October 2014
  !     HICFFT abstraction for CUDA and HIP     August 2023     B. Reuter

  USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_INT, C_PTR, C_LOC, C_FLOAT, C_DOUBLE
  USE GROWING_ALLOCATOR_MOD,       ONLY: GROWING_ALLOCATION_TYPE

  IMPLICIT NONE

  SAVE

  PRIVATE
  PUBLIC EXECUTE_DIR_FFT, EXECUTE_INV_FFT

INTERFACE EXECUTE_DIR_FFT
  MODULE PROCEDURE EXECUTE_DIR_FFT_FLOAT,EXECUTE_DIR_FFT_DOUBLE
END INTERFACE

INTERFACE EXECUTE_INV_FFT
  MODULE PROCEDURE EXECUTE_INV_FFT_FLOAT,EXECUTE_INV_FFT_DOUBLE
END INTERFACE



  ! ------------------------------------------------------------------
  CONTAINS
  ! ------------------------------------------------------------------


SUBROUTINE EXECUTE_DIR_FFT_FLOAT(PREEL_REAL,PREEL_COMPLEX,KFIELD,LOENS,OFFSETS,ALLOC)
  USE EC_PARKIND ,ONLY : JPIM

  IMPLICIT NONE

  REAL(KIND=C_FLOAT), INTENT(IN) :: PREEL_REAL(:)
  REAL(KIND=C_FLOAT), INTENT(OUT) :: PREEL_COMPLEX(:)
  INTEGER(KIND=JPIM),INTENT(IN)  :: KFIELD
  INTEGER(KIND=JPIM),INTENT(IN)  :: LOENS(:), OFFSETS(:)
  TYPE(GROWING_ALLOCATION_TYPE), INTENT(IN) :: ALLOC
  INTERFACE
    SUBROUTINE EXECUTE_DIR_FFT_FLOAT_C(PREEL_REAL,PREEL_COMPLEX,KFIELD,LOENS,OFFSETS,NFFT,ALLOC) &
 &                   BIND(C, NAME="execute_dir_fft_float")
      USE ISO_C_BINDING
      REAL(KIND=C_FLOAT), INTENT(IN) :: PREEL_REAL(*)
      REAL(KIND=C_FLOAT), INTENT(OUT) :: PREEL_COMPLEX(*)
      INTEGER(KIND=C_INT),INTENT(IN),VALUE  :: KFIELD
      INTEGER(KIND=C_INT),INTENT(IN)  :: LOENS(*), OFFSETS(*)
      INTEGER(KIND=C_INT),INTENT(IN),VALUE :: NFFT
      TYPE(C_PTR), INTENT(IN), VALUE :: ALLOC
    END SUBROUTINE
  END INTERFACE

#ifdef ACCGPU
  !$ACC HOST_DATA USE_DEVICE(PREEL_REAL,PREEL_COMPLEX)
#endif
  CALL EXECUTE_DIR_FFT_FLOAT_C(PREEL_REAL,PREEL_COMPLEX,KFIELD,LOENS,OFFSETS,SIZE(LOENS),C_LOC(ALLOC))
#ifdef ACCGPU
  !$ACC END HOST_DATA
#endif

END SUBROUTINE EXECUTE_DIR_FFT_FLOAT
SUBROUTINE EXECUTE_DIR_FFT_DOUBLE(PREEL_REAL,PREEL_COMPLEX,KFIELD,LOENS,OFFSETS,ALLOC)
  USE EC_PARKIND ,ONLY : JPIM

  IMPLICIT NONE

  REAL(KIND=C_DOUBLE), INTENT(IN) :: PREEL_REAL(:)
  REAL(KIND=C_DOUBLE), INTENT(OUT) :: PREEL_COMPLEX(:)
  INTEGER(KIND=JPIM),INTENT(IN)  :: KFIELD
  INTEGER(KIND=JPIM),INTENT(IN)  :: LOENS(:), OFFSETS(:)
  TYPE(GROWING_ALLOCATION_TYPE), INTENT(IN) :: ALLOC
  INTERFACE
    SUBROUTINE EXECUTE_DIR_FFT_DOUBLE_C(PREEL_REAL,PREEL_COMPLEX,KFIELD,LOENS,OFFSETS,NFFT,ALLOC) &
 &                   BIND(C, NAME="execute_dir_fft_double")
      USE ISO_C_BINDING, ONLY: C_DOUBLE, C_INT, C_PTR
      REAL(KIND=C_DOUBLE), INTENT(IN) :: PREEL_REAL(*)
      REAL(KIND=C_DOUBLE), INTENT(OUT) :: PREEL_COMPLEX(*)
      INTEGER(KIND=C_INT),INTENT(IN),VALUE  :: KFIELD
      INTEGER(KIND=C_INT),INTENT(IN)  :: LOENS(*), OFFSETS(*)
      INTEGER(KIND=C_INT),INTENT(IN),VALUE :: NFFT
      TYPE(C_PTR), INTENT(IN), VALUE :: ALLOC
    END SUBROUTINE
  END INTERFACE

#ifdef ACCGPU
  !$ACC HOST_DATA USE_DEVICE(PREEL_REAL,PREEL_COMPLEX)
#endif
  CALL EXECUTE_DIR_FFT_DOUBLE_C(PREEL_REAL,PREEL_COMPLEX,KFIELD,LOENS,OFFSETS,SIZE(LOENS),C_LOC(ALLOC))
#ifdef ACCGPU
  !$ACC END HOST_DATA
#endif

END SUBROUTINE EXECUTE_DIR_FFT_DOUBLE

SUBROUTINE EXECUTE_INV_FFT_FLOAT(PREEL_COMPLEX,PREEL_REAL,KFIELD,LOENS,OFFSETS,ALLOC)
  USE EC_PARKIND ,ONLY : JPIM

  IMPLICIT NONE

  REAL(KIND=C_FLOAT), INTENT(IN) :: PREEL_COMPLEX(:)
  REAL(KIND=C_FLOAT), INTENT(OUT) :: PREEL_REAL(:)
  INTEGER(KIND=JPIM),INTENT(IN)  :: KFIELD
  INTEGER(KIND=JPIM),INTENT(IN)  :: LOENS(:), OFFSETS(:)
  TYPE(GROWING_ALLOCATION_TYPE), INTENT(IN) :: ALLOC
  INTERFACE
    SUBROUTINE EXECUTE_INV_FFT_FLOAT_C(PREEL_COMPLEX,PREEL_REAL,KFIELD,LOENS,OFFSETS,NFFT,ALLOC) &
 &                   BIND(C, NAME="execute_inv_fft_float")
      USE ISO_C_BINDING, ONLY: C_FLOAT, C_INT, C_PTR
      REAL(KIND=C_FLOAT), INTENT(IN) :: PREEL_COMPLEX(*)
      REAL(KIND=C_FLOAT), INTENT(OUT) :: PREEL_REAL(*)
      INTEGER(KIND=C_INT),INTENT(IN),VALUE  :: KFIELD
      INTEGER(KIND=C_INT),INTENT(IN)  :: LOENS(*), OFFSETS(*)
      INTEGER(KIND=C_INT),INTENT(IN),VALUE :: NFFT
      TYPE(C_PTR), INTENT(IN), VALUE :: ALLOC
    END SUBROUTINE
  END INTERFACE

#ifdef ACCGPU
  !$ACC HOST_DATA USE_DEVICE(PREEL_COMPLEX,PREEL_REAL)
#endif
  CALL EXECUTE_INV_FFT_FLOAT_C(PREEL_COMPLEX,PREEL_REAL,KFIELD,LOENS,OFFSETS,SIZE(LOENS),C_LOC(ALLOC))
#ifdef ACCGPU
  !$ACC END HOST_DATA
#endif
END SUBROUTINE

SUBROUTINE EXECUTE_INV_FFT_DOUBLE(PREEL_COMPLEX,PREEL_REAL,KFIELD,LOENS,OFFSETS,ALLOC)
  USE EC_PARKIND ,ONLY : JPIM

  IMPLICIT NONE

  REAL(KIND=C_DOUBLE), INTENT(IN) :: PREEL_COMPLEX(:)
  REAL(KIND=C_DOUBLE), INTENT(OUT) :: PREEL_REAL(:)
  INTEGER(KIND=JPIM),INTENT(IN)  :: KFIELD
  INTEGER(KIND=JPIM),INTENT(IN)  :: LOENS(:), OFFSETS(:)
  TYPE(GROWING_ALLOCATION_TYPE), INTENT(IN) :: ALLOC
  INTERFACE
    SUBROUTINE EXECUTE_INV_FFT_DOUBLE_C(PREEL_COMPLEX,PREEL_REAL,KFIELD,LOENS,OFFSETS,NFFT,ALLOC) &
 &                   BIND(C, NAME="execute_inv_fft_double")
      USE ISO_C_BINDING, ONLY: C_DOUBLE, C_INT, C_PTR
      REAL(KIND=C_DOUBLE), INTENT(IN) :: PREEL_COMPLEX(*)
      REAL(KIND=C_DOUBLE), INTENT(OUT) :: PREEL_REAL(*)
      INTEGER(KIND=C_INT),INTENT(IN),VALUE  :: KFIELD
      INTEGER(KIND=C_INT),INTENT(IN)  :: LOENS(*), OFFSETS(*)
      INTEGER(KIND=C_INT),INTENT(IN),VALUE :: NFFT
      TYPE(C_PTR), INTENT(IN), VALUE :: ALLOC
    END SUBROUTINE
  END INTERFACE

#ifdef ACCGPU
  !$ACC HOST_DATA USE_DEVICE(PREEL_COMPLEX,PREEL_REAL)
#endif
  CALL EXECUTE_INV_FFT_DOUBLE_C(PREEL_COMPLEX,PREEL_REAL,KFIELD,LOENS,OFFSETS,SIZE(LOENS),C_LOC(ALLOC))
#ifdef ACCGPU
  !$ACC END HOST_DATA
#endif
END SUBROUTINE


END MODULE TPM_HICFFT
