/**
 * @file console.h
 * @brief The group E console state: SLEEP, WAIT, KEY and the TI clock.
 *
 * All of it is the program's state rather than the device's, which is why it
 * lives on the runtime beside the graphics, audio and sprite state. A host that
 * swaps one output device for another does not expect a sleeping program to wake
 * up or its function keys to be forgotten.
 */

#ifndef _AKBASIC_CONSOLE_H_
#define _AKBASIC_CONSOLE_H_

#include <akerror.h>

#include <akbasic/types.h>

/** @brief Function keys `KEY` can define. A C128 has eight. */
#define AKBASIC_MAX_FUNCTION_KEYS	8

/** @brief The state SLEEP, WAIT and KEY keep. */
typedef struct
{
    bool sleeping;			/** SLEEP is holding the step loop	*/
    int64_t sleepuntilms;		/** ...until the host clock reaches this */

    bool waiting;			/** WAIT is holding the step loop	*/
    uintptr_t waitaddress;		/** the byte it is polling		*/
    uint8_t waitmask;
    uint8_t waitxor;

    char keys[AKBASIC_MAX_FUNCTION_KEYS][AKBASIC_MAX_STRING_LENGTH];
} akbasic_ConsoleState;

struct akbasic_Runtime;

/**
 * @brief Reset the console state: nothing sleeping, nothing waiting, no macros.
 * @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_console_state_init(akbasic_ConsoleState *obj);

/**
 * @brief Answer whether a SLEEP or a WAIT is still holding the step loop.
 *
 * Called once per akbasic_runtime_step(), beside akbasic_input_service() and for
 * the same reason: waiting must not mean blocking. The step still returns, it
 * simply does not advance the program.
 *
 * @param obj Object to initialize, inspect, or modify.
 * @param blocked Output destination populated by the function; true while held.
 * @return `NULL` on success, otherwise an error context owned by the caller.
 * @throws AKERR_NULLPOINTER When either argument is NULL.
 */
akerr_ErrorContext AKERR_NOIGNORE *akbasic_console_service(struct akbasic_Runtime *obj, bool *blocked);

/**
 * @brief Refresh `TI#` and `TI$` from the host's clock.
 *
 * Called once per step. They are ordinary global variables rather than
 * pseudo-variables, because this dialect has no bare variable names -- see
 * TODO.md section 5.
 *
 * @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.
 * @throws AKBASIC_ERR_BOUNDS When no variable slot is free.
 */
akerr_ErrorContext AKERR_NOIGNORE *akbasic_console_update_clock(struct akbasic_Runtime *obj);

#endif // _AKBASIC_CONSOLE_H_
