#include <stdbool.h>
#include <stddef.h>

#include "valgrind/valgrind.h"

#include "valgrind/cachegrind.h"
#include "valgrind/callgrind.h"
#include "valgrind/dhat.h"
#include "valgrind/drd.h"
#include "valgrind/helgrind.h"
#include "valgrind/memcheck.h"

#ifdef VALGRIND_DO_CLIENT_REQUEST_EXPR
const bool GR_IS_PLATFORM_SUPPORTED_BY_VALGRIND = true;
#else
const bool GR_IS_PLATFORM_SUPPORTED_BY_VALGRIND = false;
#endif

#ifdef __VALGRIND_MAJOR__
const unsigned int GR_VALGRIND_MAJOR = __VALGRIND_MAJOR__;
#else
const unsigned int GR_VALGRIND_MAJOR = 0;
#endif

#ifdef __VALGRIND_MINOR__
const unsigned int GR_VALGRIND_MINOR = __VALGRIND_MINOR__;
#else
const unsigned int GR_VALGRIND_MINOR = 0;
#endif

/*
 * Instead of taking the original enums, we wrap the enum variants in our own
 * type. So, we stay in control which client requests we offer and can sort out
 * client requests which are not available in a specific valgrind version.
 *
 * If a definition does not exist we still assign a value to the enum variant.
 * Values <= 0x1000 indicate that this enum variant does not exists and we
 * cannot execute the client request.
 *
 * The check usage of the enum variant value is done in the rust source code
 * which uses the bindings to our enums from this header file.
 */

/*
 * VALGRIND
 * */

