#ifndef __CS_COMMON
#define __CS_COMMON

#include <ruby.h>

typedef enum {
    IMPL_ALIAS_INSTANCE,
    IMPL_ALIAS_SINGLETON,
    IMPL_PREPEND_INSTANCE,
    IMPL_PREPEND_SINGLETON,
} patch_impl;

static VALUE cs__send_method;
static VALUE cs__alias_method_sym;

extern VALUE contrast, agent, patching, policy, assess;
extern VALUE core_extensions, core_assess;
extern VALUE assess_policy, assess_propagator;
extern VALUE funchook_path;
extern VALUE components;

extern VALUE rb_sym_enter_scope;
extern VALUE rb_sym_exit_scope;
extern VALUE rb_sym_in_scope;
extern VALUE rb_sym_skip_contrast_analysis;
extern VALUE rb_sym_skip_assess_analysis;
extern VALUE rb_sym_method;
extern VALUE rb_sym_hash_get, rb_sym_hash_set, rb_sym_hash_tracked;

static VALUE patcher;
static VALUE rb_sym_instance_method;
static VALUE rb_sym_register_c_patch;
static VALUE rb_sym_alias_instance;
static VALUE rb_sym_alias_singleton;
static VALUE rb_sym_prepend_instance;
static VALUE rb_sym_prepend_singleton;

/*
 * Check if ruby version is < 3.0.0.
 * We are using this for handling ancestors of included modules.
 * Since this is fixed after Ruby 3.0.0 we should remove this after
 * dropping support for older versions, as no longer needed.
 */
int rb_ver_below_three();

void patch_via_funchook(void *original_function, void *hook_function);

void contrast_alias_method(const VALUE target, const char *to,
                           const char *from);

VALUE contrast_register_patch(const char *module_name, const char *method_name,
                              VALUE(c_fn)(const int, VALUE *,
                                          const VALUE));

VALUE contrast_register_singleton_patch(const char *module_name,
                                        const char *method_name,
                                        VALUE(c_fn)(const int, VALUE *,
                                                    const VALUE));

VALUE contrast_register_prepend_patch(const char *module_name,
                                      const char *method_name,
                                      VALUE(c_fn)(const int, VALUE *,
                                                  const VALUE));


VALUE contrast_register_singleton_prepend_patch(const char *module_name,
                                                const char *method_name,
                                                VALUE(c_fn)(const int, VALUE *,
                                                             const VALUE));

VALUE contrast_register_prepend_patch(const char *module_name,
                                                const char *method_name,
                                                VALUE(c_fn)(const int, VALUE *,
                                                             const VALUE));

static VALUE _contrast_register_patch(const char *module_name, const char *method_name,
                         VALUE(c_fn)(const int, VALUE *, const VALUE),
                         patch_impl patch_impl);

static VALUE _contrast_check_prepended(VALUE self, VALUE method_name, VALUE is_instance);

extern VALUE contrast_check_prepended(VALUE self, VALUE method_name, VALUE is_instance);

extern VALUE contrast_lookout_prepended(VALUE self, VALUE object_name, VALUE method_name,
                               VALUE is_instance);

/* check if method is prepended and register instance alias or prepend patch */
VALUE contrast_check_and_register_instance_patch(const char *module_name,
                                                const char *method_name,
                                                VALUE(c_fn)(const int, VALUE *,
                                                            const VALUE));

VALUE contrast_patcher();

void Init_cs__common(void);

#endif /* __CS_COMMON */