#ifndef _Py_PYCORE_SLOTS_H
#define _Py_PYCORE_SLOTS_H

#ifndef Py_BUILD_CORE
#  error "this header requires Py_BUILD_CORE define"
#endif

#include <stdbool.h>

/* Slot data type */
typedef enum _PySlot_DTYPE {
    _PySlot_DTYPE_VOID,
    _PySlot_DTYPE_FUNC,
    _PySlot_DTYPE_PTR,
    _PySlot_DTYPE_SIZE,
    _PySlot_DTYPE_INT64,
    _PySlot_DTYPE_UINT64,
}_PySlot_DTYPE;

/* Slot kind, used to identify:
 * - the thing the slot initializes (type/module/special)
 * - the struct type (PySlot/PyType_Slot/PyModuleDef_Slot)
 */
typedef enum _PySlot_KIND {
    _PySlot_KIND_TYPE,
    _PySlot_KIND_MOD,
    _PySlot_KIND_COMPAT,
    _PySlot_KIND_SLOT,
} _PySlot_KIND;

typedef enum _PySlot_PROBLEM_HANDLING {
    _PySlot_PROBLEM_ALLOW,
    _PySlot_PROBLEM_DEPRECATED,
    _PySlot_PROBLEM_REJECT,
} _PySlot_PROBLEM_HANDLING;

PyAPI_DATA(const char *const) _PySlot_names[];

#define _PySlot_MAX_NESTING 5

/* State for one nesting level of a slots iterator */
typedef struct _PySlotIterator_state {
    union {
        // tagged by slot_struct_kind:
        const PySlot *slot;               // with _PySlot_KIND_SLOT
        const PyType_Slot *tp_slot;       // with _PySlot_KIND_TYPE
        const PyModuleDef_Slot *mod_slot; // with _PySlot_KIND_MOD
        const void *any_slot;
    };
    _PySlot_KIND slot_struct_kind;
} _PySlotIterator_state;

#define _PySlot_SEEN_ENTRY_BITS (8 * sizeof(unsigned int))

/* State for a slots iterator */
typedef struct {
    _PySlotIterator_state *state;
    _PySlotIterator_state states[_PySlot_MAX_NESTING];
    unsigned int seen[_Py_slot_COUNT / _PySlot_SEEN_ENTRY_BITS + 1];
    _PySlot_KIND kind;
    uint8_t recursion_level;
    bool is_at_end :1;
    bool is_first_run :1;

    // Name of the object (type/module) being defined, NULL if unknown.
    // Must be set by the callers as soon as it's known.
    const char *name;

    /* Output information: */

    // The slot. Always a copy; may be modified by caller of the iterator.
    PySlot current;

} _PySlotIterator;

/* Initialize an iterator using a PySlot array */
PyAPI_FUNC(void)
_PySlotIterator_Init(_PySlotIterator *it, const PySlot *slots,
                     _PySlot_KIND result_kind);

/* Initialize an iterator using a legacy slot array */
PyAPI_FUNC(void)
_PySlotIterator_InitLegacy(_PySlotIterator *it, const void *slots,
                           _PySlot_KIND kind);

/* Reset a *successfully exhausted* iterator to the beginning.
 * The *slots* must be the same as for the previous
 * `_PySlotIterator_InitWithKind` call.
 * (Unlike creating a new iterator, we can skip some validation after Rewind.)
 */
PyAPI_FUNC(void) _PySlotIterator_Rewind(_PySlotIterator *it, const void *slots);

/* Iteration function.
 *
 * Return false at the end (when successfully exhausted).
 * Otherwise (even on error), fill output information in `it` and return true.
 *
 * On error, set an exception and set `it->current.sl_id` to `Py_slot_invalid`.
 */
PyAPI_FUNC(bool) _PySlotIterator_Next(_PySlotIterator *it);

/* Return 1 if given slot was "seen" by an earlier _PySlotIterator_Next call.
 * (This state is not reset by rewinding.)
 */
PyAPI_FUNC(bool) _PySlotIterator_SawSlot(_PySlotIterator *, int);

static inline const char *
_PySlot_GetName(uint16_t id)
{
    if (id >= _Py_slot_COUNT) {
        return "<unknown_slot>";
    }
    if (id == Py_slot_invalid) {
        return "Py_slot_invalid";
    }
    return _PySlot_names[id];
}

static inline void
_PySlot_err_bad_slot(char *kind, uint16_t id)
{
    if (id < _Py_slot_COUNT) {
        PyErr_Format(PyExc_SystemError, "invalid %s slot %d (%s)",
                    kind, (unsigned int)id, _PySlot_names[id]);
    }
    else if (id == Py_slot_invalid) {
        PyErr_Format(PyExc_SystemError, "invalid slot (Py_slot_invalid, %u)",
                     (unsigned int)id);
    }
    else {
        PyErr_Format(PyExc_SystemError, "unknown %s slot ID %u",
                     kind, (unsigned int)id);
    }
}

#include "internal/pycore_slots_generated.h"

#endif // _Py_PYCORE_SLOTS_H