typedef enum {
#ifdef RUNNING_ON_VALGRIND
  GR_RUNNING_ON_VALGRIND = VG_USERREQ__RUNNING_ON_VALGRIND,
#else
  GR_RUNNING_ON_VALGRIND = 0,
#endif
#ifdef VALGRIND_DISCARD_TRANSLATIONS
  GR_DISCARD_TRANSLATIONS = VG_USERREQ__DISCARD_TRANSLATIONS,
#else
  GR_DISCARD_TRANSLATIONS = 1,
#endif
#ifdef VALGRIND_NON_SIMD_CALL0
  GR_CLIENT_CALL0 = VG_USERREQ__CLIENT_CALL0,
#else
  GR_CLIENT_CALL0 = 3,
#endif
#ifdef VALGRIND_NON_SIMD_CALL1
  GR_CLIENT_CALL1 = VG_USERREQ__CLIENT_CALL1,
#else
  GR_CLIENT_CALL1 = 4,
#endif
#ifdef VALGRIND_NON_SIMD_CALL2
  GR_CLIENT_CALL2 = VG_USERREQ__CLIENT_CALL2,
#else
  GR_CLIENT_CALL2 = 5,
#endif
#ifdef VALGRIND_NON_SIMD_CALL3
  GR_CLIENT_CALL3 = VG_USERREQ__CLIENT_CALL3,
#else
  GR_CLIENT_CALL3 = 6,
#endif
#ifdef VALGRIND_COUNT_ERRORS
  GR_COUNT_ERRORS = VG_USERREQ__COUNT_ERRORS,
#else
  GR_COUNT_ERRORS = 7,
#endif
#ifdef VALGRIND_MALLOCLIKE_BLOCK
  GR_MALLOCLIKE_BLOCK = VG_USERREQ__MALLOCLIKE_BLOCK,
#else
  GR_MALLOCLIKE_BLOCK = 8,
#endif
#ifdef VALGRIND_RESIZEINPLACE_BLOCK
  GR_RESIZEINPLACE_BLOCK = VG_USERREQ__RESIZEINPLACE_BLOCK,
#else
  GR_RESIZEINPLACE_BLOCK = 9,
#endif
#ifdef VALGRIND_FREELIKE_BLOCK
  GR_FREELIKE_BLOCK = VG_USERREQ__FREELIKE_BLOCK,
#else
  GR_FREELIKE_BLOCK = 10,
#endif
#if defined(VALGRIND_CREATE_MEMPOOL) || defined(VALGRIND_CREATE_MEMPOOL_EXT)
  GR_CREATE_MEMPOOL = VG_USERREQ__CREATE_MEMPOOL,
#else
  GR_CREATE_MEMPOOL = 11,
#endif
#ifdef VALGRIND_DESTROY_MEMPOOL
  GR_DESTROY_MEMPOOL = VG_USERREQ__DESTROY_MEMPOOL,
#else
  GR_DESTROY_MEMPOOL = 12,
#endif
#ifdef VALGRIND_MEMPOOL_ALLOC
  GR_MEMPOOL_ALLOC = VG_USERREQ__MEMPOOL_ALLOC,
#else
  GR_MEMPOOL_ALLOC = 13,
#endif
#ifdef VALGRIND_MEMPOOL_FREE
  GR_MEMPOOL_FREE = VG_USERREQ__MEMPOOL_FREE,
#else
  GR_MEMPOOL_FREE = 14,
#endif
#ifdef VALGRIND_MEMPOOL_TRIM
  GR_MEMPOOL_TRIM = VG_USERREQ__MEMPOOL_TRIM,
#else
  GR_MEMPOOL_TRIM = 15,
#endif
#ifdef VALGRIND_MOVE_MEMPOOL
  GR_MOVE_MEMPOOL = VG_USERREQ__MOVE_MEMPOOL,
#else
  GR_MOVE_MEMPOOL = 16,
#endif
#ifdef VALGRIND_MEMPOOL_CHANGE
  GR_MEMPOOL_CHANGE = VG_USERREQ__MEMPOOL_CHANGE,
#else
  GR_MEMPOOL_CHANGE = 17,
#endif
#ifdef VALGRIND_MEMPOOL_EXISTS
  GR_MEMPOOL_EXISTS = VG_USERREQ__MEMPOOL_EXISTS,
#else
  GR_MEMPOOL_EXISTS = 18,
#endif
#ifdef VALGRIND_STACK_REGISTER
  GR_STACK_REGISTER = VG_USERREQ__STACK_REGISTER,
#else
  GR_STACK_REGISTER = 19,
#endif
#ifdef VALGRIND_STACK_DEREGISTER
  GR_STACK_DEREGISTER = VG_USERREQ__STACK_DEREGISTER,
#else
  GR_STACK_DEREGISTER = 20,
#endif
#ifdef VALGRIND_STACK_CHANGE
  GR_STACK_CHANGE = VG_USERREQ__STACK_CHANGE,
#else
  GR_STACK_CHANGE = 21,
#endif
#ifdef VALGRIND_LOAD_PDB_DEBUGINFO
  GR_LOAD_PDB_DEBUGINFO = VG_USERREQ__LOAD_PDB_DEBUGINFO,
#else
  GR_LOAD_PDB_DEBUGINFO = 22,
#endif
#ifdef VALGRIND_MAP_IP_TO_SRCLOC
  GR_MAP_IP_TO_SRCLOC = VG_USERREQ__MAP_IP_TO_SRCLOC,
#else
  GR_MAP_IP_TO_SRCLOC = 23,
#endif
#if defined(VALGRIND_DISABLE_ERROR_REPORTING) ||                               \
    defined(VALGRIND_ENABLE_ERROR_REPORTING)
  GR_CHANGE_ERR_DISABLEMENT = VG_USERREQ__CHANGE_ERR_DISABLEMENT,
#else
  GR_CHANGE_ERR_DISABLEMENT = 24,
#endif
#ifdef VALGRIND_MONITOR_COMMAND
  GR_GDB_MONITOR_COMMAND = VG_USERREQ__GDB_MONITOR_COMMAND,
#else
  GR_GDB_MONITOR_COMMAND = 25,
#endif
#ifdef VALGRIND_CLO_CHANGE
  GR_CLO_CHANGE = VG_USERREQ__CLO_CHANGE,
#else
  GR_CLO_CHANGE = 26,
#endif
} GR_ValgrindClientRequest;

/*
 * CALLGRIND
 * */

