/**
 * @file sprite.h
 * @brief Declares the sprite backend: where SPRITE, MOVSPR and SPRSAV land.
 *
 * The same shape as akbasic/graphics.h and for the same reason -- the core
 * library builds with no SDL and no libakgl present, so a sprite verb calls
 * through a record of function pointers rather than reaching for akgl_actor_*.
 * akbasic_sprite_init_akgl() lives in the separate akbasic_akgl target.
 *
 * **A sprite is a Commodore sprite, not a libakgl sprite.** libakgl's has a
 * sheet, a frame list, an animation speed and a state-to-sprite map, all of
 * which arrive from a JSON document; BASIC 7.0 has a word for none of it. What
 * BASIC has is eight numbered slots, each holding one still image with a
 * position, a colour, a visibility flag and two expansion bits. So the JSON
 * layer is bypassed rather than reached through: the adaptor builds the same
 * structures directly and hands the interpreter this much smaller surface.
 *
 * The one place the two overlap is loading an image from a file, which is why
 * `define_file` is here: a program that has art on disk should be able to say so,
 * and libakgl already resolves a relative asset path and decodes the image. That
 * path is not bypassed -- it is exactly akgl_spritesheet_initialize().
 *
 * A runtime with no sprite backend is the normal case, so every verb that needs
 * one refuses with AKBASIC_ERR_DEVICE. The verbs that only touch interpreter
 * state -- SPRCOLOR, and the RSP* readbacks -- work regardless, which is what
 * makes them testable without a device.
 */

#ifndef _AKBASIC_SPRITE_H_
#define _AKBASIC_SPRITE_H_

#include <akerror.h>

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

/** @brief Sprites a program has. Eight, as on a C128; the verbs number them 1-8. */
#define AKBASIC_MAX_SPRITES		8

/**
 * @brief The collision shapes SPRHIT understands.
 *
 * **Deliberately libakgl's own numbers**, so the two documents agree and nobody
 * has to maintain a mapping between them. A kind this interpreter has no use for
 * is still refused by name rather than silently accepted.
 */
#define AKBASIC_SHAPE_NONE		0
#define AKBASIC_SHAPE_BOX		1
#define AKBASIC_SHAPE_CIRCLE		2
#define AKBASIC_SHAPE_CAPSULE_X		3
#define AKBASIC_SHAPE_CAPSULE_Y		4
/** @brief One past the last kind, for the range check. */
#define AKBASIC_SHAPE_LAST		5

/**
 * @brief Static collision rectangles a program can register with SOLID.
 *
 * Sixty-four, which is the smallest power of two that holds a sixty-brick wall
 * with headroom. The ceiling matters because these come out of libakgl's
 * collision proxy pool, which is shared with whatever host this interpreter is
 * embedded in: eight sprites plus sixty-four solids is seventy-two of
 * `AKGL_MAX_HEAP_COLLISION_PROXY`, and the rest is the host's.
 */
#ifndef AKBASIC_MAX_SOLIDS
#define AKBASIC_MAX_SOLIDS		64
#endif

/** @brief Width of a Commodore sprite in pixels. */
#define AKBASIC_SPRITE_WIDTH		24
/** @brief Height of a Commodore sprite in pixels. */
#define AKBASIC_SPRITE_HEIGHT		21
/**
 * @brief Bytes in a sprite pattern: three per row, twenty-one rows.
 *
 * The C128 documents SPRSAV's string as the SSHAPE data format at a fixed 24x21,
 * which is 63 bytes of bitmap plus a four-byte header. Only the bitmap is
 * carried here -- the header records a width and height that are constants for a
 * sprite -- so a DIMmed integer array of this many elements is a whole pattern.
 */
#define AKBASIC_SPRITE_PATTERN_BYTES	63

/**
 * @brief Fastest MOVSPR speed, and how far one unit of it travels.
 *
 * `MOVSPR n, angle # speed` takes 0-15 on a C128 and the reference manual does
 * not say what a unit is in pixels; it says 15 is fastest and 0 stops. So the
 * mapping is ours: one unit is #AKBASIC_SPRITE_SPEED_PIXELS_PER_SECOND device
 * pixels per second, which puts speed 15 at four seconds to cross a 320-pixel
 * screen and proportionally longer across a wider one. Recorded as a deviation
 * in TODO.md section 5 rather than presented as fidelity.
 */
#define AKBASIC_SPRITE_MAX_SPEED	15
/** @brief Pixels per second one unit of MOVSPR speed is worth. See #AKBASIC_SPRITE_MAX_SPEED. */
#define AKBASIC_SPRITE_SPEED_PIXELS_PER_SECOND	5.0

