// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-FileCopyrightText: Copyright Contributors to the Kokkos project

#ifndef KOKKOS_IMPL_PUBLIC_INCLUDE
#include <Kokkos_Macros.hpp>
static_assert(false,
              "Including non-public Kokkos header files is not allowed.");
#endif

#ifndef KOKKOS_OPENACC_SPACE_HPP
#define KOKKOS_OPENACC_SPACE_HPP

#include <Kokkos_Concepts.hpp>
#include <impl/Kokkos_Tools.hpp>

#include <openacc.h>
#include <iosfwd>

namespace Kokkos::Experimental {

class OpenACC;

class OpenACCSpace {
 public:
  using memory_space    = OpenACCSpace;
  using execution_space = OpenACC;
  using device_type     = Kokkos::Device<execution_space, memory_space>;

  using size_type  = size_t;
  using index_type = std::make_signed_t<size_type>;

  OpenACCSpace() = default;

  /**\brief  Allocate untracked memory in the space */
  void* allocate(const Kokkos::Experimental::OpenACC& exec_space,
                 const size_t arg_alloc_size) const;
  void* allocate(const Kokkos::Experimental::OpenACC& exec_space,
                 const char* arg_label, const size_t arg_alloc_size,
                 const size_t arg_logical_size = 0) const;
  void* allocate(const size_t arg_alloc_size) const;
  void* allocate(const char* arg_label, const size_t arg_alloc_size,
                 const size_t arg_logical_size = 0) const;

  /**\brief  Deallocate untracked memory in the space */
  void deallocate(void* const arg_alloc_ptr, const size_t arg_alloc_size) const;
  void deallocate(const char* arg_label, void* const arg_alloc_ptr,
                  const size_t arg_alloc_size,
                  const size_t arg_logical_size = 0) const;

  static constexpr char const* name() { return "OpenACCSpace"; }

 private:
  void* impl_allocate(const Kokkos::Experimental::OpenACC& exec_space,
                      const char* arg_label, const size_t arg_alloc_size,
                      const size_t arg_logical_size = 0,
                      const Kokkos::Tools::SpaceHandle =
                          Kokkos::Tools::make_space_handle(name())) const;
  void* impl_allocate(const char* arg_label, const size_t arg_alloc_size,
                      const size_t arg_logical_size = 0,
                      const Kokkos::Tools::SpaceHandle =
                          Kokkos::Tools::make_space_handle(name())) const;
  void impl_deallocate(const char* arg_label, void* const arg_alloc_ptr,
                       const size_t arg_alloc_size,
                       const size_t arg_logical_size = 0,
                       const Kokkos::Tools::SpaceHandle =
                           Kokkos::Tools::make_space_handle(name())) const;
};

}  // namespace Kokkos::Experimental

/*--------------------------------------------------------------------------*/

template <>
struct Kokkos::Impl::MemorySpaceAccess<Kokkos::HostSpace,
                                       Kokkos::Experimental::OpenACCSpace> {
#if defined(KOKKOS_ENABLE_OPENACC_FORCE_HOST_AS_DEVICE)
  enum : bool{assignable = true};
  enum : bool{accessible = true};
#else
  enum : bool { assignable = false };
  enum : bool { accessible = false };
#endif
};

template <>
struct Kokkos::Impl::MemorySpaceAccess<Kokkos::Experimental::OpenACCSpace,
                                       Kokkos::HostSpace> {
#if defined(KOKKOS_ENABLE_OPENACC_FORCE_HOST_AS_DEVICE)
  enum : bool{assignable = true};
  enum : bool{accessible = true};
#else
  enum : bool { assignable = false };
  enum : bool { accessible = false };
#endif
};

template <>
struct Kokkos::Impl::MemorySpaceAccess<Kokkos::Experimental::OpenACCSpace,
                                       Kokkos::Experimental::OpenACCSpace> {
  enum : bool { assignable = true };
  enum : bool { accessible = true };
};
/*--------------------------------------------------------------------------*/

#endif
