/* ========================================================================= */
/**
 * @file ref.c
 * Copyright (c) 2026 Philipp Kaeser
 */

#include <stdbool.h>
#include <libbase/libbase.h>

/* == Exported methods ===================================================== */

/* ------------------------------------------------------------------------- */
void bs_ref_init(bs_ref_t *ref_ptr, void (*destroy_fn)(bs_ref_t *ref_ptr))
{
    *ref_ptr = (bs_ref_t){ .count = 1, .destroy_fn = destroy_fn };
}

/* ------------------------------------------------------------------------- */
void bs_ref_retain(bs_ref_t *ref_ptr)
{
    ++ref_ptr->count;
}

/* ------------------------------------------------------------------------- */
void bs_ref_release(bs_ref_t *ref_ptr)
{
    if (0 < --ref_ptr->count) return;
    BS_ASSERT(0 == ref_ptr->count);
    ref_ptr->destroy_fn(ref_ptr);
}

/* == Unit tests =========================================================== */

static void bs_ref_test(bs_test_t *test_ptr);

/** Test cases. */
static const bs_test_case_t   bs_ref_test_cases[] = {
    { true, "ref", bs_ref_test },
    BS_TEST_CASE_SENTINEL()
};

const bs_test_set_t           bs_ref_test_set = BS_TEST_SET(
    true, "ref", bs_ref_test_cases);

/* ------------------------------------------------------------------------- */

/** State used in tests. */
struct _bs_ref_test {
    /** Number of calls to dtor. */
    int dtor_calls;
    /** Reference state. */
    bs_ref_t ref;
};

/** Dtor used in tests. */
static void _bs_ref_test_dtor(bs_ref_t *r) {
    struct _bs_ref_test *t = BS_CONTAINER_OF(r, struct _bs_ref_test, ref);
    ++t->dtor_calls;
}

/** Simple test, for ref and unref. */
void bs_ref_test(bs_test_t *test_ptr)
{
    struct _bs_ref_test t = {};

    bs_ref_init(&t.ref, _bs_ref_test_dtor);

    BS_TEST_VERIFY_EQ(test_ptr, 1, t.ref.count);
    bs_ref_retain(&t.ref);
    BS_TEST_VERIFY_EQ(test_ptr, 2, t.ref.count);
    bs_ref_release(&t.ref);
    BS_TEST_VERIFY_EQ(test_ptr, 1, t.ref.count);
    BS_TEST_VERIFY_EQ(test_ptr, 0, t.dtor_calls);
    bs_ref_release(&t.ref);
    BS_TEST_VERIFY_EQ(test_ptr, 0, t.ref.count);
    BS_TEST_VERIFY_EQ(test_ptr, 1, t.dtor_calls);
}

/* == End of ref.c ========================================================= */
