#ifndef __ENGINE_H__
#define __ENGINE_H__

#include <tabula.h>
#include <alias/data_structure/inline_list.h>
#include <alias/log.h>
#include <alias/ecs.h>
#include <alias/math.h>
#include <alias/color.h>
#include <alias/cpp.h>
#include <alias/gl.h>
#include <alias/transform.h>
#include <alias/physics.h>

// macros
#define ENGINE_STRING(NAME, DEFAULT, HELP)                                                                             \
  const char *NAME = DEFAULT;                                                                                          \
  struct engine_Variable NAME##__var = {                                                                               \
      .tag = engine_Variable_string,                                                                                   \
      .name = #NAME,                                                                                                   \
      .help = HELP,                                                                                                    \
      ._string.ptr = &NAME,                                                                                            \
  };                                                                                                                   \
  static void __attribute__((constructor)) NAME##__register(void) { engine_variable_register(&NAME##__var); }

#define ENGINE_BOOLEAN(NAME, DEFAULT, HELP)                                                                            \
  bool NAME = DEFAULT;                                                                                                 \
  struct engine_Variable NAME##__var = {                                                                               \
      .tag = engine_Variable_boolean,                                                                                  \
      .name = #NAME,                                                                                                   \
      .help = HELP,                                                                                                    \
      ._boolean.ptr = &NAME,                                                                                           \
  };                                                                                                                   \
  static void __attribute__((constructor)) NAME##__register(void) { engine_variable_register(&NAME##__var); }

#define ENGINE_INTEGER(NAME, DEFAULT, HELP)                                                                            \
  int64_t NAME = DEFAULT;                                                                                              \
  struct engine_Variable NAME##__var = {                                                                               \
      .tag = engine_Variable_integer,                                                                                  \
      .name = #NAME,                                                                                                   \
      .help = HELP,                                                                                                    \
      ._integer.ptr = &NAME,                                                                                           \
  };                                                                                                                   \
  static void __attribute__((constructor)) NAME##__register(void) { engine_variable_register(&NAME##__var); }

#define ENGINE_REAL(NAME, DEFAULT, HELP)                                                                               \
  double NAME = DEFAULT;                                                                                               \
  struct engine_Variable NAME##__var = {                                                                               \
      .tag = engine_Variable_real,                                                                                     \
      .name = #NAME,                                                                                                   \
      .help = HELP,                                                                                                    \
      ._real.ptr = &NAME,                                                                                              \
  };                                                                                                                   \
  static void __attribute__((constructor)) NAME##__register(void) { engine_variable_register(&NAME##__var); }

#define ENGINE_ENUM__OPTION2(NAME, VALUE, HELP) {#NAME, VALUE, HELP},
#define ENGINE_ENUM__OPTION(X) ENGINE_ENUM__OPTION2 X

#define ENGINE_UNWRAP(...) __VA_ARGS__

#define ENGINE_ENUM__FIELD2(NAME1, NAME2, VALUE, HELP) NAME1 ## _ ## NAME2 = VALUE,
#define ENGINE_ENUM__FIELD(NAME, X) ALIAS_CPP_DEFER_2(ENGINE_ENUM__FIELD2_ID)()(NAME, ENGINE_UNWRAP X)

#define ENGINE_ENUM__FIELD2_ID() ENGINE_ENUM__FIELD2

#define ENGINE_ENUM(NAME, DEFAULT, HELP, ...)                                                                          \
  int64_t NAME = DEFAULT;                                                                                              \
  enum { \
		ALIAS_CPP_EVAL(ALIAS_CPP_MAP2(ENGINE_ENUM__FIELD, NAME, __VA_ARGS__)) \
	}; \
  static const struct engine_Variable_EnumeratorOption NAME##__options[] = {                                           \
      ALIAS_CPP_EVAL(ALIAS_CPP_MAP(ENGINE_ENUM__OPTION, __VA_ARGS__)){0, 0, 0}};                                       \
  struct engine_Variable NAME##__var = {                                                                               \
      .tag = engine_Variable_enumerator,                                                                               \
      .name = #NAME,                                                                                                   \
      .help = HELP,                                                                                                    \
      ._enumerator.ptr = &NAME,                                                                                        \
      ._enumerator.options = NAME##__options,                                                                          \
  };                                                                                                                   \
  static void __attribute__((constructor)) NAME##__register(void) { engine_variable_register(&NAME##__var); }

