#ifndef EZT_CUDA_H
#define EZT_CUDA_H

#ifdef __cplusplus
extern "C" {
#endif

#include <cuda.h>

  typedef uint32_t kernel_id_t;
  typedef size_t ezt_cuda_size_t;


  enum ezt_cudaMemcpyKind {
    CUDA_MEMCPY_KIND_HTOD = 1,
    CUDA_MEMCPY_KIND_DTOH = 2,
    CUDA_MEMCPY_KIND_HTOA = 3,
    CUDA_MEMCPY_KIND_ATOH = 4,
    CUDA_MEMCPY_KIND_ATOA = 5,
    CUDA_MEMCPY_KIND_ATOD = 6,
    CUDA_MEMCPY_KIND_DTOA = 7,
    CUDA_MEMCPY_KIND_DTOD = 8,
    CUDA_MEMCPY_KIND_HTOH = 9,
    CUDA_MEMCPY_KIND_UNKNOWN = -1
  };

#define memoryTypesToCpyKind(srcType, destType)				\
  (((srcType)== CU_MEMORYTYPE_HOST && (destType)== CU_MEMORYTYPE_HOST)? CUDA_MEMCPY_KIND_HTOH : \
   ((srcType)== CU_MEMORYTYPE_HOST && (destType)== CU_MEMORYTYPE_DEVICE)? CUDA_MEMCPY_KIND_HTOD : \
   ((srcType)== CU_MEMORYTYPE_HOST && (destType)== CU_MEMORYTYPE_ARRAY)? CUDA_MEMCPY_KIND_HTOA : \
   ((srcType)== CU_MEMORYTYPE_DEVICE && (destType)== CU_MEMORYTYPE_HOST)? CUDA_MEMCPY_KIND_DTOH : \
   ((srcType)== CU_MEMORYTYPE_DEVICE && (destType)== CU_MEMORYTYPE_DEVICE)? CUDA_MEMCPY_KIND_DTOD : \
   ((srcType)== CU_MEMORYTYPE_DEVICE && (destType)== CU_MEMORYTYPE_ARRAY)? CUDA_MEMCPY_KIND_DTOA : \
   ((srcType)== CU_MEMORYTYPE_ARRAY && (destType)== CU_MEMORYTYPE_HOST)? CUDA_MEMCPY_KIND_ATOH : \
   ((srcType)== CU_MEMORYTYPE_ARRAY && (destType)== CU_MEMORYTYPE_DEVICE)? CUDA_MEMCPY_KIND_ATOD : \
   ((srcType)== CU_MEMORYTYPE_ARRAY && (destType)== CU_MEMORYTYPE_ARRAY)? CUDA_MEMCPY_KIND_ATOA : \
   CUDA_MEMCPY_KIND_UNKNOWN)

#define CUDA_MEMCPY_KIND_TO_EZT(kind)				\
  ((kind)==cudaMemcpyHostToHost?CUDA_MEMCPY_KIND_HTOH:		\
   ((kind)==cudaMemcpyHostToDevice?CUDA_MEMCPY_KIND_HTOD:	\
    ((kind)==cudaMemcpyDeviceToDevice?CUDA_MEMCPY_KIND_DTOD:	\
     ((kind)==cudaMemcpyDeviceToHost?CUDA_MEMCPY_KIND_DTOH:	\
      CUDA_MEMCPY_KIND_UNKNOWN))))

#define CUPTI_ACTIVITY_MEMCPY_KIND_TO_EZT(kind)				\
  ((kind) == CUPTI_ACTIVITY_MEMCPY_KIND_HTOD ? CUDA_MEMCPY_KIND_HTOD:	\
   ((kind) == CUPTI_ACTIVITY_MEMCPY_KIND_DTOH ? CUDA_MEMCPY_KIND_DTOH:	\
    ((kind) == CUPTI_ACTIVITY_MEMCPY_KIND_HTOA ? CUDA_MEMCPY_KIND_HTOA:	\
     ((kind) == CUPTI_ACTIVITY_MEMCPY_KIND_ATOH ? CUDA_MEMCPY_KIND_ATOH: \
      ((kind) == CUPTI_ACTIVITY_MEMCPY_KIND_ATOA ? CUDA_MEMCPY_KIND_ATOA: \
       ((kind) == CUPTI_ACTIVITY_MEMCPY_KIND_ATOD ? CUDA_MEMCPY_KIND_ATOD: \
	((kind) == CUPTI_ACTIVITY_MEMCPY_KIND_DTOA ? CUDA_MEMCPY_KIND_DTOA: \
	 ((kind) == CUPTI_ACTIVITY_MEMCPY_KIND_DTOD ? CUDA_MEMCPY_KIND_DTOD: \
	  ((kind) == CUPTI_ACTIVITY_MEMCPY_KIND_HTOH ? CUDA_MEMCPY_KIND_HTOH: \
	   CUDA_MEMCPY_KIND_UNKNOWN)))))))))


  /* return true if the transfer starts from the CPU */
#define IS_FROM_CPU(kind)			\
  (((kind) == CUDA_MEMCPY_KIND_HTOD) ||		\
   ((kind) == CUDA_MEMCPY_KIND_HTOA) ||		\
   ((kind) == CUDA_MEMCPY_KIND_HTOH))

  /* return true if the transfer starts from the GPU */
#define IS_FROM_GPU(kind)			\
  (((kind) == CUDA_MEMCPY_KIND_DTOH) ||		\
   ((kind) == CUDA_MEMCPY_KIND_ATOH) ||		\
   ((kind) == CUDA_MEMCPY_KIND_ATOA) ||		\
   ((kind) == CUDA_MEMCPY_KIND_ATOD) ||		\
   ((kind) == CUDA_MEMCPY_KIND_DTOA) ||		\
   ((kind) == CUDA_MEMCPY_KIND_DTOD))

  /* return true if the transfer ends to the CPU */
#define IS_TO_CPU(kind)				\
  (((kind) == CUDA_MEMCPY_KIND_DTOH) ||		\
   ((kind) == CUDA_MEMCPY_KIND_ATOH) ||		\
   ((kind) == CUDA_MEMCPY_KIND_HTOH))

  /* return true if the transfer ends to the GPU */
#define IS_TO_GPU(kind)				\
  (((kind) == CUDA_MEMCPY_KIND_HTOD) ||		\
   ((kind) == CUDA_MEMCPY_KIND_HTOA) ||		\
   ((kind) == CUDA_MEMCPY_KIND_ATOD) ||		\
   ((kind) == CUDA_MEMCPY_KIND_ATOA) ||		\
   ((kind) == CUDA_MEMCPY_KIND_DTOA) ||		\
   ((kind) == CUDA_MEMCPY_KIND_DTOD))


#define MEMCPY_TYPE_STR(kind)						\
  ((IS_FROM_CPU(kind) && IS_TO_CPU(kind))? "HToH_memcpy":		\
   (IS_FROM_CPU(kind) && IS_TO_GPU(kind))? "HToD_memcpy":		\
   (IS_FROM_GPU(kind) && IS_TO_CPU(kind))? "DToH_memcpy":		\
   (IS_FROM_GPU(kind) && IS_TO_GPU(kind))? "DToD_memcpy": "Unknown")

  extern int __ezt_cuda_initialized;
  void __ezt_cuda_initialize();
#define EZT_CUDA_INITIALIZE do { if(!__ezt_cuda_initialized) { __ezt_cuda_initialize(); } } while(0)


  void CUPTIAPI eztrace_cuda_driver_callback(void *userdata, CUpti_CallbackDomain domain,
					     CUpti_CallbackId cbid, const void *cbdata);

  void CUPTIAPI eztrace_cuda_runtime_callback(void *userdata, CUpti_CallbackDomain domain,
					      CUpti_CallbackId cbid, const void *cbdata);

#ifdef __cplusplus
}
#endif

#endif	/* EZT_CUDA_H */
