/**
 * @file actor.h
 * @brief A live thing in the world: state bits, motion, hierarchy, and behaviour hooks.
 *
 * An actor is an instance; the akgl_Character it points at is its template. The
 * actor carries only what is unique to it -- where it is, which way it is
 * moving, which animation frame it is on -- and borrows speed, acceleration and
 * the state-to-sprite map from the character. That is what makes a hundred of
 * one kind of thing cheap.
 *
 * **State is a bitmask**, not an enum. An actor is facing left *and* moving left
 * *and* alive at the same time, and the whole combination is the key that
 * selects a sprite (see akgl_character_sprite_get). Adding a state means adding
 * a bit here *and* a name in `src/actor_state_string_names.c`, or character JSON
 * cannot refer to it.
 *
 * **Behaviour attaches as function pointers**, not by inheritance. Every actor
 * gets the library's default `updatefunc`, `renderfunc`, `facefunc`,
 * `movementlogicfunc`, `changeframefunc` and `addchild` from
 * akgl_actor_initialize; a game that wants a different flavour of anything
 * replaces the pointer on that one actor.
 *
 * **Motion is split four ways** -- thrust, environmental, acceleration, and the
 * velocity that is their sum -- so that a character's own walking and the
 * world's gravity and drag can be reasoned about separately. See physics.h for
 * the model and which fields mean what.
 *
 * Actors are pool objects (akgl_heap_next_actor) published in
 * #AKGL_REGISTRY_ACTOR under their name.
 */

#ifndef _AKGL_ACTOR_H_
#define _AKGL_ACTOR_H_

#include <stdint.h>
#include <akgl/types.h>
#include <akgl/character.h>

//           ---- LOW WORD STATUSES ----
//
// The trailing column is the bit pattern within its own 16-bit half -- the
// low half here, the high half below -- not the full 32-bit value. Read as a
// whole word it looks like bit 16 restarts at bit 0, which it does not.

#define AKGL_ACTOR_STATE_FACE_DOWN             (1 << 0)             // 1           0000 0000 0000 0001
#define AKGL_ACTOR_STATE_FACE_LEFT             (1 << 1)             // 2           0000 0000 0000 0010
#define AKGL_ACTOR_STATE_FACE_RIGHT            (1 << 2)             // 4           0000 0000 0000 0100
#define AKGL_ACTOR_STATE_FACE_UP               (1 << 3)             // 8           0000 0000 0000 1000
#define AKGL_ACTOR_STATE_ALIVE                 (1 << 4)             // 16          0000 0000 0001 0000
#define AKGL_ACTOR_STATE_DYING                 (1 << 5)             // 32          0000 0000 0010 0000
#define AKGL_ACTOR_STATE_DEAD                  (1 << 6)             // 64          0000 0000 0100 0000
#define AKGL_ACTOR_STATE_MOVING_LEFT           (1 << 7)             // 128         0000 0000 1000 0000
#define AKGL_ACTOR_STATE_MOVING_RIGHT          (1 << 8)             // 256         0000 0001 0000 0000
#define AKGL_ACTOR_STATE_MOVING_UP             (1 << 9)             // 512         0000 0010 0000 0000
#define AKGL_ACTOR_STATE_MOVING_DOWN           (1 << 10)            // 1024        0000 0100 0000 0000
#define AKGL_ACTOR_STATE_MOVING_IN             (1 << 11)            // 2048        0000 1000 0000 0000
#define AKGL_ACTOR_STATE_MOVING_OUT            (1 << 12)            // 4096        0001 0000 0000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_13          (1 << 13)            // 8192        0010 0000 0000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_14          (1 << 14)            // 16384       0100 0000 0000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_15          (1 << 15)            // 32768       1000 0000 0000 0000

//            ----- HIGH WORD STATUSES -----