typedef enum {
#ifdef CALLGRIND_DUMP_STATS
  GR_DUMP_STATS = VG_USERREQ__DUMP_STATS,
#else
  GR_DUMP_STATS = 0,
#endif
#ifdef CALLGRIND_DUMP_STATS_AT
  GR_DUMP_STATS_AT = VG_USERREQ__DUMP_STATS_AT,
#else
  GR_DUMP_STATS_AT = 1,
#endif
#ifdef CALLGRIND_ZERO_STATS
  GR_ZERO_STATS = VG_USERREQ__ZERO_STATS,
#else
  GR_ZERO_STATS = 2,
#endif
#ifdef CALLGRIND_TOGGLE_COLLECT
  GR_TOGGLE_COLLECT = VG_USERREQ__TOGGLE_COLLECT,
#else
  GR_TOGGLE_COLLECT = 3,
#endif
#ifdef CALLGRIND_START_INSTRUMENTATION
  GR_START_INSTRUMENTATION = VG_USERREQ__START_INSTRUMENTATION,
#else
  GR_START_INSTRUMENTATION = 4,
#endif
#ifdef CALLGRIND_STOP_INSTRUMENTATION
  GR_STOP_INSTRUMENTATION = VG_USERREQ__STOP_INSTRUMENTATION,
#else
  GR_STOP_INSTRUMENTATION = 5,
#endif
} GR_CallgrindClientRequest;

/*
 * DHAT
 * */

typedef enum {
#ifdef DHAT_AD_HOC_EVENT
  GR_DHAT_AD_HOC_EVENT = VG_USERREQ__DHAT_AD_HOC_EVENT,
#else
  GR_DHAT_AD_HOC_EVENT = 0,
#endif
#ifdef DHAT_HISTOGRAM_MEMORY
  GR_DHAT_HISTOGRAM_MEMORY = VG_USERREQ__DHAT_HISTOGRAM_MEMORY,
#else
  GR_DHAT_HISTOGRAM_MEMORY = 1,
#endif
} GR_DHATClientRequest;

/*
 * Memcheck
 * */

typedef enum {
#ifdef VALGRIND_MAKE_MEM_NOACCESS
  GR_MAKE_MEM_NOACCESS = VG_USERREQ__MAKE_MEM_NOACCESS,
#else
  GR_MAKE_MEM_NOACCESS = 0,
#endif
#ifdef VALGRIND_MAKE_MEM_UNDEFINED
  GR_MAKE_MEM_UNDEFINED = VG_USERREQ__MAKE_MEM_UNDEFINED,
#else
  GR_MAKE_MEM_UNDEFINED = 1,
#endif
#ifdef VALGRIND_MAKE_MEM_DEFINED
  GR_MAKE_MEM_DEFINED = VG_USERREQ__MAKE_MEM_DEFINED,
#else
  GR_MAKE_MEM_DEFINED = 2,
#endif
#ifdef VALGRIND_DISCARD
  GR_DISCARD = VG_USERREQ__DISCARD,
#else
  GR_DISCARD = 3,
#endif
#ifdef VALGRIND_CHECK_MEM_IS_ADDRESSABLE
  GR_CHECK_MEM_IS_ADDRESSABLE = VG_USERREQ__CHECK_MEM_IS_ADDRESSABLE,
#else
  GR_CHECK_MEM_IS_ADDRESSABLE = 4,
#endif
#if defined(VALGRIND_CHECK_MEM_IS_DEFINED) ||                                  \
    defined(VALGRIND_CHECK_VALUE_IS_DEFINED)
  GR_CHECK_MEM_IS_DEFINED = VG_USERREQ__CHECK_MEM_IS_DEFINED,
#else
  GR_CHECK_MEM_IS_DEFINED = 5,
#endif
#if defined(VALGRIND_DO_LEAK_CHECK) ||                                         \
    defined(VALGRIND_DO_ADDED_LEAK_CHECK) ||                                   \
    defined(VALGRIND_DO_CHANGED_LEAK_CHECK) ||                                 \
    defined(VALGRIND_DO_NEW_LEAK_CHECK) ||                                     \
    defined(VALGRIND_DO_QUICK_LEAK_CHECK)
  GR_DO_LEAK_CHECK = VG_USERREQ__DO_LEAK_CHECK,
#else
  GR_DO_LEAK_CHECK = 6,
#endif
#ifdef VALGRIND_COUNT_LEAKS
  GR_COUNT_LEAKS = VG_USERREQ__COUNT_LEAKS,
#else
  GR_COUNT_LEAKS = 7,
#endif
#ifdef VALGRIND_GET_VBITS
  GR_GET_VBITS = VG_USERREQ__GET_VBITS,
#else
  GR_GET_VBITS = 8,
#endif
#ifdef VALGRIND_SET_VBITS
  GR_SET_VBITS = VG_USERREQ__SET_VBITS,
#else
  GR_SET_VBITS = 9,
#endif
#ifdef VALGRIND_CREATE_BLOCK
  GR_CREATE_BLOCK = VG_USERREQ__CREATE_BLOCK,
#else
  GR_CREATE_BLOCK = 10,
#endif
#ifdef VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE
  GR_MAKE_MEM_DEFINED_IF_ADDRESSABLE =
      VG_USERREQ__MAKE_MEM_DEFINED_IF_ADDRESSABLE,
#else
  GR_MAKE_MEM_DEFINED_IF_ADDRESSABLE = 11,
#endif
#ifdef VALGRIND_COUNT_LEAK_BLOCKS
  GR_COUNT_LEAK_BLOCKS = VG_USERREQ__COUNT_LEAK_BLOCKS,
#else
  GR_COUNT_LEAK_BLOCKS = 12,
#endif
#ifdef VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE
  GR_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE =
      VG_USERREQ__ENABLE_ADDR_ERROR_REPORTING_IN_RANGE,
#else
  GR_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE = 13,
#endif
#ifdef VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE
  GR_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE =
      VG_USERREQ__DISABLE_ADDR_ERROR_REPORTING_IN_RANGE,
#else
  GR_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE = 14,
#endif
} GR_MemcheckClientRequest;

