/**
 * @file ui.h
 * @brief Declares the UI backend: where MENU, DIALOG, HUD and UISTYLE land.
 *
 * Same reasoning as graphics.h, audio.h and input.h -- the core library is free
 * of SDL and builds with no libakgl present, so the UI verbs call through a
 * record of function pointers rather than reaching akgl_ui_* directly.
 *
 * One thing here is unlike the other four groups, and it shapes the whole
 * record. **libakgl's UI is immediate mode and this interpreter is not.** A game
 * written against akgl/ui.h re-declares its widgets inside a frame bracket sixty
 * times a second and clay borrows the text pointers until the bracket closes; a
 * BASIC program says `MENU 1, "START", "QUIT"` once on line 100 and expects it to
 * still be there on line 900, several hundred frames later. So the entry points
 * below are all *setters*: a verb pushes what it wants into storage the backend
 * owns, and the backend re-declares the whole set once a frame from that. A
 * BASIC string is transient by construction and is never what clay is handed.
 *
 * A runtime with no UI backend is the normal case -- the standalone driver has
 * none -- so every UI verb refuses with AKBASIC_ERR_DEVICE rather than
 * dereferencing a NULL vtable.
 */

#ifndef _AKBASIC_UI_H_
#define _AKBASIC_UI_H_

#include <akerror.h>

#include <akbasic/graphics.h>
#include <akbasic/types.h>

/**
 * @brief How many menus a program may have up at once.
 *
 * Four rather than one because a pause menu over a title menu is an ordinary
 * thing to want, and rather than eight because nothing needs eight and each one
 * costs its item storage in the backend.
 */
#define AKBASIC_UI_MAX_MENUS		4

/** @brief How many HUD label slots there are. Eight, the way there are eight sprites. */
#define AKBASIC_UI_MAX_LABELS		8

/**
 * @brief Most entries one MENU may carry.
 *
 * This is libakgl's AKGL_UI_MENU_MAX_ITEMS, restated rather than included: the
 * core library must not include an akgl header, and a menu longer than a screen
 * is a different widget anyway. src/ui_akgl.c asserts the two agree at compile
 * time, so raising one without the other is a build error and not a surprise.
 */
#define AKBASIC_UI_MAX_MENU_ITEMS	16

/**
 * @brief Where on the screen a HUD label pins itself.
 *
 * These are akgl_UiAnchor's five values and its numbering, so the adaptor maps
 * them across without a table. **There is deliberately no top-centre or
 * bottom-centre**, because libakgl has neither and this repository does not edit
 * its dependencies to add one; see libakgl issues #79 and #80.
 */
typedef enum
{
    AKBASIC_UI_ANCHOR_TOP_LEFT = 0,
    AKBASIC_UI_ANCHOR_TOP_RIGHT,
    AKBASIC_UI_ANCHOR_BOTTOM_LEFT,
    AKBASIC_UI_ANCHOR_BOTTOM_RIGHT,
    AKBASIC_UI_ANCHOR_CENTER,
    AKBASIC_UI_ANCHOR_LIMIT		/* one past the last; not an anchor	*/
} akbasic_UiAnchor;

/**
 * @brief Where the UI verbs draw.
 *
 * Every entry point is required. There is no optional one -- akbasic_GraphicsBackend
 * has one only because `size` was added after hosts already existed, and this
 * record has no such history.
 *
 * Text arguments are **copied** by the implementation, not borrowed. A BASIC
 * string lives in a value from the per-line pool and is gone by the next
 * statement, let alone the next frame.
 */