/**
 * @brief One sprite, as BASIC sees it.
 *
 * Every field here is something a BASIC verb sets and an RSP* function reads
 * back, which is why the whole thing lives on the runtime rather than on the
 * device: a host that swaps one renderer for another does not expect the
 * program's sprite positions to go with it. The device is told about changes; it
 * is never asked what they were.
 */
typedef struct
{
    bool defined;		/* SPRSAV has given this slot an image		*/
    bool enabled;		/* SPRITE n, 1					*/
    bool behind;		/* priority: drawn behind the drawing surface	*/
    bool xexpand;		/* SPRITE's x-expand bit			*/
    bool yexpand;
    bool multicolour;
    int colorindex;		/* 1-16, the sprite's own colour		*/
    double x;			/* position in device pixels; see graphics.h	*/
    double y;
    double angle;		/* MOVSPR's continuous motion: degrees		*/
    int speed;			/*   clockwise from vertical, and 0-15		*/

    /**
     * SPRHIT's collision shape: one of the `AKBASIC_SHAPE_*` kinds, and a
     * rectangle measured from the sprite's top-left corner.
     *
     * `shapeexplicit` is what separates "the program asked for the whole frame"
     * from "the program has not said". They are the same shape today and will
     * not stay that way: a frame-fitting shape has to be rebuilt when the
     * picture or an expansion bit changes, and one the program gave in pixels
     * must not be.
     */
    int shapekind;
    double shapex1;
    double shapey1;
    double shapex2;
    double shapey2;
    bool shapeexplicit;
} akbasic_Sprite;

/**
 * @brief One rectangle registered by SOLID, as BASIC sees it.
 *
 * The id is the array index plus one and is the program's own number, not a
 * minted handle. A brick in a sixty-brick wall has a natural index; making the
 * program carry one back out of the interpreter would be inventing a second
 * numbering for something that already has one.
 */
typedef struct
{
    bool active;
    double x1;
    double y1;
    double x2;
    double y2;
} akbasic_Solid;

/**
 * @brief What sprite n last collided with, and how.
 *
 * Narrower than libakgl's `akgl_Contact` on purpose. No actor pointers, because
 * BASIC has no actor; no tile fields, because there is no tilemap; no z, because
 * every test is planar. And **no `dt` and no `sensor`**, which libakgl documents
 * as filled in by the resolver -- this interpreter never resolves anything, so
 * they would come back zero and mean nothing. An always-zero field in a reference
 * table is a lie.
 *
 * `axis` is the one field that is not the library's. It is the minimum
 * translation axis -- which way to reverse -- and it is here because working it
 * out from the normal is exactly where this dialect's left-operand arithmetic
 * rule catches people, and because it is the single field that deletes the most
 * BASIC.
 */
typedef struct
{
    int what;			/* 0 nothing yet, 1 a sprite, 2 static geometry	*/
    int other;			/* the other sprite's number, or the SOLID id	*/
    double nx;			/* unit normal, out of the other and toward this	*/
    double ny;
    double depth;		/* how far in, in pixels			*/
    double px;			/* where they touched				*/
    double py;
    int axis;			/* 1 reverse x, 2 reverse y			*/
} akbasic_Contact;

/**
 * @brief The sprite verbs' own state, which lives on the runtime.
 *
 * `bumped` accumulates rather than reporting an instant. A collision that
 * happens between two BUMP() calls is still news when the second one asks, and a
 * program polling once a frame would otherwise miss anything that started and
 * ended inside a frame. BUMP() clears what it reports, which is what a C128 does
 * and what makes "has this sprite hit anything since I last looked" answerable.
 */
typedef struct
{
    akbasic_Sprite sprites[AKBASIC_MAX_SPRITES];
    akbasic_Solid solids[AKBASIC_MAX_SOLIDS];
    akbasic_Contact contacts[AKBASIC_MAX_SPRITES];
    int sharedcolor1;		/* SPRCOLOR's two multicolour registers, 1-16	*/
    int sharedcolor2;
    uint16_t bumped;		/* bit n-1 set when sprite n has collided	*/
    uint16_t bumpedsolid;	/*   and when it has met static geometry	*/
    int64_t lastservicems;	/* when MOVSPR's continuous motion last moved	*/
} akbasic_SpriteState;

/**
 * @brief Where the sprite verbs land.
 *
 * Sprites are numbered 1 through #AKBASIC_MAX_SPRITES at this boundary, the same
 * way a BASIC program numbers them. Translating to a zero-based slot is the
 * backend's business, and doing it here would mean two numbering schemes in one
 * file for no gain.
 *
 * There is deliberately no entry point for the RSP* readbacks. Everything they
 * report is in #akbasic_SpriteState, which the interpreter owns, so asking the
 * device would be asking it to repeat what it was told.
 */
