#include "local.h"

static alias_InlineList _free_resources = ALIAS_INLINE_LIST_INIT(_free_resources);
static alias_InlineList _inactive_resources = ALIAS_INLINE_LIST_INIT(_inactive_resources);
static alias_InlineList _active_resources = ALIAS_INLINE_LIST_INIT(_active_resources);
static alias_Vector(struct LoadedResource *) _resources = ALIAS_VECTOR_INIT;

static uint32_t _resource_id = 1, _resource_gen = 1;

static void _free_resource(struct LoadedResource * resource) {
  switch(resource->type) {
  case ResourceType_Image:
    BackendImage_unload(&resource->image);
    break;
  default:
    break;
  }

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

void Engine__resources_gc(void) {
  struct LoadedResource * resource, * next_resource;
  ALIAS_INLINE_LIST_EACH_CONTAINER_SAFE(&_inactive_resources, resource, next_resource, list) {
    _free_resource(resource);
  }
  alias_InlineList_push_list(&_inactive_resources, &_active_resources);
  _resource_gen++;
}

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

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

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

struct LoadedResource * Engine__loaded_resource_by_id(uint32_t id) {
  return _resources.data[id - 1];
}

struct LoadedResource * Engine__load_image(struct 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 = _allocate_resource(ResourceType_Image);
    BackendImage_load(&resource->image, path); 
    img->resource = resource;
    img->resource_id = resource->id;
  } else {
    Engine__touch_resource(img->resource);
  }

  return img->resource;
}