#define AKGL_ACTOR_STATE_UNDEFINED_16          (1 << 16)            // 65536       0000 0000 0000 0001
#define AKGL_ACTOR_STATE_UNDEFINED_17          (1 << 17)            // 131072      0000 0000 0000 0010
#define AKGL_ACTOR_STATE_UNDEFINED_18          (1 << 18)            // 262144      0000 0000 0000 0100
#define AKGL_ACTOR_STATE_UNDEFINED_19          (1 << 19)            // 524288      0000 0000 0000 1000
#define AKGL_ACTOR_STATE_UNDEFINED_20          (1 << 20)            // 1048576     0000 0000 0001 0000
#define AKGL_ACTOR_STATE_UNDEFINED_21          (1 << 21)            // 2097152     0000 0000 0010 0000
#define AKGL_ACTOR_STATE_UNDEFINED_22          (1 << 22)            // 4194304     0000 0000 0100 0000
#define AKGL_ACTOR_STATE_UNDEFINED_23          (1 << 23)            // 8388608     0000 0000 1000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_24          (1 << 24)            // 16777216    0000 0001 0000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_25          (1 << 25)            // 33554432    0000 0010 0000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_26          (1 << 26)            // 67108864    0000 0100 0000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_27          (1 << 27)            // 134217728   0000 1000 0000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_28          (1 << 28)            // 268435456   0001 0000 0000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_29          (1 << 29)            // 536870912   0010 0000 0000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_30          (1 << 30)            // 1073741824  0100 0000 0000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_31          (1 << 31)            // 2147483648  1000 0000 0000 0000

/** @brief Bits in an actor's state word. Fixed by the width of `int32_t state`. */
#define AKGL_ACTOR_MAX_STATES           32

/**
 * @brief Bit position -> state name, as text. Index `i` names the bit `1 << i`.
 *
 * akgl_registry_init_actor_state_strings builds
 * #AKGL_REGISTRY_ACTOR_STATE_STRINGS out of this, which is what lets character
 * JSON write `"AKGL_ACTOR_STATE_FACE_LEFT"` instead of `2`. A bit whose name
 * here does not match its `#define` above cannot be referred to from JSON at
 * all -- which was the case for `MOVING_IN` and `MOVING_OUT` until 0.5.0.
 *
 * Maintained by hand in `src/actor_state_string_names.c`, and exactly
 * #AKGL_ACTOR_MAX_STATES entries long: it was declared one longer than it was
 * defined until 0.5.0, so a consumer trusting the declared bound read past the
 * object. `tests/registry.c` checks every entry against its bit.
 */
extern char *AKGL_ACTOR_STATE_STRING_NAMES[AKGL_ACTOR_MAX_STATES];

/** @brief Every facing bit. Clear this before setting one, so an actor faces exactly one way. */
#define AKGL_ACTOR_STATE_FACE_ALL              (AKGL_ACTOR_STATE_FACE_DOWN | AKGL_ACTOR_STATE_FACE_LEFT | AKGL_ACTOR_STATE_FACE_RIGHT | AKGL_ACTOR_STATE_FACE_UP)
/** @brief Every planar movement bit. The two depth bits, MOVING_IN and MOVING_OUT, are not in it. */
#define AKGL_ACTOR_STATE_MOVING_ALL            (AKGL_ACTOR_STATE_MOVING_LEFT | AKGL_ACTOR_STATE_MOVING_RIGHT | AKGL_ACTOR_STATE_MOVING_UP | AKGL_ACTOR_STATE_MOVING_DOWN)

/** @brief Longest actor name, including the terminator. Names are truncated, not rejected. */
#define AKGL_ACTOR_MAX_NAME_LENGTH      128
/** @brief Children one actor can carry. A child moves with its parent rather than simulating. */
#define AKGL_ACTOR_MAX_CHILDREN         8