typedef struct akbasic_SpriteBackend
{
    void *self;

    /**
     * Install a pattern into sprite @p n from packed bitmap bytes.
     *
     * Three bytes per row, most significant bit leftmost, twenty-one rows --
     * the layout a C128 keeps in its sprite block, and what a type-in listing's
     * DATA statements hold. @p fg is what a set bit draws and @p bg what a clear
     * one does; a clear bit is normally transparent, which is @p bg with a zero
     * alpha.
     */
    akerr_ErrorContext AKERR_NOIGNORE *(*define)(struct akbasic_SpriteBackend *self, int n, const uint8_t *pattern, int bytes, akbasic_Color fg, akbasic_Color bg);
    /**
     * Install a pattern into sprite @p n from a region SSHAPE saved.
     *
     * The handle is the graphics backend's, not this one's -- SSHAPE puts it in
     * a BASIC string and SPRSAV hands it straight back. An adaptor that serves
     * both devices resolves it; one that does not may refuse with
     * AKBASIC_ERR_DEVICE.
     */
    akerr_ErrorContext AKERR_NOIGNORE *(*define_shape)(struct akbasic_SpriteBackend *self, int n, int handle);
    /**
     * Install a pattern into sprite @p n by loading an image file.
     *
     * @p path is what the program wrote, resolved by the backend rather than
     * here: the interpreter has no idea what a path means to the host, and the
     * akgl adaptor resolves it exactly as libakgl resolves a spritesheet named
     * by a sprite document. @p root is the directory to fall back to when the
     * path does not resolve against the working directory, normally the
     * directory the running program was loaded from; it may be NULL.
     *
     * Unlike the other two, this one does not force 24x21: the image's own size
     * is the sprite's size. A modern PC has no reason to throw away art that
     * does not happen to be sprite-shaped.
     */
    akerr_ErrorContext AKERR_NOIGNORE *(*define_file)(struct akbasic_SpriteBackend *self, int n, const char *path, const char *root);
    /** Show or hide sprite @p n. */
    akerr_ErrorContext AKERR_NOIGNORE *(*show)(struct akbasic_SpriteBackend *self, int n, bool visible);
    /** Put sprite @p n at (@p x, @p y), in device pixels. */
    akerr_ErrorContext AKERR_NOIGNORE *(*move)(struct akbasic_SpriteBackend *self, int n, double x, double y);
    /**
     * Set sprite @p n's own colour, its expansion bits and its priority.
     *
     * One call rather than four because `SPRITE` sets them together and a
     * backend that has to rebuild a texture to change a colour would otherwise
     * do it four times for one statement.
     */
    akerr_ErrorContext AKERR_NOIGNORE *(*configure)(struct akbasic_SpriteBackend *self, int n, akbasic_Color color, bool xexpand, bool yexpand, bool behind);
    /** Set the two multicolour registers every multicolour sprite shares. */
    akerr_ErrorContext AKERR_NOIGNORE *(*shared_colors)(struct akbasic_SpriteBackend *self, akbasic_Color c1, akbasic_Color c2);
    /**
     * Report which sprites are currently overlapping another sprite.
     *
     * Bit n-1 of @p mask is set when sprite n overlaps at least one other. The
     * interpreter calls this once per step, ORs the result into
     * akbasic_SpriteState::bumped, and raises the collision interrupt when
     * anything is set -- so a backend answers about *now* and does not have to
     * remember anything.
     */
    akerr_ErrorContext AKERR_NOIGNORE *(*collisions)(struct akbasic_SpriteBackend *self, uint16_t *mask);
    /**
     * Give sprite @p n a collision shape.
     *
     * @p kind is an #AKBASIC_SHAPE_NONE .. #AKBASIC_SHAPE_CAPSULE_Y, and the
     * rectangle is two corners measured from the sprite's top-left, in device
     * pixels -- the same form `BOX` and `SSHAPE` take, because a dialect with two
     * spellings for a rectangle is a dialect nobody can write from memory.
     *
     * Optional. A backend that withholds it makes `SPRHIT` refuse by name, which
     * is what a device that cannot do what was asked is supposed to do.
     */
    akerr_ErrorContext AKERR_NOIGNORE *(*shape)(struct akbasic_SpriteBackend *self, int n, int kind,
						double x1, double y1, double x2, double y2);
    /**
     * Register, replace or retire static collision geometry @p id.
     *
     * @p define false retires it and the rectangle is ignored. Rectangles are in
     * window pixels -- the coordinates `BOX` draws in and `MOVSPR` positions in,
     * with `SCALE` not applying, exactly as for a sprite.
     *
     * Optional, like `shape`. A backend that withholds it makes `SOLID` refuse
     * by name.
     */
    akerr_ErrorContext AKERR_NOIGNORE *(*solid)(struct akbasic_SpriteBackend *self, int id, bool define,
						double x1, double y1, double x2, double y2);
    /**
     * Report which sprites are overlapping static geometry.
     *
     * Bit n-1 is set when sprite n overlaps at least one rectangle `solid`
     * registered. Answered from the same pass as `collisions`, so the two are
     * always about the same instant whichever order they are called in.
     *
     * Optional. A runtime whose backend withholds it reports no static
     * collisions rather than refusing, exactly as one with no backend at all
     * reports none -- a step is not a statement and has no line to blame.
     */
    akerr_ErrorContext AKERR_NOIGNORE *(*solids)(struct akbasic_SpriteBackend *self, uint16_t *mask);
    /**
     * Describe sprite @p n's deepest collision from the scan just run.
     *
     * @p found comes back false when that sprite hit nothing, and @p dest is
     * then untouched. Deepest rather than first, because the deepest is the one
     * a program has to undo.
     *
     * A pure accessor: it reads what the same pass that answered `collisions`
     * and `solids` already worked out. That is why the contract on `collisions`
     * says the two are about the same instant -- this depends on it.
     *
     * Optional. Withholding it makes `RCOLLISION` report nothing rather than
     * refuse, the way a runtime with no backend at all does.
     */
    akerr_ErrorContext AKERR_NOIGNORE *(*contact)(struct akbasic_SpriteBackend *self, int n,
						  akbasic_Contact *dest, bool *found);
} akbasic_SpriteBackend;

