#include "stb_image.h"

struct Image {
  uint32_t width;
  uint32_t height;
  uint32_t depth;

  uint32_t levels;
  uint32_t layers;

  uint32_t internal_format;

  union {
    struct {
      uint32_t image;
    } gl;
    struct {
      uint64_t image;
      uint64_t memory;
      uint64_t imageview;
    } vk;
  };
};

void _image_upload_2d(struct Image *image, int width, int height, int channels, const void * data, bool generate_mipmap) {
  GLenum internal_format = channels == 3 ? GL_RGB8 : GL_RGBA8;
  GLenum external_format = channels == 3 ? GL_RGB : GL_RGBA;

  int levels = 1, w = width, h = height;
  if(generate_mipmap) {
    while(w > 1 && h > 1) {
      levels++;
      w = w >> (w > 1);
      h = h >> (h > 1);
    }
  }

  glCreateTextures(GL_TEXTURE_2D, 1, &image->gl.image);
  glTextureStorage2D(image->gl.image, levels, internal_format, width, height);
  glTextureSubImage2D(image->gl.image, 0, 0, 0, width, height, external_format, GL_UNSIGNED_BYTE, data);
  if(generate_mipmap) {
    glGenerateTextureMipmap(image->gl.image);
  }
  // TODO use sampler for this
  glTextureParameteri(image->gl.image, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTextureParameteri(image->gl.image, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);

  image->width = width;
  image->height = height;
  image->depth = 1;
  image->levels = levels;
  image->layers = 1;
  image->internal_format = internal_format;
}

void _image_load(struct Image *image, const char *path) {
  int width, height, channels;
  uint8_t *data = stbi_load(path, &width, &height, &channels, 0);

  if(data == NULL) {
    engine_warning("stb_load: loading %s failed.", path);
    return;
  }

  if(channels != 3 && channels != 4) {
    engine_warning("stb_load: invalid channel value %i", channels);
    stbi_image_free(data);
    return;
  }

  engine_trace("stb_load: loaded %s", path);

  _image_upload_2d(image, width, height, channels, data, true);

  stbi_image_free(data);
}

void _image_unload(struct Image *image) {
}