/** @brief Represents a live actor, including state, motion, hierarchy, and behavior callbacks. */
typedef struct akgl_Actor {
    uint8_t refcount;                       /**< Pool bookkeeping; 0 means the slot is free. One reference per parent, plus one for the registry. */
    char name[AKGL_ACTOR_MAX_NAME_LENGTH];  /**< Registry key. Truncated, not rejected, if the source name is longer. */
    akgl_Character *basechar;               /**< The template this actor instantiates. Borrowed; no reference is taken. Required before the actor can update, render, or simulate. */
    uint8_t curSpriteFrameId;               /**< Index into the current sprite's `frameids`, not a frame number on the sheet. */
    SDL_Time curSpriteFrameTimer;           /**< When the current frame was shown. The next frame is due once the sprite's `speed` has elapsed. */
    bool curSpriteReversing;                /**< Walking the animation backwards, for a sprite with `loopReverse` set. */
    uint32_t layer;                         /**< Which tilemap layer the actor is drawn and simulated on. Set from the object layer it was placed in. */
    int32_t state;                          /**< The `AKGL_ACTOR_STATE_*` bitmask. The whole value is the key that selects a sprite. */
    bool movement_controls_face;            /**< When set, the default `facefunc` turns the actor to face whichever way it is moving. Clear it for an actor that aims independently. */
    void *actorData;                        /**< The game's own per-actor data. Never read or freed by the library. */
    bool visible;                           /**< Whether to draw at all. An actor off-camera is skipped separately, so this is for deliberate hiding. */
    SDL_Time movetimer;                     /**< Unused. Nothing in the library reads or writes it. */
    akgl_CollisionShape shape;              /**< The collision volume this actor occupies. Copied from the character by akgl_actor_set_character unless #shape_override is set. Zeroed by akgl_actor_initialize, so an actor nobody configured does not collide. */
    bool shape_override;                    /**< The game set #shape itself and akgl_actor_set_character must leave it alone. Distinct from "the shape is empty": an actor deliberately given no collider and an actor not yet given one are different states, and only this flag tells them apart. */
    struct akgl_CollisionProxy *proxy;      /**< This actor's registration in the broad phase, or `NULL` when it has none. Owned by the library and released with the actor; a game neither sets nor frees it. */
    // Velocity. Combined effect of all forces acting on the actor resulting
    // in energy along an axis.
    float32_t vx;   /**< Velocity along x, world units per second. Recomputed each step as `ex + tx`; writing it directly is overwritten. */
    float32_t vy;   /**< Velocity along y. On a child actor this is read as an offset from the parent instead. */
    float32_t vz;   /**< Velocity along z. */
    // Environmental velocity. These are the forces acting on the actor by the
    // environment (such as gravity and atmospheric drag)
    float32_t ex;   /**< Environmental velocity along x: what gravity and drag have done. Accumulates across steps. */
    float32_t ey;   /**< Environmental velocity along y. This is what carries a falling actor. */
    float32_t ez;   /**< Environmental velocity along z. */
    // Thrust. Energy originating only from the actor's own acceleration on a
    // given axis, before the effects of gravity and drag.
    float32_t tx;   /**< Thrust along x: the actor's own effort. Capped at `sx`, which is why gravity can outrun top speed and walking cannot. */
    float32_t ty;   /**< Thrust along y, capped at `sy`. */
    float32_t tz;   /**< Thrust along z, capped at `sz`. */
    // Acceleration. These are borrowed from the base character object.
    // A given axis resets to 0 when the actor stops moving in a given axis.
    float32_t ax;   /**< Acceleration along x, signed by the direction of travel. Copied from the character each step by the default movement logic. */
    float32_t ay;   /**< Acceleration along y, signed the same way. */
    float32_t az;   /**< Acceleration along z. Not set by the default movement logic. */
    // Max speed. These are borrowed from the base character object.
    float32_t sx;   /**< Maximum thrust along x, copied from the character. */
    float32_t sy;   /**< Maximum thrust along y. */
    float32_t sz;   /**< Maximum thrust along z. */
    // Position.
    float32_t x;    /**< Position in map pixels. For a child, an offset from the parent. */
    float32_t y;    /**< Position in map pixels. Also what the tilemap's perspective band scales against. */
    float32_t z;    /**< Depth. Carried through the simulation but not used at draw time. */
    float32_t scale;                                        /**< Draw scale, 1.0 for natural size. Written by akgl_tilemap_scale_actor, or forced to 1.0 when tilemap scaling is off. */
    struct akgl_Actor *children[AKGL_ACTOR_MAX_CHILDREN];   /**< Attached actors, `NULL` in unused slots. Each holds a reference and is released with the parent. */
    struct akgl_Actor *parent;                              /**< The actor this one is attached to, or `NULL`. A child is positioned relative to it and does not simulate on its own. */
    akerr_ErrorContext AKERR_NOIGNORE *(*updatefunc)(struct akgl_Actor *obj);                                                   /**< Per-frame logic: facing, then animation frame. Defaults to akgl_actor_update. */
    akerr_ErrorContext AKERR_NOIGNORE *(*renderfunc)(struct akgl_Actor *obj);                                                   /**< Per-frame draw. Defaults to akgl_actor_render. */
    akerr_ErrorContext AKERR_NOIGNORE *(*facefunc)(struct akgl_Actor *obj);                                                     /**< Chooses the facing bits. Defaults to akgl_actor_automatic_face. */
    akerr_ErrorContext AKERR_NOIGNORE *(*movementlogicfunc)(struct akgl_Actor *obj, float32_t dt);                              /**< Called by the physics step before gravity. Defaults to akgl_actor_logic_movement. May raise AKGL_ERR_LOGICINTERRUPT to skip the rest of the step. */
    akerr_ErrorContext AKERR_NOIGNORE *(*changeframefunc)(struct akgl_Actor *obj, akgl_Sprite *curSprite, SDL_Time curtimems);  /**< Advances the animation. Defaults to akgl_actor_logic_changeframe. */
    akerr_ErrorContext AKERR_NOIGNORE *(*addchild)(struct akgl_Actor *obj, struct akgl_Actor *child);                           /**< Attaches a child. Defaults to akgl_actor_add_child. */
    akerr_ErrorContext AKERR_NOIGNORE *(*collidefunc)(struct akgl_Actor *obj, akgl_Contact *contact);                           /**< Responds to one contact. Defaults to akgl_actor_collide_block. Called once per contact found against this actor, with the normal already pointing the way this actor has to move. */
} akgl_Actor;