/**
 * @brief Reset the sprite state to its power-on values.
 *
 * Eight undefined, hidden sprites at the origin in white, the two shared
 * multicolour registers at their C128 defaults, and nothing bumped. NEW comes
 * back through here.
 *
 * @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_sprite_state_init(akbasic_SpriteState *obj);

/**
 * @brief Unpack 63 bytes of sprite bitmap into one pixel per byte.
 *
 * Three bytes per row, most significant bit leftmost. @p dest receives
 * #AKBASIC_SPRITE_WIDTH * #AKBASIC_SPRITE_HEIGHT entries, each 0 or 1. Shared by
 * every backend that has to turn a pattern into pixels, and separate from them
 * so the bit order is asserted once rather than trusted eight times.
 *
 * @param pattern Packed bitmap; at least @p bytes readable.
 * @param bytes How many bytes @p pattern holds; must be #AKBASIC_SPRITE_PATTERN_BYTES.
 * @param dest Output destination populated by the function; 504 entries.
 * @return `NULL` on success, otherwise an error context owned by the caller.
 * @throws AKERR_NULLPOINTER When either pointer is NULL.
 * @throws AKBASIC_ERR_BOUNDS When `bytes` is not #AKBASIC_SPRITE_PATTERN_BYTES.
 */
akerr_ErrorContext AKERR_NOIGNORE *akbasic_sprite_unpack(const uint8_t *pattern, int bytes, uint8_t *dest);

struct akbasic_Runtime;

/**
 * @brief Move every sprite MOVSPR set in continuous motion.
 *
 * Called once per akbasic_runtime_step(), before the line runs, and paced off
 * akbasic_runtime_settime() -- exactly the way the PLAY queue is paced, and for
 * the same reason: this library owns no loop and must not block, so the caller
 * that owns the frame owns the clock.
 *
 * A host that never sets the time leaves it at zero and nothing moves, which is
 * a still picture rather than a hang.
 *
 * @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_sprite_service(struct akbasic_Runtime *obj);

/**
 * @brief Ask the device what is overlapping, and raise the collision interrupt.
 *
 * Also called once per step. What it finds is ORed into
 * akbasic_SpriteState::bumped for BUMP() to report, and raised as
 * #AKBASIC_INTERRUPT_SPRITE -- unconditionally, because an unarmed interrupt
 * records nothing and there is therefore nothing to ask first.
 *
 * A runtime with no sprite device, or one whose backend withholds `collisions`,
 * does nothing here rather than refusing: a step is not a statement and has no
 * program line to blame.
 *
 * @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_collision_service(struct akbasic_Runtime *obj);

#endif // _AKBASIC_SPRITE_H_
