enum ResourceType { ResourceType_invalid, ResourceType_image, ResourceType_font };

struct LoadedResource {
  alias_InlineList list;

  enum ResourceType type;
  uint32_t id, gen;

  union {
    struct Image image;
    struct Font font;
  };
};

static alias_InlineList _resource_list_free = ALIAS_INLINE_LIST_INIT(_resource_list_free);
static alias_InlineList _resource_list_inactive = ALIAS_INLINE_LIST_INIT(_resource_list_inactive);
static alias_InlineList _resource_list_active = ALIAS_INLINE_LIST_INIT(_resource_list_active);
static alias_Vector(struct LoadedResource *) _resource_list = ALIAS_VECTOR_INIT;
static uint32_t _resource_id = 1, _resource_gen = 1;

static void _resource_free(struct LoadedResource * resource) {
  switch(resource->type) {
  case ResourceType_image:
    _image_unload(&resource->image);
    break;
  default:
    break;
  }

  alias_InlineList_remove_self(&resource->list);
  alias_InlineList_push(&_resource_list_free, &resource->list);
  _resource_list.data[resource->id - 1] = NULL;
}

static void _resource_gc(void) {
  struct LoadedResource * resource, * next_resource;
  ALIAS_INLINE_LIST_EACH_CONTAINER_SAFE(&_resource_list_inactive, resource, next_resource, list) {
    _resource_free(resource);
  }
  alias_InlineList_push_list(&_resource_list_inactive, &_resource_list_active);
  _resource_gen++;
}


static struct LoadedResource * _resource_allocate(enum ResourceType type) {
  struct LoadedResource * resource;
  if(alias_InlineList_is_empty(&_resource_list_free)) {
    resource = alias_malloc(alias_default_MemoryCB(), sizeof(*resource), alignof(*resource));
    alias_InlineList_init(&resource->list);
    resource->id = _resource_id++;

    alias_Vector_space_for(&_resource_list, alias_default_MemoryCB(), 1);
    *alias_Vector_push(&_resource_list) = NULL;
  } else {
    resource = ALIAS_INLINE_LIST_CONTAINER(alias_InlineList_pop(&_resource_list_free), struct LoadedResource, list);
  }
  alias_InlineList_push(&_resource_list_active, &resource->list);
  resource->type = type;
  resource->gen = _resource_gen;
  _resource_list.data[resource->id - 1] = resource;
  return resource;
}

static void _resource_touch(struct LoadedResource * resource) {
  if(resource->gen != _resource_gen) {
    alias_InlineList_remove_self(&resource->list);
    alias_InlineList_push(&_resource_list_active, &resource->list);
    resource->gen = _resource_gen;
  }
}

static struct LoadedResource * _resource_from_image(struct engine_Image *img) {
  struct LoadedResource * resource;

  if(img->resource == NULL || img->resource_id != img->resource->id) {
    char path[1024];
    sprintf(path, sizeof(path), "assets/%s", img->path);

    resource = _resource_allocate(ResourceType_image);
    _image_load(&resource->image, path); 
    img->resource = resource;
    img->resource_id = resource->id;
  } else {
    _resource_touch(img->resource);
  }

  return img->resource;
}

static struct LoadedResource * _resource_from_id(uint32_t id) {
  return _resource_list.data[id - 1];
}