/**
 * @brief Zero a pooled actor, name it, wire up its default behaviour, and register it.
 *
 * Sets `scale` to 1.0 and `movement_controls_face` to true, installs all seven
 * default function pointers, publishes the actor in #AKGL_REGISTRY_ACTOR, and
 * takes the first reference. It does *not* set a character: the actor cannot
 * update, render, or simulate until akgl_actor_set_character has run.
 *
 * @param obj  Pooled actor to initialize, normally from akgl_heap_next_actor.
 *             Required. Any previous contents are discarded -- including its
 *             child list, so release an actor rather than reinitializing it.
 * @param name Registry key, NUL-terminated. Required. Truncated at
 *             #AKGL_ACTOR_MAX_NAME_LENGTH. An existing entry with the same name
 *             is silently replaced, and the actor it displaced becomes
 *             unreachable rather than being released.
 * @return `NULL` on success, otherwise an error context owned by the caller.
 * @throws AKERR_NULLPOINTER If @p obj or @p name is `NULL`.
 * @throws AKERR_KEY If the actor cannot be written into #AKGL_REGISTRY_ACTOR --
 *         in practice, because akgl_registry_init has not run.
 */
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_initialize(akgl_Actor *obj, char *name);
/**
 * @brief Bind an actor to a registered character, and copy its top speeds.
 *
 * Looks the character up by name and takes its `sx`/`sy` as this actor's speed
 * limits, zeroing `ax` and `ay` so the actor starts from rest. The character is
 * borrowed, not referenced: releasing it out from under a live actor leaves a
 * dangling pointer.
 *
 * @param obj          The actor to bind. Required.
 * @param basecharname Registry name of the character. Required. Rebinding an
 *                     actor mid-life is allowed, and is how a character swaps
 *                     its whole sprite set at once.
 * @return `NULL` on success, otherwise an error context owned by the caller.
 * @throws AKERR_NULLPOINTER If @p obj or @p basecharname is `NULL`, or if no
 *         character is registered under that name. The last case is a lookup
 *         miss reported with a pointer status rather than AKERR_KEY.
 *
 * @note `sz` is not copied and `az` is not zeroed, so an actor's depth speed
 *       keeps whatever it had. The default movement logic re-copies all three
 *       each step, which papers over it for anything that simulates.
 */
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_set_character(akgl_Actor *obj, char *basecharname);
/**
 * @brief Draw an actor's current animation frame, if it is on camera.
 *
 * Selects the sprite for the actor's current state, checks it against the
 * camera, and blits the frame at the actor's position translated into screen
 * space. A child actor is drawn at its parent's position plus its own, which is
 * what makes an offset mean an offset.
 *
 * Several things are skipped rather than reported, because a frame is not the
 * place to fail: an actor with no sprite for its state, one off camera, one with
 * `visible` clear, and one whose frame index has run past its current sprite
 * are all simply not drawn.
 *
 * @param obj The actor to draw. Required, along with its `basechar`.
 * @return `NULL` on success -- including every skipped case above -- otherwise
 *         an error context owned by the caller.
 * @throws AKERR_NULLPOINTER If @p obj or `obj->basechar` is `NULL`.
 * @throws AKERR_* Whatever akgl_spritesheet_coords_for_frame or the renderer's
 *         `draw_texture` raises.
 *
 * @note The destination height is computed from the sprite's *width*, so a
 *       non-square sprite is drawn square.
 */
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_render(akgl_Actor *obj);
/**
 * @brief Per-frame logic: turn the actor to face its direction, then advance its animation.
 *
 * Calls the actor's `facefunc`, then selects the sprite for the resulting state
 * and calls `changeframefunc` if that sprite's frame time has elapsed. An actor
 * with no sprite registered for its current state is left alone and reported as
 * success -- states change faster than art gets drawn, and a missing sprite
 * should not stop the frame.
 *
 * @param obj The actor to update. Required, along with its `basechar`.
 * @return `NULL` on success, otherwise an error context owned by the caller.
 * @throws AKERR_NULLPOINTER If @p obj or `obj->basechar` is `NULL`.
 * @throws AKERR_* Whatever `facefunc` or `changeframefunc` raises. Note that an
 *         AKERR_KEY from either is swallowed along with the missing-sprite case.
 */
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_update(akgl_Actor *obj);
/**
 * @brief The default `movementlogicfunc`: turn movement bits into signed acceleration.
 *
 * Re-copies the character's speed limits onto the actor -- so a change to the
 * character takes effect next step -- and sets `ax`/`ay` to plus or minus the
 * character's acceleration according to which movement bits are set. It does not
 * integrate anything; the physics backend does that.
 *
 * Opposing bits do not cancel: `MOVING_LEFT` wins over `MOVING_RIGHT` because it
 * is tested first. An actor with no movement bits keeps its previous
 * acceleration rather than being zeroed, which is why the input handlers clear
 * it themselves on key release.
 *
 * @param obj The actor to compute acceleration for. Required, along with its
 *            `basechar`.
 * @param dt  Seconds since the previous step. Accepted for the hook's signature;
 *            this implementation does not use it, since it sets an acceleration
 *            rather than integrating one.
 * @return `NULL` on success, otherwise an error context owned by the caller.
 * @throws AKERR_NULLPOINTER If @p obj or `obj->basechar` is `NULL`.
 *
 * @note A replacement for this hook may raise AKGL_ERR_LOGICINTERRUPT to tell
 *       the physics step to skip the rest of this actor's tick. This
 *       implementation never does.
 */
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_logic_movement(akgl_Actor *obj, float32_t dt);
/**
 * @brief The default `changeframefunc`: step to the next animation frame.
 *
 * Walks forward through the sprite's frames; at the last one it either wraps to
 * 0, or -- for a sprite with both `loop` and `loopReverse` -- turns round and
 * walks back down, turning again at frame 0. A sprite with `loop` clear also
 * wraps to 0 rather than holding the final frame.
 *
 * @param obj       The actor whose frame index to advance. Required.
 * @param curSprite The sprite currently selected for the actor, whose frame
 *                  count and loop flags decide what happens at the end.
 *                  Required in practice, and **not** checked -- a `NULL` here is
 *                  a crash, not an error.
 * @param curtimems The current clock reading. Accepted for the hook's signature;
 *                  this implementation does not use it, because the caller has
 *                  already decided the frame is due.
 * @return `NULL` on success, otherwise an error context owned by the caller.
 * @throws AKERR_NULLPOINTER If @p obj is `NULL`.
 */
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_logic_changeframe(akgl_Actor *obj, akgl_Sprite *curSprite, SDL_Time curtimems);
/**
 * @brief The default `facefunc`: turn the actor to face whichever way it is moving.
 *
 * Clears every facing bit and sets the one matching the first movement bit it
 * finds, in the order left, right, up, down -- so an actor moving diagonally
 * faces the horizontal component. An actor with `movement_controls_face` clear
 * is left alone entirely, which is the hook for something that aims
 * independently of the way it walks.
 *
 * @param obj The actor to turn. Required.
 * @return `NULL` on success, otherwise an error context owned by the caller.
 * @throws AKERR_NULLPOINTER If @p obj is `NULL`.
 *
 * @note An actor that has stopped moving is left with no facing bit at all,
 *       rather than keeping the way it was last facing -- so its state no longer
 *       matches any "standing still facing left" sprite. The implementation
 *       carries a TODO saying as much.
 */
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_automatic_face(akgl_Actor *obj);
/**
 * @brief Attach one actor to another so it moves with it.
 *
 * A child is positioned relative to its parent rather than simulated: the
 * physics step snaps it to the parent's position plus its own velocity fields,
 * used here as a fixed offset. That is what a carried lantern or a turret on a
 * tank wants.
 *
 * The parent takes a reference on the child, and releasing the parent releases
 * every child with it.
 *
 * @param obj   The parent. Required.
 * @param child The actor to attach. Required. Must not already have a parent --
 *              this builds a tree, not a graph. Its `x`/`y` become an offset
 *              from the parent's position rather than a world position.
 * @return `NULL` on success, otherwise an error context owned by the caller.
 * @throws AKERR_NULLPOINTER If @p obj or @p child is `NULL`.
 * @throws AKERR_RELATIONSHIP If @p child already has a parent. Detach it first --
 *         though note there is no detach function, so in practice this means
 *         releasing and rebuilding it.
 * @throws AKERR_OUTOFBOUNDS If @p obj already has #AKGL_ACTOR_MAX_CHILDREN
 *         children.
 *
 * @warning Nothing checks for a cycle. Making an actor its own ancestor makes
 *          akgl_heap_release_actor recurse until the stack runs out.
 */
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_add_child(akgl_Actor *obj, akgl_Actor *child);