typedef enum {
#ifdef VALGRIND_HG_CLEAN_MEMORY
  GR_HG_CLEAN_MEMORY = VG_USERREQ__HG_CLEAN_MEMORY,
#else
  GR_HG_CLEAN_MEMORY = 1,
#endif
#ifdef ANNOTATE_HAPPENS_BEFORE
  GR_HG_USERSO_SEND_PRE = _VG_USERREQ__HG_USERSO_SEND_PRE,
#else
  GR_HG_USERSO_SEND_PRE = 2,
#endif
#ifdef ANNOTATE_HAPPENS_AFTER
  GR_HG_USERSO_RECV_POST = _VG_USERREQ__HG_USERSO_RECV_POST,
#else
  GR_HG_USERSO_RECV_POST = 3,
#endif
#ifdef ANNOTATE_HAPPENS_BEFORE_FORGET_ALL
  GR_HG_USERSO_FORGET_ALL = _VG_USERREQ__HG_USERSO_FORGET_ALL,
#else
  GR_HG_USERSO_FORGET_ALL = 4,
#endif
#ifdef ANNOTATE_RWLOCK_CREATE
  GR_HG_PTHREAD_RWLOCK_INIT_POST = _VG_USERREQ__HG_PTHREAD_RWLOCK_INIT_POST,
#else
  GR_HG_PTHREAD_RWLOCK_INIT_POST = 5,
#endif
#ifdef ANNOTATE_RWLOCK_DESTROY
  GR_HG_PTHREAD_RWLOCK_DESTROY_PRE = _VG_USERREQ__HG_PTHREAD_RWLOCK_DESTROY_PRE,
#else
  GR_HG_PTHREAD_RWLOCK_DESTROY_PRE = 6,
#endif
#ifdef ANNOTATE_RWLOCK_ACQUIRED
  GR_HG_PTHREAD_RWLOCK_ACQUIRED = _VG_USERREQ__HG_PTHREAD_RWLOCK_ACQUIRED,
#else
  GR_HG_PTHREAD_RWLOCK_ACQUIRED = 7,
#endif
#ifdef ANNOTATE_RWLOCK_RELEASED
  GR_HG_PTHREAD_RWLOCK_RELEASED = _VG_USERREQ__HG_PTHREAD_RWLOCK_RELEASED,
#else
  GR_HG_PTHREAD_RWLOCK_RELEASED = 8,
#endif
} GR_HelgrindClientRequest;

