#define UI_NUM_GROUPS   1024

static alias_ui * _ui = NULL;
static bool _ui_recording = false;
static alias_ui_OutputGroup _ui_groups[UI_NUM_GROUPS];

static inline void _ui_text_size(alias_ui *_ui, const char * buffer, float size, float _max_width, float * out_width, float * out_height) {
  (void)_ui;
  (void)_max_width;
  _font_measure(&BreeSerif, buffer, size, 0, out_width, out_height);
}

static inline void _ui_text_draw(alias_ui *_ui, const char * buffer, float x, float y, float _width, float size, alias_Color color) {
  (void)_ui;
  (void)_width;
  _font_draw(&BreeSerif, buffer, x, y, size, 0, color);
}

static inline void _ui_start(void) {
  static alias_ui_Input input;

  if(_ui == NULL) {
    // TODO proper init/shutdown
    alias_ui_initialize(NULL, &_ui);
  }

  if(!_ui_recording) {
    input.screen_size.width = _r_width;
    input.screen_size.height = _r_height;
    input.text_size = _ui_text_size;
    input.text_draw = _ui_text_draw;

    alias_ui_begin_frame(_ui, alias_default_MemoryCB(), &input);
    _ui_recording = true;

    alias_ui_begin_stack(_ui);
  }
}

void engine_ui_image(struct engine_Image * img) {
  _ui_start();
  struct LoadedResource * resource = _resource_from_image(img);
  alias_ui_image(_ui, resource->image.width, resource->image.height, 0, 0, 1, 1, resource->id);
}

void engine_ui_alignFractions(float x, float y) {
  _ui_start();
  alias_ui_align(_ui, x, y);
}

void engine_ui_fontSize(alias_R size) {
  _ui_start();
  alias_ui_font_size(_ui, size);
}

void engine_ui_fontColor(alias_Color color) {
  _ui_start();
  alias_ui_font_color(_ui, color);
}

void engine_ui_text(const char * format, ...) {
  _ui_start();

  va_list ap;
  va_start(ap, format);
  alias_ui_textv(_ui, format, ap);
  va_end(ap);
}

void engine_ui_vertical(void) {
  _ui_start();
  alias_ui_begin_vertical_stack(_ui);
}

void engine_ui_horizontal(void) {
  _ui_start();
  alias_ui_begin_horizontal_stack(_ui);
}

void engine_ui_stack(void) {
  _ui_start();
  alias_ui_begin_stack(_ui);
}

void engine_ui_end(void) {
  _ui_start();
  alias_ui_end_stack(_ui);
}

void _ui_frame(void) {
  _state_ui();

  if(!_ui_recording) {
    return;
  }

  alias_ui_end_stack(_ui); // end stack

  uint32_t num_vertexes = 0, num_indexes = 0, num_groups = 0;
  alias_ui_stats(_ui, &num_vertexes, &num_indexes, &num_groups);

  uint32_t * indexes;
  struct ScreenVertex * vertexes;

  _ScreenVertex_begin_draw(num_indexes, &indexes, num_vertexes, &vertexes);

  alias_ui_Output output = {
      .num_groups = 0
    , .max_groups = UI_NUM_GROUPS
    , .groups = _ui_groups
    , .num_indexes = 0
    , .index_sub_buffer = (alias_memory_SubBuffer) {
        .pointer = indexes
      , .count = num_indexes
      , .stride = sizeof(*indexes)
      , .type_format = alias_memory_Format_Uint32
      , .type_length = 1
      }
    , .num_vertexes = 0
    , .xy_sub_buffer = (alias_memory_SubBuffer) {
        .pointer = (uint8_t *)vertexes + offsetof(struct ScreenVertex, xy)
      , .count = num_vertexes
      , .stride = sizeof(*vertexes)
      , .type_format = alias_memory_Format_Float32
      , .type_length = 2
      }
    , .rgba_sub_buffer = (alias_memory_SubBuffer) {
        .pointer = (uint8_t *)vertexes + offsetof(struct ScreenVertex, rgba)
      , .count = num_vertexes
      , .stride = sizeof(*vertexes)
      , .type_format = alias_memory_Format_Float32
      , .type_length = 4
      }
    , .st_sub_buffer = (alias_memory_SubBuffer) {
        .pointer = (uint8_t *)vertexes + offsetof(struct ScreenVertex, st)
      , .count = num_vertexes
      , .stride = sizeof(*vertexes)
      , .type_format = alias_memory_Format_Float32
      , .type_length = 2
      }
    };
  
  alias_ui_end_frame(_ui, alias_default_MemoryCB(), &output);
  _ui_recording = 0;

  for(uint32_t g = 0; g < output.num_groups; g++) {
    uint32_t length = _ui_groups[g].length;
    if(length == 0) {
      continue;
    }

    struct LoadedResource * material = _resource_from_id(_ui_groups[g].texture_id);

    _ScreenVertex_draw(&material->image, _ui_groups[g].index, _ui_groups[g].length);
  }

  _ScreenVertex_end_draw();
}