/**
 * @brief The default `collidefunc`: stop at the surface and give up the motion into it.
 *
 * Moves @p obj out along the contact normal by exactly the penetration depth,
 * and removes the component of its motion that was going into the surface. What
 * is left is the tangential part, which is what makes an actor slide along a
 * wall instead of sticking to it.
 *
 * @section collide_block_velocity Why this writes `e` and `t` and not `v`
 *
 * akgl_physics_simulate recomputes velocity as `e + t` at the top of every step,
 * so a write to `vx`/`vy`/`vz` is discarded before anything reads it. The
 * component has to come off the environmental and thrust terms themselves --
 * `e` is where gravity accumulates and `t` is the actor's own effort -- or an
 * actor holding a direction into a wall keeps accumulating thrust while standing
 * still and leaves at speed the instant the wall ends.
 *
 * They are treated separately rather than as their sum, so an actor pressing
 * into a floor loses its downward gravity without losing the sideways run it is
 * also doing.
 *
 * @section collide_block_separating A separating contact is left alone
 *
 * If the actor is already moving out of the surface, nothing is removed. Zeroing
 * a separating velocity is how an actor gets stuck to a wall it is walking away
 * from, and it looks like the collision "grabbing" the player.
 *
 * @section collide_block_sensor A sensor is reported and never pushed
 *
 * A contact flagged as a sensor returns success having changed nothing. Walking
 * through a coin is not being blocked by it.
 *
 * @note This does **not** pre-load the environmental term with a step of
 *       gravity. A resolver that runs *before* the step has to, or the step it
 *       is correcting for immediately re-adds `gravity * dt` and commits
 *       `gravity * dt^2` of penetration -- a quarter of a pixel at 60 Hz, which
 *       is invisible and still fatal, because the actor is then overlapping the
 *       floor and every horizontal move reads as blocked. This library resolves
 *       *after* the move, so the case does not arise. The sidescroller example
 *       carries that workaround because it had to.
 *
 * @param obj     The actor to resolve. Required.
 * @param contact The contact. Required. Its normal points the way @p obj must
 *                move.
 * @return `NULL` on success, otherwise an error context owned by the caller.
 * @throws AKERR_NULLPOINTER If @p obj or @p contact is `NULL`.
 */
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_collide_block(akgl_Actor *obj, akgl_Contact *contact);

