/*
 * Prelude for file-scope fragments in docs/20 and docs/21 that assume the
 * galaga example's own declarations already exist -- the shared structures
 * from examples/galaga/galaga.h and the helpers a fragment calls but does not
 * define. The types are copied rather than included so a fragment compiles
 * against exactly what the chapter has shown so far; the helper declarations
 * are invented prototypes, per the prelude policy in MAINTENANCE.md.
 */
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include <SDL3/SDL.h>
#include <SDL3_ttf/SDL_ttf.h>

#include <akerror.h>
#include <akstdlib.h>

#include <akgl/actor.h>
#include <akgl/character.h>
#include <akgl/controller.h>
#include <akgl/draw.h>
#include <akgl/error.h>
#include <akgl/game.h>
#include <akgl/heap.h>
#include <akgl/physics.h>
#include <akgl/registry.h>
#include <akgl/renderer.h>
#include <akgl/sprite.h>
#include <akgl/text.h>
#include <akgl/ui.h>
#include <akgl/util.h>

#include <akbasic/environment.h>
#include <akbasic/error.h>
#include <akbasic/host.h>
#include <akbasic/runtime.h>
#include <akbasic/sink.h>

#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
#define GALAGA_MAX_ENEMY_SHOTS	8
#define GALAGA_ES_ENTERING	(1 << 0)
#define GALAGA_ES_FORMATION	(1 << 1)
#define GALAGA_ES_DIVING	(1 << 2)

typedef struct galaga_Enemy
{
    int32_t  kind;
    int32_t  state;
    float    homex;
    float    homey;
    float    t;
    int32_t  hp;
    int32_t  fire;
    float    rnd;
} galaga_Enemy;

typedef struct galaga_Shared
{
    float    playerx;
    float    playery;
    int32_t  wave;
    float    rnd;
} galaga_Shared;

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

typedef struct galaga_Game
{
    galaga_Screen screen;
    int      frame;
    float    dt;
    bool     autoplay;
    int      score;
    int      lives;
    int      kills[GALAGA_ENEMY_KINDS];
    int      shots[GALAGA_ENEMY_KINDS];
    int      script_errors;
    akgl_Actor *player;
    float    fire_cooldown;
    float    respawn_timer;
    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];

float galaga_random(void);
akerr_ErrorContext AKERR_NOIGNORE *galaga_script_update_enemy(galaga_Enemy *enemy, akgl_Actor *actor, float dt);
akerr_ErrorContext AKERR_NOIGNORE *galaga_boom_spawn(float x, float y);
akerr_ErrorContext AKERR_NOIGNORE *enemy_fire(galaga_Enemy *enemy, akgl_Actor *from);
akerr_ErrorContext AKERR_NOIGNORE *kill_enemy(int index);
akerr_ErrorContext AKERR_NOIGNORE *player_update(akgl_Actor *obj);
akerr_ErrorContext AKERR_NOIGNORE *left_on(akgl_Actor *obj, SDL_Event *event);
akerr_ErrorContext AKERR_NOIGNORE *left_off(akgl_Actor *obj, SDL_Event *event);
akerr_ErrorContext AKERR_NOIGNORE *right_on(akgl_Actor *obj, SDL_Event *event);
akerr_ErrorContext AKERR_NOIGNORE *right_off(akgl_Actor *obj, SDL_Event *event);
akerr_ErrorContext AKERR_NOIGNORE *fire_on(akgl_Actor *obj, SDL_Event *event);
akerr_ErrorContext AKERR_NOIGNORE *fire_off(akgl_Actor *obj, SDL_Event *event);
void shot_box(akgl_Actor *actor, SDL_FRect *dest);
void enemy_box(akgl_Actor *actor, SDL_FRect *dest);
void player_box(akgl_Actor *actor, SDL_FRect *dest);