#define ENGINE_COMMAND(NAME, HELP) \
	static void NAME##__f(int argc, char const * const * argv); \
	static struct engine_Command NAME##__cmd = { .name=#NAME, .help=HELP, .f=NAME##__f }; \
	static void __attribute__((constructor)) NAME##__register(void) { engine_command_register(&NAME##__cmd); } \
	static void NAME##__f(int argc, char const * const * argv)

#define ENGINE_LAZY_GLOBAL(TYPE, IDENT, ...)                                                                           \
  TYPE IDENT(void) {                                                                                                   \
    static TYPE inner;                                                                                                 \
    static int init = 0;                                                                                               \
    if(init == 0) {                                                                                                    \
      engine_trace("making " #IDENT " ...");                                                                           \
      __VA_ARGS__                                                                                                      \
      init = 1;                                                                                                        \
    }                                                                                                                  \
    return inner;                                                                                                      \
  }

#define ENGINE_LAZY_GLOBAL_PTR(TYPE, IDENT, ...)                                                                       \
  TYPE *IDENT(void) {                                                                                                  \
    static TYPE inner;                                                                                                 \
    static TYPE *__ptr = NULL;                                                                                         \
    if(__ptr == NULL) {                                                                                                \
      engine_trace("making " #IDENT " ...");                                                                           \
      __VA_ARGS__                                                                                                      \
      __ptr = &inner;                                                                                                  \
    }                                                                                                                  \
    return __ptr;                                                                                                      \
  }

#define ENGINE_ECS(F, ...) assert(ALIAS_ECS_SUCCESS == alias_ecs_##F(__VA_ARGS__))

#define ENGINE_DEFINE_FONT(IDENT, PATH) LAZY_GLOBAL_PTR(Font, IDENT, inner = LoadFont(PATH);)

#define ENGINE_DECLARE_COMPONENT(IDENT, ...)                                                                           \
  struct IDENT __VA_ARGS__;                                                                                            \
  alias_ecs_ComponentHandle IDENT##_component(void);                                                                   \
  const struct IDENT *IDENT##_read(alias_ecs_EntityHandle entity);                                                     \
  struct IDENT *IDENT##_write(alias_ecs_EntityHandle entity);

#define ENGINE_DEFINE_COMPONENT(IDENT, ...)                                                                            \
  ENGINE_LAZY_GLOBAL(alias_ecs_ComponentHandle, IDENT##_component,                                                     \
                     ENGINE_ECS(register_component, engine_ecs(),                                                      \
                                &(alias_ecs_ComponentCreateInfo){.size = sizeof(struct IDENT), ##__VA_ARGS__},         \
                                &inner);)                                                                              \
  const struct IDENT *IDENT##_read(alias_ecs_EntityHandle entity) {                                                    \
    const struct IDENT *ptr;                                                                                           \
    alias_ecs_read_entity_component(engine_ecs(), entity, IDENT##_component(), (const void **)&ptr);                   \
    return ptr;                                                                                                        \
  }                                                                                                                    \
  struct IDENT *IDENT##_write(alias_ecs_EntityHandle entity) {                                                         \
    struct IDENT *ptr;                                                                                                 \
    alias_ecs_write_entity_component(engine_ecs(), entity, IDENT##_component(), (void **)&ptr);                        \
    return ptr;                                                                                                        \
  }

#define ENGINE_DECLARE_TAG_COMPONENT(IDENT) alias_ecs_ComponentHandle IDENT##_component(void);

#define ENGINE_DEFINE_TAG_COMPONENT(IDENT)                                                                             \
  ENGINE_LAZY_GLOBAL(                                                                                                  \
      alias_ecs_ComponentHandle, IDENT##_component,                                                                    \
      ENGINE_ECS(register_component, engine_ecs(), &(alias_ecs_ComponentCreateInfo){.size = 0}, &inner);)

#define ENGINE_COMPONENT_impl(IDENT, ITYPE, DREF, ...)                                                                 \
  ENGINE_LAZY_GLOBAL(alias_ecs_ComponentHandle, IDENT##_component,                                                     \
                     ENGINE_ECS(register_component, engine_ecs(),                                                      \
                                &(alias_ecs_ComponentCreateInfo){.size = sizeof(ITYPE), ##__VA_ARGS__}, &inner);)      \
  const struct IDENT *IDENT##_read(alias_ecs_EntityHandle entity) {                                                    \
    const ITYPE *ptr;                                                                                                  \
    alias_ecs_read_entity_component(engine_ecs(), entity, IDENT##_component(), (const void **)&ptr);                   \
    return DREF ptr;                                                                                                   \
  }                                                                                                                    \
  struct IDENT *IDENT##_write(alias_ecs_EntityHandle entity) {                                                         \
    ITYPE *ptr;                                                                                                        \
    alias_ecs_write_entity_component(engine_ecs(), entity, IDENT##_component(), (void **)&ptr);                        \
    return DREF ptr;                                                                                                   \
  }

#define ENGINE_COMPONENT(IDENT, ...) ENGINE_COMPONENT_impl(IDENT, struct IDENT, , ##__VA_ARGS__)

#define ENGINE_SPAWN_COMPONENT(...) ENGINE_SPAWN_COMPONENT_ __VA_ARGS__
#define ENGINE_SPAWN_COMPONENT_(TYPE, ...)                                                                             \
  {.component = TYPE##_component(), .stride = sizeof(struct TYPE), .data = (void *)&(struct TYPE){__VA_ARGS__}},

#define ENGINE_SPAWN_LAYER(LAYER, ...)                                                                                 \
  ({                                                                                                                   \
    alias_ecs_EntityHandle _entity;                                                                                    \
    alias_ecs_EntitySpawnComponent _components[] = {                                                                   \
        ALIAS_CPP_EVAL(ALIAS_CPP_MAP(ENGINE_SPAWN_COMPONENT, __VA_ARGS__))};                                           \
    ENGINE_ECS(spawn, engine_ecs(),                                                                                    \
               &(alias_ecs_EntitySpawnInfo){.layer = LAYER,                                                            \
                                            .count = 1,                                                                \
                                            .num_components = sizeof(_components) / sizeof(_components[0]),            \
                                            .components = _components},                                                \
               &_entity);                                                                                              \
    _entity;                                                                                                           \
  })

#define ENGINE_SPAWN(...) SPAWN_LAYER(ALIAS_ECS_INVALID_LAYER, ##__VA_ARGS__)

#define ENGINE_BUNDLE_COMPONENT(BUNDLE, IDENT)                                                                         \
  static inline alias_ecs_ComponentHandle alias_##IDENT##_component(void) { return BUNDLE()->IDENT##_component; }      \
  static inline const struct alias_##IDENT *alias_##IDENT##_read(alias_ecs_EntityHandle entity) {                      \
    const struct alias_##IDENT *ptr;                                                                                   \
    alias_ecs_read_entity_component(engine_ecs(), entity, BUNDLE()->IDENT##_component, (const void **)&ptr);           \
    return ptr;                                                                                                        \
  }                                                                                                                    \
  static inline struct alias_##IDENT *alias_##IDENT##_write(alias_ecs_EntityHandle entity) {                           \
    struct alias_##IDENT *ptr;                                                                                         \
    alias_ecs_write_entity_component(engine_ecs(), entity, BUNDLE()->IDENT##_component, (void **)&ptr);                \
    return ptr;                                                                                                        \
  }

#define ALIAS_CPP_EQ__ENGINE_QUERYstate_state(...) ALIAS_CPP_PROBE
#define ALIAS_CPP_EQ__ENGINE_QUERYpre_pre(...) ALIAS_CPP_PROBE
#define ALIAS_CPP_EQ__ENGINE_QUERYread_read(...) ALIAS_CPP_PROBE
#define ALIAS_CPP_EQ__ENGINE_QUERYwrite_write(...) ALIAS_CPP_PROBE
#define ALIAS_CPP_EQ__ENGINE_QUERYoptional_optional(...) ALIAS_CPP_PROBE
#define ALIAS_CPP_EQ__ENGINE_QUERYexclude_exclude(...) ALIAS_CPP_PROBE
#define ALIAS_CPP_EQ__ENGINE_QUERYmodified_modified(...) ALIAS_CPP_PROBE
#define ALIAS_CPP_EQ__ENGINE_QUERYaction_action(...) ALIAS_CPP_PROBE
#define ALIAS_CPP_EQ__ENGINE_QUERYpost_post(...) ALIAS_CPP_PROBE

#define ENGINE_QUERY_is_state(X) ALIAS_CPP_EQ(ENGINE_QUERY, state, X)
#define ENGINE_QUERY_is_pre(X) ALIAS_CPP_EQ(ENGINE_QUERY, pre, X)
#define ENGINE_QUERY_is_read(X) ALIAS_CPP_EQ(ENGINE_QUERY, read, X)
#define ENGINE_QUERY_is_write(X) ALIAS_CPP_EQ(ENGINE_QUERY, write, X)
#define ENGINE_QUERY_is_filter(X)                                                                                      \
  ALIAS_CPP_OR(ALIAS_CPP_OR(ALIAS_CPP_EQ(ENGINE_QUERY, optional, X), ALIAS_CPP_EQ(ENGINE_QUERY, exclude, X)),          \
               ALIAS_CPP_EQ(ENGINE_QUERY, modified, X))
#define ENGINE_QUERY_is_action(X) ALIAS_CPP_EQ(ENGINE_QUERY, action, X)
#define ENGINE_QUERY_is_post(X) ALIAS_CPP_EQ(ENGINE_QUERY, post, X)

#define ENGINE_QUERY_emit(X) ALIAS_CPP_CAT(ENGINE_QUERY_emit_, X)
#define ENGINE_QUERY_emit_state(TYPE, NAME, ...) TYPE NAME;
#define ENGINE_QUERY_emit_write(TYPE, NAME) struct TYPE *NAME = (struct TYPE *)data[__i++];
#define ENGINE_QUERY_emit_read(TYPE, NAME) const struct TYPE *NAME = (const struct TYPE *)data[__i++];
#define ENGINE_QUERY_emit_pre(...) __VA_ARGS__
#define ENGINE_QUERY_emit_action(...) __VA_ARGS__
#define ENGINE_QUERY_emit_post(...) __VA_ARGS__

#define ENGINE_QUERY_emit_create(X) ALIAS_CPP_CAT(ENGINE_QUERY_emit_create_, X)
#define ENGINE_QUERY_emit_create_read(TYPE, NAME) TYPE##_component(),
#define ENGINE_QUERY_emit_create_write(TYPE, NAME) TYPE##_component(),
#define ENGINE_QUERY_emit_create_optional(TYPE) {.component = TYPE##_component(), .filter = ALIAS_ECS_FILTER_OPTIONAL},
#define ENGINE_QUERY_emit_create_exclude(TYPE) {.component = TYPE##_component(), .filter = ALIAS_ECS_FILTER_EXCLUDE},
#define ENGINE_QUERY_emit_create_modified(TYPE) {.component = TYPE##_component(), .filter = ALIAS_ECS_FILTER_MODIFIED},

#define ENGINE_QUERY(NAME, ...) ALIAS_CPP_EVAL(ENGINE_QUERY_impl(NAME, __VA_ARGS__))
#define ENGINE_QUERY_impl(NAME, ...)                                                                                   \
  struct ALIAS_CPP_CAT(NAME, _state) {                                                                                 \
    alias_ecs_Query *query;                                                                                            \
    ALIAS_CPP_FILTER_MAP(ENGINE_QUERY_is_state, ENGINE_QUERY_emit, __VA_ARGS__)                                        \
  };                                                                                                                   \
  static void ALIAS_CPP_CAT(NAME, _do)(void *ud, alias_ecs_Instance *instance, alias_ecs_EntityHandle entity,          \
                                       void **data) {                                                                  \
    uint32_t __i = 0;                                                                                                  \
    struct ALIAS_CPP_CAT(NAME, _state) *state = (struct ALIAS_CPP_CAT(NAME, _state) *)ud;                              \
    (void)state;                                                                                                       \
    ALIAS_CPP_FILTER_MAP(ENGINE_QUERY_is_write, ENGINE_QUERY_emit, __VA_ARGS__)                                        \
    ALIAS_CPP_FILTER_MAP(ENGINE_QUERY_is_read, ENGINE_QUERY_emit, __VA_ARGS__)                                         \
    ALIAS_CPP_FILTER_MAP(ENGINE_QUERY_is_action, ENGINE_QUERY_emit, __VA_ARGS__)                                       \
  }                                                                                                                    \
  void NAME(void) {                                                                                                    \
    static struct ALIAS_CPP_CAT(NAME, _state) _state = {0};                                                            \
    static struct ALIAS_CPP_CAT(NAME, _state) *state = &_state;                                                        \
    if(state->query == NULL) {                                                                                         \
      alias_ecs_ComponentHandle _rlist[] = {                                                                           \
          ALIAS_CPP_FILTER_MAP(ENGINE_QUERY_is_read, ENGINE_QUERY_emit_create, __VA_ARGS__)};                          \
      alias_ecs_ComponentHandle _wlist[] = {                                                                           \
          ALIAS_CPP_FILTER_MAP(ENGINE_QUERY_is_write, ENGINE_QUERY_emit_create, __VA_ARGS__)};                         \
      alias_ecs_QueryFilterCreateInfo _flist[] = {                                                                     \
          ALIAS_CPP_FILTER_MAP(ENGINE_QUERY_is_filter, ENGINE_QUERY_emit_create, __VA_ARGS__)};                        \
      alias_ecs_create_query(engine_ecs(),                                                                             \
                             &(alias_ecs_QueryCreateInfo){.num_write_components = sizeof(_wlist) / sizeof(_wlist[0]),  \
                                                          .write_components = _wlist,                                  \
                                                          .num_read_components = sizeof(_rlist) / sizeof(_rlist[0]),   \
                                                          .read_components = _rlist,                                   \
                                                          .num_filters = sizeof(_flist) / sizeof(_flist[0]),           \
                                                          .filters = _flist},                                          \
                             &state->query);                                                                           \
    }                                                                                                                  \
    ALIAS_CPP_FILTER_MAP(ENGINE_QUERY_is_pre, ENGINE_QUERY_emit, __VA_ARGS__)                                          \
    alias_ecs_execute_query(engine_ecs(), state->query, (alias_ecs_QueryCB){ALIAS_CPP_CAT(NAME, _do), state});         \
    ALIAS_CPP_FILTER_MAP(ENGINE_QUERY_is_post, ENGINE_QUERY_emit, __VA_ARGS__)                                         \
  }

#define ENGINE_INPUT_SIGNAL_PASS(BINDING)                                                                              \
  (struct engine_input_Signal) { .type = engine_input_Signal_pass, .bindings[0] = BINDING }
#define ENGINE_INPUT_SIGNAL_UP(BINDING)                                                                                \
  (struct engine_input_Signal) { .type = engine_input_Signal_up, .bindings[0] = BINDING }
#define ENGINE_INPUT_SIGNAL_DOWN(BINDING)                                                                              \
  (struct engine_input_Signal) { .type = engine_input_Signal_down, .bindings[0] = BINDING }
#define ENGINE_INPUT_SIGNAL_DIRECTION(BINDING_X, BINDING_Y)                                                            \
  (struct engine_input_Signal) {                                                                                       \
    .type = engine_input_Signal_direction, .bindings[0] = BINDING_X, .bindings[1] = BINDING_Y                          \
  }
#define ENGINE_INPUT_SIGNAL_POINT(BINDING_X, BINDING_Y)                                                                \
  (struct engine_input_Signal) { .type = engine_input_Signal_point, .bindings[0] = BINDING_X, .bindings[1] = BINDING_Y }
#define ENGINE_INPUT_SIGNAL_VIEWPORT_POINT(BINDING_X, BINDING_Y, CAMERA)                                               \
  (struct engine_input_Signal) {                                                                                       \
    .type = engine_input_Signal_viewportPoint, .bindings[0] = BINDING_X, .bindings[1] = BINDING_Y,                     \
    .click_camera = CAMERA                                                                                             \
  }

// structures and enumerations
struct engine_State {
  void (*begin)(void *ud);
  void (*frame)(void *ud);
  void (*ui)(void *ud);
  void (*pause)(void *ud);
  void (*unpause)(void *ud);
  void (*end)(void *ud);
  void *ud;
  struct engine_State *_prev;
};

struct engine_Variable_EnumeratorOption {
  const char *name;
  int64_t value;
  const char *help;
};

struct engine_Variable {
  enum {
    engine_Variable_undefined,
    engine_Variable_string,
    engine_Variable_boolean,
    engine_Variable_integer,
    engine_Variable_real,
    engine_Variable_enumerator
  } tag;
  const char *name;
  const char *help;
  const char *_value;
  union {
    struct {
      const char **ptr;
      bool is_cloned;
    } _string;
    struct {
      bool *ptr;
    } _boolean;
    struct {
      int64_t *ptr;
    } _integer;
    struct {
      double *ptr;
    } _real;
    struct {
      int64_t *ptr;
      const struct engine_Variable_EnumeratorOption *options;
    } _enumerator;
  };
};

struct engine_Command {
  const char *name;
  const char *help;
  void (*f)(int argc, char const * const *);
};

struct engine_Image {
  const char *path;
  uint32_t resource_id;
  struct LoadedResource *resource;
};

enum engine_input_Device {
  engine_input_Device_keyboardApostrophe,
  engine_input_Device_keyboardComma,
  engine_input_Device_keyboardMinus,
  engine_input_Device_keyboardPeriod,
  engine_input_Device_keyboardSlash,
  engine_input_Device_keyboard0,
  engine_input_Device_keyboard1,
  engine_input_Device_keyboard2,
  engine_input_Device_keyboard3,
  engine_input_Device_keyboard4,
  engine_input_Device_keyboard5,
  engine_input_Device_keyboard6,
  engine_input_Device_keyboard7,
  engine_input_Device_keyboard8,
  engine_input_Device_keyboard9,
  engine_input_Device_keyboardSemicolon,
  engine_input_Device_keyboardEqual,
  engine_input_Device_keyboardA,
  engine_input_Device_keyboardB,
  engine_input_Device_keyboardC,
  engine_input_Device_keyboardD,
  engine_input_Device_keyboardE,
  engine_input_Device_keyboardF,
  engine_input_Device_keyboardG,
  engine_input_Device_keyboardH,
  engine_input_Device_keyboardI,
  engine_input_Device_keyboardJ,
  engine_input_Device_keyboardK,
  engine_input_Device_keyboardL,
  engine_input_Device_keyboardM,
  engine_input_Device_keyboardN,
  engine_input_Device_keyboardO,
  engine_input_Device_keyboardP,
  engine_input_Device_keyboardQ,
  engine_input_Device_keyboardR,
  engine_input_Device_keyboardS,
  engine_input_Device_keyboardT,
  engine_input_Device_keyboardU,
  engine_input_Device_keyboardV,
  engine_input_Device_keyboardW,
  engine_input_Device_keyboardX,
  engine_input_Device_keyboardY,
  engine_input_Device_keyboardZ,
  engine_input_Device_keyboardSpace,
  engine_input_Device_keyboardEscape,
  engine_input_Device_keyboardEnter,
  engine_input_Device_keyboardTab,
  engine_input_Device_keyboardBackspace,
  engine_input_Device_keyboardInsert,
  engine_input_Device_keyboardDelete,
  engine_input_Device_keyboardRight,
  engine_input_Device_keyboardLeft,
  engine_input_Device_keyboardDown,
  engine_input_Device_keyboardUp,
  engine_input_Device_keyboardPageUp,
  engine_input_Device_keyboardPageDown,
  engine_input_Device_keyboardHome,
  engine_input_Device_keyboardEnd,
  engine_input_Device_keyboardCapsLock,
  engine_input_Device_keyboardScrollLock,
  engine_input_Device_keyboardNumLock,
  engine_input_Device_keyboardPrintScreen,
  engine_input_Device_keyboardPause,
  engine_input_Device_keyboardF1,
  engine_input_Device_keyboardF2,
  engine_input_Device_keyboardF3,
  engine_input_Device_keyboardF4,
  engine_input_Device_keyboardF5,
  engine_input_Device_keyboardF6,
  engine_input_Device_keyboardF7,
  engine_input_Device_keyboardF8,
  engine_input_Device_keyboardF9,
  engine_input_Device_keyboardF10,
  engine_input_Device_keyboardF11,
  engine_input_Device_keyboardF12,
  engine_input_Device_keyboardLeftShift,
  engine_input_Device_keyboardLeftControl,
  engine_input_Device_keyboardLeftAlt,
  engine_input_Device_keyboardLeftMeta,
  engine_input_Device_keyboardRightShift,
  engine_input_Device_keyboardRightControl,
  engine_input_Device_keyboardRightAlt,
  engine_input_Device_keyboardRightMeta,
  engine_input_Device_keyboardMenu,
  engine_input_Device_keyboardLeftBracket,
  engine_input_Device_keyboardBackslash,
  engine_input_Device_keyboardRightBracket,
  engine_input_Device_keyboardGrave,
  engine_input_Device_keyboardPad0,
  engine_input_Device_keyboardPad1,
  engine_input_Device_keyboardPad2,
  engine_input_Device_keyboardPad3,
  engine_input_Device_keyboardPad4,
  engine_input_Device_keyboardPad5,
  engine_input_Device_keyboardPad6,
  engine_input_Device_keyboardPad7,
  engine_input_Device_keyboardPad8,
  engine_input_Device_keyboardPad9,
  engine_input_Device_keyboardPadPeriod,
  engine_input_Device_keyboardPadDivide,
  engine_input_Device_keyboardPadMultiply,
  engine_input_Device_keyboardPadMinus,
  engine_input_Device_keyboardPadAdd,
  engine_input_Device_keyboardPadEnter,
  engine_input_Device_keyboardPadEqual,

  engine_input_Device_mouseLeftButton,
  engine_input_Device_mouseRightButton,
  engine_input_Device_mousePositionX,
  engine_input_Device_mousePositionY,

  engine_input_Device_COUNT
};

struct engine_input_BackendPair {
  enum engine_input_Device device;
  uint32_t binding;
};

struct engine_input_Signal {
  enum {
    engine_input_Signal_pass,
    engine_input_Signal_up,
    engine_input_Signal_down,
    engine_input_Signal_direction,
    engine_input_Signal_point,
    engine_input_Signal_viewportPoint
  } type;

  // inputs
  uint32_t bindings[2];
  union {
    alias_ecs_EntityHandle click_camera;
  };

  // output
  union {
    bool boolean;
    alias_pga2d_Direction direction;
    alias_pga2d_Point point;
  };
  union {
    bool up;
    bool down;
  } internal;
};

ENGINE_DECLARE_COMPONENT(engine_Camera, {
  alias_pga2d_Point viewport_min;
  alias_pga2d_Point viewport_max;
  alias_R zoom;
})

ENGINE_DECLARE_COMPONENT(engine_DrawRectangle, {
  float width;
  float height;
  alias_Color color;
})

ENGINE_DECLARE_COMPONENT(engine_DrawCircle, {
  alias_R radius;
  alias_Color color;
})

ENGINE_DECLARE_COMPONENT(engine_DrawText, {
  const char *text;
  alias_R size;
  alias_Color color;
})

ENGINE_DECLARE_COMPONENT(engine_Sprite, {
  struct engine_Image *image;
  alias_R s0, t0, s1, t1;
  alias_Color color;
})

// constants
static void engine_Camera_init(struct engine_Camera *cam) {
  cam->viewport_max = alias_pga2d_point(1, 1);
  cam->viewport_min = alias_pga2d_point(0, 0);
  cam->zoom = 1;
}

// variable declarations
extern struct alias_gl_ShaderResource engine_r_time;
extern struct alias_gl_ShaderResource engine_r_model_matrix;
extern struct alias_gl_ShaderResource engine_r_view_matrix;
extern struct alias_gl_ShaderResource engine_r_model_view_matrix;
extern struct alias_gl_ShaderResource engine_r_projection_matrix;
extern struct alias_gl_ShaderResource engine_r_view_projection_matrix;
extern struct alias_gl_ShaderResource engine_r_model_view_projection_matrix;

// functions
void engine_state_pop(void);
void engine_state_push(struct engine_State *state);

alias_ecs_Instance *engine_ecs(void);

alias_TransformBundle *engine_ecs_transform(void);
alias_Physics2DBundle *engine_ecs_physics_2d(void);

ENGINE_BUNDLE_COMPONENT(engine_ecs_transform, Translation2D)
ENGINE_BUNDLE_COMPONENT(engine_ecs_transform, Rotation2D)
ENGINE_BUNDLE_COMPONENT(engine_ecs_transform, Transform2D)
ENGINE_BUNDLE_COMPONENT(engine_ecs_transform, LocalToWorld2D)
ENGINE_BUNDLE_COMPONENT(engine_ecs_transform, Parent2D)

ENGINE_BUNDLE_COMPONENT(engine_ecs_physics_2d, Physics2DMotion)
ENGINE_BUNDLE_COMPONENT(engine_ecs_physics_2d, Physics2DBodyMotion)
ENGINE_BUNDLE_COMPONENT(engine_ecs_physics_2d, Physics2DMass)
ENGINE_BUNDLE_COMPONENT(engine_ecs_physics_2d, Physics2DDampen)
ENGINE_BUNDLE_COMPONENT(engine_ecs_physics_2d, Physics2DGravity)

struct engine_State *engine_application_entryPoint(void);

#define engine_error ALIAS_ERROR
#define engine_warning ALIAS_WARNING
#define engine_info ALIAS_INFO
#define engine_debug ALIAS_DEBUG
#define engine_trace ALIAS_TRACE

void engine_variable_register(struct engine_Variable *);

const char *engine_variable_get(const char *name, const char *_default);
int64_t engine_variable_getInteger(const char *name, int64_t _default);
double engine_variable_getReal(const char *name, double _default);

void engine_variable_set(const char *name, const char *value);
void engine_variable_setInteger(const char *name, int64_t value);
void engine_variable_setReal(const char *name, double value);

void engine_command_register(struct engine_Command *);

void engine_input_setPlayerBackend(uint32_t player_index, const struct engine_input_BackendPair *pairs,
                                   uint32_t pair_count);

uint32_t engine_input_addFrontend(uint32_t player_index, struct engine_input_Signal *signals, uint32_t signal_count);
void engine_input_removeFrontend(uint32_t player_index, uint32_t index);

void engine_ui_alignFractions(float x, float y);

static inline void engine_ui_topLeft(void) { engine_ui_alignFractions(0, 0); }
static inline void engine_ui_top(void) { engine_ui_alignFractions(0.5, 0); }
static inline void engine_ui_topRight(void) { engine_ui_alignFractions(1, 0); }
static inline void engine_ui_left(void) { engine_ui_alignFractions(0, 0.5); }
static inline void engine_ui_center(void) { engine_ui_alignFractions(0.5, 0.5); }
static inline void engine_ui_right(void) { engine_ui_alignFractions(1, 0.5); }
static inline void engine_ui_bottomLeft(void) { engine_ui_alignFractions(0, 1); }
static inline void engine_ui_bottom(void) { engine_ui_alignFractions(0.5, 1); }
static inline void engine_ui_bottomRight(void) { engine_ui_alignFractions(1, 1); }

void engine_ui_image(struct engine_Image *);

void engine_ui_vertical(void);
void engine_ui_horizontal(void);
void engine_ui_stack(void);
void engine_ui_end(void);

void engine_ui_fontSize(alias_R size);
void engine_ui_fontColor(alias_Color color);
void engine_ui_text(const char *format, ...);

#endif