/*
 * The control-map handler functions ("cmhf"). These are what
 * akgl_controller_default binds the arrow keys and the D-pad to, and what a
 * game's own akgl_Control bindings can point at. They are called with the
 * control map's target actor, not with a global "player", so they work for any
 * number of locally controlled actors.
 *
 * Each pair is symmetric: the `_on` handler starts movement in a direction and
 * turns the actor to face it, and the `_off` handler stops it dead -- zeroing
 * acceleration, thrust, environmental velocity and velocity on that axis, so
 * there is no coasting. That is an arcade feel rather than a physical one, and
 * it is deliberate; a game wanting momentum binds its own handlers instead.
 *
 * An `_on` handler clears *every* facing and movement bit before setting its
 * own, so holding two directions at once moves in whichever was pressed last
 * rather than diagonally.
 */

/**
 * @brief Start the actor moving left, and turn it to face left.
 * @param obj   The actor to move, supplied by the control map. Required, along
 *              with its `basechar`, whose acceleration is negated onto `ax`.
 * @param event The event that triggered this. Required, but not read -- the
 *              binding has already decided what the event means.
 * @return `NULL` on success, otherwise an error context owned by the caller.
 * @throws AKERR_NULLPOINTER If @p obj, @p event, or `obj->basechar` is `NULL`.
 */
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_cmhf_left_on(akgl_Actor *obj, SDL_Event *event);
/**
 * @brief Stop the actor moving left, zeroing everything on the x axis.
 * @param obj   The actor to stop. Required. `basechar` is not needed here.
 * @param event The event that triggered this. Required, but not read.
 * @return `NULL` on success, otherwise an error context owned by the caller.
 * @throws AKERR_NULLPOINTER If @p obj or @p event is `NULL`.
 * @note It clears the x axis whichever way the actor was going, so releasing
 *       left while holding right also stops the rightward movement.
 */
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_cmhf_left_off(akgl_Actor *obj, SDL_Event *event);
/**
 * @brief Start the actor moving right, and turn it to face right.
 * @param obj   The actor to move. Required, along with its `basechar`.
 * @param event The event that triggered this. Required, but not read.
 * @return `NULL` on success, otherwise an error context owned by the caller.
 * @throws AKERR_NULLPOINTER If @p obj, @p event, or `obj->basechar` is `NULL`.
 */
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_cmhf_right_on(akgl_Actor *obj, SDL_Event *event);
/**
 * @brief Stop the actor moving right, zeroing everything on the x axis.
 * @param obj   The actor to stop. Required.
 * @param event The event that triggered this. Required, but not read.
 * @return `NULL` on success, otherwise an error context owned by the caller.
 * @throws AKERR_NULLPOINTER If @p obj or @p event is `NULL`.
 */
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_cmhf_right_off(akgl_Actor *obj, SDL_Event *event);
/**
 * @brief Start the actor moving up the screen, and turn it to face up.
 * @param obj   The actor to move. Required, along with its `basechar`, whose y
 *              acceleration is negated -- y grows downward.
 * @param event The event that triggered this. Required, but not read.
 * @return `NULL` on success, otherwise an error context owned by the caller.
 * @throws AKERR_NULLPOINTER If @p obj, @p event, or `obj->basechar` is `NULL`.
 */
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_cmhf_up_on(akgl_Actor *obj, SDL_Event *event);
/**
 * @brief Stop the actor moving up, zeroing everything on the y axis.
 * @param obj   The actor to stop. Required.
 * @param event The event that triggered this. Required, but not read.
 * @return `NULL` on success, otherwise an error context owned by the caller.
 * @throws AKERR_NULLPOINTER If @p obj or @p event is `NULL`.
 * @note This also zeroes `ey`, so an actor under gravity has its accumulated
 *       fall cancelled by releasing a movement key.
 */
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_cmhf_up_off(akgl_Actor *obj, SDL_Event *event);
/**
 * @brief Start the actor moving down the screen, and turn it to face down.
 * @param obj   The actor to move. Required, along with its `basechar`.
 * @param event The event that triggered this. Required, but not read.
 * @return `NULL` on success, otherwise an error context owned by the caller.
 * @throws AKERR_NULLPOINTER If @p obj, @p event, or `obj->basechar` is `NULL`.
 */
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_cmhf_down_on(akgl_Actor *obj, SDL_Event *event);
/**
 * @brief Stop the actor moving down, zeroing everything on the y axis.
 * @param obj   The actor to stop. Required.
 * @param event The event that triggered this. Required, but not read.
 * @return `NULL` on success, otherwise an error context owned by the caller.
 * @throws AKERR_NULLPOINTER If @p obj or @p event is `NULL`.
 * @note Zeroes `ey` as well; see akgl_actor_cmhf_up_off.
 */
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_cmhf_down_off(akgl_Actor *obj, SDL_Event *event);

