/**
 * @file error.h
 * @brief Declares akbasic's status codes and the registry claim for them.
 */

#ifndef _AKBASIC_ERROR_H_
#define _AKBASIC_ERROR_H_

#include <akerror.h>

/*
 * libakerror 2.0.0 is the floor, raised from 1.0.0 because 2.0.0 is an ABI break
 * that a compile against the wrong header cannot survive quietly:
 *
 *   - The context behind `IGNORE` became thread-local. `IGNORE` expands at *our*
 *     call site, so our objects reference that storage under whichever model the
 *     header on the include path declared. 2.0.2 went further and made it a
 *     per-translation-unit `static` snapshot named `akerr_last_ignored`, copied
 *     from the pool slot so the slot can be released; the old spelling
 *     `__akerr_last_ignored` was an `extern` pointer and no longer exists.
 *   - `akerr_next_error()` now returns a context that already holds a reference,
 *     and `ENSURE_ERROR_READY` no longer increments. Objects compiled against a
 *     1.x header count every reference twice and never give a slot back.
 *
 * Neither is a compile error. Both are a pool that leaks or a use-after-free,
 * which is exactly the class of mismatch a guard is for.
 *
 * libakerror still publishes no version macro, so this feature-tests on
 * AKERR_THREAD_SAFE, which 2.0.0 introduced and writes into the generated header
 * as 1 or 0 -- so `#ifndef` is the right test and `#if` is not. It replaces the
 * AKERR_FIRST_CONSUMER_STATUS test this carried for 1.0.0, which 2.0.0 also
 * still defines and which therefore no longer distinguishes anything.
 */
#ifndef AKERR_THREAD_SAFE
#error "libakbasic requires libakerror >= 2.0.0: the akerror.h on the include path predates the thread-safe error pool. Rebuild and reinstall libakerror."
#endif

/*
 * 2.0.1 additionally fixes an exit status this band made worse than most.
 * `akerr_default_handler_unhandled_error()` used to end in `exit(errctx->status)`
 * and a process exit status is one byte, so a consumer status came out truncated
 * -- and **AKBASIC_ERR_BASE is 512, which truncates to 0**. An unhandled
 * `AKBASIC_ERR_SYNTAX` reported success to whatever was watching `$?`. It exits
 * AKERR_EXIT_STATUS_UNREPRESENTABLE (125) now.
 *
 * Nothing here calls `exit()` on a status -- `src/main.c` handles the context and
 * returns EXIT_FAILURE, and every test installs a HANDLE_DEFAULT -- so the hazard
 * was latent rather than live. It is guarded anyway, because "no caller relies on
 * it today" is not a property a header can keep true.
 */
#ifndef AKERR_EXIT_STATUS_UNREPRESENTABLE
#error "libakbasic requires libakerror >= 2.0.1: an unhandled status in akbasic's band would exit 0. Rebuild and reinstall libakerror."
#endif

/*
 * libakerror reserves 0-255 for the host's errno values and its own AKERR_*
 * codes. libakgl claims 256-262 -- five codes until 0.8.0 added
 * AKGL_ERR_COLLISION and 0.9.0 added AKGL_ERR_UI, seven since. akbasic claims
 * 512-767, leaving 263-511 as
 * headroom for libakgl to grow into -- see MAINTENANCE.md for the coordinated
 * range map, which is the only coordination there is: libakerror can enumerate
 * its consumers no better than we can.
 *
 * **That growth is why the map is worth keeping current.** Nothing enforces the
 * boundary between two libraries' bands at compile time; the first anyone would
 * know of an overlap is a status printing under the wrong owner's name in a
 * stack trace, which is a bad way to find out.
 *
 * These are absolute constants, never offsets from AKERR_LAST_ERRNO_VALUE, so a
 * libc that grows an errno cannot move them. An enum rather than a chain of
 * #defines so the values stay compile-time integer constants -- HANDLE expands
 * to case labels, which require that.
 *
 * Add a code here and you must name it in akbasic_error_register(), or it prints
 * as "Unknown Error" in every stack trace that carries it.
 */
#define AKBASIC_OWNER		"akbasic"

enum {
    AKBASIC_ERR_BASE	= 512,	/** Start of akbasic's reserved status range */
    AKBASIC_ERR_SYNTAX	= AKBASIC_ERR_BASE,
				/** Parse-time grammar violation */
    AKBASIC_ERR_TYPE,		/** Incompatible types in an operation */
    AKBASIC_ERR_UNDEFINED,	/** Reference to an undefined verb, function or label */
    AKBASIC_ERR_BOUNDS,		/** Array subscript or pool index out of range */
    AKBASIC_ERR_ENVIRONMENT,	/** Environment pool exhausted or orphaned environment */
    AKBASIC_ERR_VALUE,		/** A value was malformed, truncated or unconvertible */
    AKBASIC_ERR_STATE,		/** A verb ran outside the block structure it requires */
    AKBASIC_ERR_DEVICE,		/** A verb needs a graphics, audio or input backend the runtime has not been given, or one that cannot do what was asked */
    AKBASIC_ERR_LAST,		/** One past the last real code. Not a status; tests/error_codes.c walks up to it so a new code is checked for a name without anybody remembering to widen the loop. */
    AKBASIC_ERR_LIMIT	= AKBASIC_ERR_BASE + 256
};

/**
 * @brief Claim the akbasic status band and register a name for every code in it.
 *
 * Reserves the whole 256-value range in one call -- libakerror treats only an
 * identical reservation as a repeat, so a subset or superset raises -- and then
 * names each code through the owned entry point. Call it before anything else in
 * the library. Repeating the call is a no-op.
 *
 * @return `NULL` on success, otherwise an error context owned by the caller.
 * @throws AKERR_STATUS_RANGE_OVERLAP When another component already owns part of the band.
 * @throws AKERR_STATUS_RANGE_FULL When libakerror has no reservation slots left.
 * @throws AKERR_STATUS_NAME_FULL When libakerror's name registry is full.
 */
akerr_ErrorContext AKERR_NOIGNORE *akbasic_error_register(void);

#endif // _AKBASIC_ERROR_H_
