/**
 * @file galaga.h
 * @brief Shared declarations for the GALAGA embedding example.
 *
 * The engine is C on libakgl; the enemies think in BASIC. Everything the two
 * sides share crosses in exactly one place: the three structures below, which
 * script.c registers as host types so a script reads and writes them directly.
 * docs/20-tutorial-galaga.md and docs/21-tutorial-galaga-enemies.md build this
 * program from an empty file; the split between files follows the split
 * between chapters.
 */

#ifndef _GALAGA_H_
#define _GALAGA_H_

#include <stdbool.h>
#include <stdint.h>

#include <akerror.h>

#include <akgl/actor.h>

/* ------------------------------------------------------------- geometry --- */

/*
 * The view is sized to the artwork rather than the other way round: the Kenney
 * sprites are ~100 pixels wide, libakgl has no way to draw a sprite smaller
 * than it is (akgl_Actor.scale is overwritten every frame -- libakgl
 * docs/12-actors.md), and a ten-column formation of them needs 1120 pixels.
 */
#define GALAGA_VIEW_WIDTH	1280
#define GALAGA_VIEW_HEIGHT	960

#define GALAGA_FORM_COLUMNS	10	/* formation width, in slots	*/
#define GALAGA_FORM_LEFT	136	/* x of column 0, map pixels	*/
#define GALAGA_FORM_TOP		120	/* y of row 0, map pixels	*/
#define GALAGA_COL_PITCH	112
#define GALAGA_ROW_PITCH	100

#define GALAGA_PLAYER_Y		860.0f
#define GALAGA_PLAYER_SPEED	420.0f	/* map pixels per second	*/
#define GALAGA_PLAYER_MARGIN	60.0f	/* how close to the edge it may go */

/* ------------------------------------------------------------- entities --- */

#define GALAGA_ENEMY_BEE	0
#define GALAGA_ENEMY_BUTTERFLY	1
#define GALAGA_ENEMY_BOSS	2
#define GALAGA_ENEMY_KINDS	3

#define GALAGA_MAX_ENEMIES	40
#define GALAGA_MAX_PLAYER_SHOTS	2	/* the classic two-on-screen rule */
#define GALAGA_MAX_ENEMY_SHOTS	8

/*
 * galaga_Enemy.state bits. The script owns these transitions; the engine only
 * writes the word at spawn and when a script error forces an enemy dumb.
 * galaga.bas spells the same three values as literals, with a REM naming them.
 *
 * 8             0
 * 0 0 0 0 0 1 1 1
 *           | | `-- ENTERING: flying its entry path toward the formation slot
 *           | `---- FORMATION: holding (and breathing around) homex/homey
 *           `------ DIVING: attacking, off the grid until it leaves the screen
 */
#define GALAGA_ES_ENTERING	(1 << 0)
#define GALAGA_ES_FORMATION	(1 << 1)
#define GALAGA_ES_DIVING	(1 << 2)

/** @brief One enemy, as both sides see it. Hangs off akgl_Actor.actorData. */
typedef struct galaga_Enemy
{
    int32_t  kind;	/* GALAGA_ENEMY_BEE / BUTTERFLY / BOSS		*/
    int32_t  state;	/* GALAGA_ES_* bit flags			*/
    float    homex;	/* formation slot, in map pixels		*/
    float    homey;
    float    t;		/* parametric clock for the current maneuver	*/
    int32_t  hp;
    int32_t  fire;	/* outbox: script sets 1, engine consumes	*/
    float    rnd;	/* inbox: fresh 0..1 each call; ROLL% in BASIC	*/
} galaga_Enemy;

/** @brief Frame state every enemy may read. Bound once as GAME@. */
typedef struct galaga_Shared
{
    float    playerx;	/* the player actor's position, this frame	*/
    float    playery;
    int32_t  wave;
    float    rnd;	/* fresh 0..1 each frame; ROLL% to the script	*/
} galaga_Shared;

/* --------------------------------------------------------------- screens --- */

typedef enum
{
    GALAGA_SCREEN_TITLE = 0,
    GALAGA_SCREEN_PLAY,
    GALAGA_SCREEN_GAMEOVER,
    GALAGA_SCREEN_VICTORY
} galaga_Screen;

/* ------------------------------------------------------------ game state --- */

typedef struct galaga_Game
{
    galaga_Screen screen;
    int      frame;
    float    dt;		/* seconds, clamped; see main.c		*/
    bool     autoplay;

    int      score;
    int      lives;
    int      kills[GALAGA_ENEMY_KINDS];
    int      shots[GALAGA_ENEMY_KINDS];	/* shots each kind fired	*/
    int      script_errors;

    akgl_Actor *player;
    float    fire_cooldown;
    float    respawn_timer;	/* > 0 while the player is invulnerable	*/
    bool     firing;
    bool     moveleft;
    bool     moveright;

    int      player_shots_live;
    int      enemy_shots_live;
} galaga_Game;

extern galaga_Game galaga_game;
extern galaga_Shared galaga_shared;

extern galaga_Enemy galaga_enemies[GALAGA_MAX_ENEMIES];
extern akgl_Actor *galaga_enemy_actors[GALAGA_MAX_ENEMIES];

/* ---------------------------------------------------------------- script --- */

akerr_ErrorContext AKERR_NOIGNORE *galaga_script_boot(char *path);
akerr_ErrorContext AKERR_NOIGNORE *galaga_script_update_enemy(galaga_Enemy *enemy, akgl_Actor *actor, float dt);

/* --------------------------------------------------------------- enemies --- */

akerr_ErrorContext AKERR_NOIGNORE *galaga_wave_spawn(void);
akerr_ErrorContext AKERR_NOIGNORE *galaga_wave_release(void);
int galaga_enemies_alive(void);
akerr_ErrorContext AKERR_NOIGNORE *galaga_boom_spawn(float x, float y);

/* ---------------------------------------------------------------- player --- */

akerr_ErrorContext AKERR_NOIGNORE *galaga_player_spawn(void);
akerr_ErrorContext AKERR_NOIGNORE *galaga_player_controls(void);
akerr_ErrorContext AKERR_NOIGNORE *galaga_player_autoplay(int frame);

/** @brief A 0..1 random draw from the engine's own PRNG (see enemies.c). */
float galaga_random(void);

#endif // _GALAGA_H_
