#include <stdio.h>
#include <stdint.h>

// See comments in build_native_lib()
#define EXPORT __attribute__((visibility("default")))

/* Test: test_access_pointer */

EXPORT void print_pointer(const int32_t *ptr) {
  printf("printing pointer dereference from C: %d\n", *ptr);
}

/* Test: test_access_simple */

typedef struct Simple {
  int32_t field;
} Simple;

EXPORT int32_t access_simple(const Simple *s_ptr) {
  return s_ptr->field;
}
// Some copies so Rust can import them at different types.
EXPORT int32_t access_simple2(const Simple *s_ptr) {
  return s_ptr->field;
}
EXPORT int32_t access_simple3(const Simple *s_ptr) {
  return s_ptr->field;
}

/* Test: test_access_nested */

typedef struct Nested {
  int32_t value;
  struct Nested *next;
} Nested;

// Returns the innermost/last value of a Nested pointer chain.
EXPORT int32_t access_nested(const Nested *n_ptr) {
  // Edge case: `n_ptr == NULL` (i.e. first Nested is None).
  if (!n_ptr) { return 0; }

  while (n_ptr->next) {
    n_ptr = n_ptr->next;
  }

  return n_ptr->value;
}

/* Test: test_access_static */

typedef struct Static {
    int32_t value;
    struct Static *recurse;
} Static;

EXPORT int32_t access_static(const Static *s_ptr) {
  return s_ptr->recurse->recurse->value;
}

/* Test: unexposed_reachable_alloc */

EXPORT uintptr_t do_one_deref(const int32_t ***ptr) {
  return (uintptr_t)*ptr;
}

/* Test: pass_fn_ptr */

EXPORT void pass_fn_ptr(void f(void)) {
  (void)f; // suppress unused warning
}

/* Test: function_ptrs */
EXPORT void call_fn_ptr(void f(void)) {
    if (f != NULL) {
        f();
    }
}