typedef enum {
#ifdef DRD_GET_VALGRIND_THREADID
  GR_DRD_GET_VALGRIND_THREAD_ID = VG_USERREQ__DRD_GET_VALGRIND_THREAD_ID,
#else
  GR_DRD_GET_VALGRIND_THREAD_ID = 0,
#endif
#ifdef DRD_GET_DRD_THREADID
  GR_DRD_GET_DRD_THREAD_ID = VG_USERREQ__DRD_GET_DRD_THREAD_ID,
#else
  GR_DRD_GET_DRD_THREAD_ID = 1,
#endif
#if defined(DRD_IGNORE_VAR) || defined(ANNOTATE_BENIGN_RACE) ||                \
    defined(ANNOTATE_BENIGN_RACE_SIZED)
  GR_DRD_START_SUPPRESSION = VG_USERREQ__DRD_START_SUPPRESSION,
#else
  GR_DRD_START_SUPPRESSION = 2,
#endif
#ifdef DRD_STOP_IGNORING_VAR
  GR_DRD_FINISH_SUPPRESSION = VG_USERREQ__DRD_FINISH_SUPPRESSION,
#else
  GR_DRD_FINISH_SUPPRESSION = 3,
#endif
#if defined(DRD_TRACE_VAR) || defined(ANNOTATE_TRACE_MEMORY)
  GR_DRD_START_TRACE_ADDR = VG_USERREQ__DRD_START_TRACE_ADDR,
#else
  GR_DRD_START_TRACE_ADDR = 4,
#endif
#ifdef DRD_STOP_TRACING_VAR
  GR_DRD_STOP_TRACE_ADDR = VG_USERREQ__DRD_STOP_TRACE_ADDR,
#else
  GR_DRD_STOP_TRACE_ADDR = 5,
#endif
#ifdef ANNOTATE_SEM_INIT_PRE
  GR_DRD_ANNOTATE_SEM_INIT_PRE = VG_USERREQ__DRD_ANNOTATE_SEM_INIT_PRE,
#else
  GR_DRD_ANNOTATE_SEM_INIT_PRE = 6,
#endif
#ifdef ANNOTATE_SEM_DESTROY_POST
  GR_DRD_ANNOTATE_SEM_DESTROY_POST = VG_USERREQ__DRD_ANNOTATE_SEM_DESTROY_POST,
#else
  GR_DRD_ANNOTATE_SEM_DESTROY_POST = 7,
#endif
#ifdef ANNOTATE_SEM_WAIT_PRE
  GR_DRD_ANNOTATE_SEM_WAIT_PRE = VG_USERREQ__DRD_ANNOTATE_SEM_WAIT_PRE,
#else
  GR_DRD_ANNOTATE_SEM_WAIT_PRE = 8,
#endif
#ifdef ANNOTATE_SEM_WAIT_POST
  GR_DRD_ANNOTATE_SEM_WAIT_POST = VG_USERREQ__DRD_ANNOTATE_SEM_WAIT_POST,
#else
  GR_DRD_ANNOTATE_SEM_WAIT_POST = 9,
#endif
#ifdef ANNOTATE_SEM_POST_PRE
  GR_DRD_ANNOTATE_SEM_POST_PRE = VG_USERREQ__DRD_ANNOTATE_SEM_POST_PRE,
#else
  GR_DRD_ANNOTATE_SEM_POST_PRE = 10,
#endif
#if defined(ANNOTATE_IGNORE_READS_BEGIN) || defined(ANNOTATE_IGNORE_READS_END)
  GR_DRD_RECORD_LOADS = VG_USERREQ__DRD_RECORD_LOADS,
#else
  GR_DRD_RECORD_LOADS = 11,
#endif
#if defined(ANNOTATE_IGNORE_WRITES_BEGIN) || defined(ANNOTATE_IGNORE_WRITES_END)
  GR_DRD_RECORD_STORES = VG_USERREQ__DRD_RECORD_STORES,
#else
  GR_DRD_RECORD_STORES = 12,
#endif
#ifdef ANNOTATE_NEW_MEMORY
  GR_DRD_CLEAN_MEMORY = VG_USERREQ__DRD_CLEAN_MEMORY,
#else
  GR_DRD_CLEAN_MEMORY = 13,
#endif
#ifdef ANNOTATE_THREAD_NAME
  GR_DRD_SET_THREAD_NAME = VG_USERREQ__DRD_SET_THREAD_NAME,
#else
  GR_DRD_SET_THREAD_NAME = 14,
#endif
} GR_DRDClientRequest;

typedef enum {
#ifdef CACHEGRIND_START_INSTRUMENTATION
  GR_CG_START_INSTRUMENTATION = VG_USERREQ__CG_START_INSTRUMENTATION,
#else
  GR_CG_START_INSTRUMENTATION = 0,
#endif
#ifdef CACHEGRIND_STOP_INSTRUMENTATION
  GR_CG_STOP_INSTRUMENTATION = VG_USERREQ__CG_STOP_INSTRUMENTATION,
#else
  GR_CG_STOP_INSTRUMENTATION = 1,
#endif
} GR_CachegrindClientRequest;
