/* ----- DECLARATIONS ----- */

#include "detect_capabilities.h"

// These are implemented on the Rust side
extern int mldrs_sys_check_capability(mld_sys_cap cap);

// A Rust-side wrapper to portably ensure call_once logic.
extern void mldrs_call_once_init_sys_check_capability(void);

/* ----- DEFINITIONS ----- */

#define MLDRS_UNINITIALIZED 0xbeef

// Caches the results of mldrs_sys_check_capability
static int MLDRS_CACHED_CHECK_CAPABILITY[MLDRS_SYS_CAP_MAX + 1] = {
    MLDRS_UNINITIALIZED,
    MLDRS_UNINITIALIZED,
};

// This non-atomic flag is only ever written once by
// mldrs_init_sys_check_capability
static int MLDRS_SYS_CHECK_CAPABILITY_INITIALIZED = 0;

// We precompute the results of mldrs_sys_check_capability once, to
// avoid the cost of calling a function in the hot path.
//
// This is called from mldrs_call_once_init_sys_check_capability,
// which _portably_ ensures the initialization is done only once,
// avoiding race conditions and UB.
//
// Returns 1 on SUCCESS, 0 on FAILURE.
//
// The only failure mode is if somehow this function has been called
// twice, which should never happen as long as the Rust call_once
// wrapper is correct.
int mldrs_init_sys_check_capability(void)
{
  if (MLDRS_SYS_CHECK_CAPABILITY_INITIALIZED != 0)
    return 0;

  for (unsigned int cap = 0; cap <= MLDRS_SYS_CAP_MAX; cap++) {
    MLDRS_CACHED_CHECK_CAPABILITY[cap] = mldrs_sys_check_capability(cap);
  }

  MLDRS_SYS_CHECK_CAPABILITY_INITIALIZED = 1;

  return 1;
}

// This is the function that mldsa-native looks for
// if MLD_CONFIG_CUSTOM_CAPABILITY_FUNC is defined
static MLD_INLINE int mld_sys_check_capability(mld_sys_cap cap)
{
  // First check on the non-atomic flag, so the Rust call_once machinery is
  // skipped once initialization has been done, avoiding the atmoic_int cost.
  //
  // NOTE: Before initialization is completed this check does not
  // prevent concurrent threads to call
  // mldrs_call_once_init_sys_check_capability(), but that is fine as
  // the Rust side will correctly handle concurrency.
  if (!MLDRS_SYS_CHECK_CAPABILITY_INITIALIZED)
    mldrs_call_once_init_sys_check_capability();

  if (cap <= MLDRS_SYS_CAP_MAX)
    return MLDRS_CACHED_CHECK_CAPABILITY[cap];
  else
    return mldrs_sys_check_capability(cap);
}

// We provide this to the Rust side for testing: it wraps the inlined
// `mld_sys_check_capability()` expected by mldsa-native.
int mldrs_test_mld_sys_check_capability(mld_sys_cap cap)
{
  return mld_sys_check_capability(cap);
}