/**
 * @brief `SDL_EnumerateProperties` callback that applies an akgl_Iterator to one registered actor.
 *
 * Runs the operations the iterator asks for, in a fixed order regardless of the
 * order the bits were set: layer filter, update, tilemap scaling, render. With
 * #AKGL_ITERATOR_OP_LAYERMASK set, an actor on any other layer is skipped
 * entirely; with #AKGL_ITERATOR_OP_TILEMAPSCALE clear, `scale` is forced to 1.0
 * rather than left alone.
 *
 * @param userdata The akgl_Iterator carrying the operation flags, passed through
 *                 by `SDL_EnumerateProperties`. Required despite the `void *`.
 * @param registry #AKGL_REGISTRY_ACTOR, supplied by SDL.
 * @param name     The actor's registry key. Required.
 *
 * @warning This is an SDL callback, so it returns `void` and has nowhere to
 *          propagate an error to. It ends in `FINISH_NORETURN`, which logs the
 *          stack trace and then calls libakerror's unhandled-error handler --
 *          whose default implementation **exits the process** with the error
 *          status. An actor whose `updatefunc` fails therefore terminates the
 *          game rather than skipping a frame. Install your own
 *          `akerr_handler_unhandled_error` if that is not what you want.
 */
void akgl_registry_iterate_actor(void *userdata, SDL_PropertiesID registry, const char *name);

#endif // _AKGL_ACTOR_H_