typedef struct akbasic_UiBackend
{
    void *self;

    /** Set the dialog panel's text. NULL or empty retires the panel. */
    akerr_ErrorContext AKERR_NOIGNORE *(*dialog)(struct akbasic_UiBackend *self, const char *text);

    /**
     * Set one HUD label slot. NULL text retires the slot.
     *
     * `slot` is zero-based here; BASIC numbers them from one, and the verb does
     * that subtraction. `anchor` is an akbasic_UiAnchor, already range-checked.
     */
    akerr_ErrorContext AKERR_NOIGNORE *(*label)(struct akbasic_UiBackend *self, int slot, int anchor, const char *text);

    /**
     * Define one menu, replacing whatever it held. `count` 0 retires it.
     *
     * Defining a menu resets its highlight to the first entry and clears its
     * activation latch: the program has just changed what the entries mean, so
     * a selection index into the old list is not worth carrying over.
     */
    akerr_ErrorContext AKERR_NOIGNORE *(*menu)(struct akbasic_UiBackend *self, int slot, const char *const *items, int count);

    /**
     * Read a menu's highlight and its activation latch.
     *
     * `selected` comes back zero-based. `activated` is true when the entry has
     * been confirmed since the latch was last cleared, and `clear` clears it --
     * which is what makes RMENU(n,1) a read-and-clear the way BUMP() is. A menu
     * that was never defined reports 0 and false rather than failing: a program
     * polling a menu it has retired has asked a reasonable question.
     */
    akerr_ErrorContext AKERR_NOIGNORE *(*menu_state)(struct akbasic_UiBackend *self, int slot, int *selected, bool *activated, bool clear);

    /**
     * Set the one style every widget draws with.
     *
     * `padding` and `radius` are pixels. Restoring the library default is
     * `style` with a NULL fill -- see akbasic_ui_style_default().
     */
    akerr_ErrorContext AKERR_NOIGNORE *(*style)(struct akbasic_UiBackend *self, akbasic_Color *fill, akbasic_Color *edge, akbasic_Color *ink, double padding, double radius);

    /** Retire every widget and restore the default style. NEW and CLR call this. */
    akerr_ErrorContext AKERR_NOIGNORE *(*clear)(struct akbasic_UiBackend *self);
} akbasic_UiBackend;

/**
 * @brief The UI verbs' own state, which lives on the runtime.
 *
 * Deliberately small. The widget text is in the backend, because the backend is
 * what has to hand clay a pointer that outlives a frame; what is left here is
 * what the *interpreter* needs to answer questions about, which is which menus
 * exist and whether a GETMENU is holding.
 */
typedef struct
{
    bool menudefined[AKBASIC_UI_MAX_MENUS];	/* MENU n has entries, so it is on screen	*/
    bool waiting;				/* a GETMENU is holding the program		*/
    int waitmenu;				/* ...on this menu, zero-based			*/
    char variable[64];				/* ...to assign the chosen entry to		*/
} akbasic_UiState;

/* --- Internal API: exposed for the step loop and the tests. --- */

struct akbasic_Runtime;

/**
 * @brief Reset the UI state to its power-on values: no menus, nothing holding.
 * @param obj Object to initialize, inspect, or modify.
 * @return `NULL` on success, otherwise an error context owned by the caller.
 * @throws AKERR_NULLPOINTER When `obj` is NULL.
 */
akerr_ErrorContext AKERR_NOIGNORE *akbasic_ui_state_init(akbasic_UiState *obj);

/**
 * @brief Answer whether a GETMENU is still holding, assigning the entry if one was chosen.
 *
 * Called from akbasic_runtime_step() beside akbasic_input_service() and for the
 * same reason: waiting must not mean blocking. While `*blocked` comes back true
 * the step executes no source line, so the program stays where the GETMENU left
 * it, and the sprite, audio and collision services -- which run *before* this --
 * keep running underneath it.
 *
 * Two things release the hold rather than wedging the script, both of them the
 * rules akbasic_input_service() already keeps: taking the UI device away, and
 * retiring the menu that was being waited on. Either assigns 0.
 *
 * @param obj Object to initialize, inspect, or modify.
 * @param blocked Output destination populated by the function; true while a GETMENU is unsatisfied.
 * @return `NULL` on success, otherwise an error context owned by the caller.
 * @throws AKERR_NULLPOINTER When either argument is NULL.
 * @throws AKBASIC_ERR_UNDEFINED When the variable the GETMENU named has gone out of scope.
 */
akerr_ErrorContext AKERR_NOIGNORE *akbasic_ui_service(struct akbasic_Runtime *obj, bool *blocked);

#endif // _AKBASIC_UI_H_
