#include "gpu.h"
#include <SDL3/SDL.h>
#include <iostream>

SDL_GPUShader* load_shader(SDL_GPUDevice* dev, const char* path, SDL_GPUShaderStage stage,
                            uint32_t num_samplers, uint32_t num_uniform_buffers) {
  size_t code_size;
  void*  code = SDL_LoadFile(path, &code_size);
  if (!code) {
    std::cerr << "Failed to load " << path << ": " << SDL_GetError() << std::endl;
    return nullptr;
  }

  SDL_GPUShaderCreateInfo info {
    .code_size           = code_size,
    .code                = (uint8_t*)code,
    .entrypoint          = "main",
    .format              = SDL_GPU_SHADERFORMAT_SPIRV,
    .stage               = stage,
    .num_samplers        = num_samplers,
    .num_uniform_buffers = num_uniform_buffers,
  };

  SDL_GPUShader* shader = SDL_CreateGPUShader(dev, &info);
  if (!shader)
    std::cerr << "Shader creation failed (" << path << "): " << SDL_GetError() << std::endl;
  SDL_free(code);
  return shader;
}

SDL_GPUTexture* create_render_texture(SDL_GPUDevice* dev, SDL_Window* win,
                                       uint32_t w, uint32_t h) {
  SDL_GPUTextureCreateInfo info {
    .type   = SDL_GPU_TEXTURETYPE_2D,
    .format = SDL_GetGPUSwapchainTextureFormat(dev, win),
    .usage  = SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_COLOR_TARGET,
    .width  = w,
    .height = h,
    .layer_count_or_depth = 1,
    .num_levels = 1,
  };

  SDL_GPUTexture* tex = SDL_CreateGPUTexture(dev, &info);
  if (!tex)
    std::cerr << "Render texture creation failed: " << SDL_GetError() << std::endl;
  return tex;
}

SDL_GPUSampler* create_linear_sampler(SDL_GPUDevice* dev) {
  SDL_GPUSamplerCreateInfo info {
    .min_filter     = SDL_GPU_FILTER_LINEAR,
    .mag_filter     = SDL_GPU_FILTER_LINEAR,
    .address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
    .address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
  };
  return SDL_CreateGPUSampler(dev, &info);
}

void upload_buffer(SDL_GPUDevice* dev, SDL_GPUBuffer* dst, const void* data, uint32_t size) {
  SDL_GPUTransferBufferCreateInfo trans_info {
    .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD,
    .size  = size,
  };
  SDL_GPUTransferBuffer* trans = SDL_CreateGPUTransferBuffer(dev, &trans_info);

  void* mapped = SDL_MapGPUTransferBuffer(dev, trans, false);
  memcpy(mapped, data, size);
  SDL_UnmapGPUTransferBuffer(dev, trans);

  SDL_GPUCommandBuffer* cmd  = SDL_AcquireGPUCommandBuffer(dev);
  SDL_GPUCopyPass*      copy = SDL_BeginGPUCopyPass(cmd);

  SDL_GPUTransferBufferLocation src { .transfer_buffer = trans, .offset = 0 };
  SDL_GPUBufferRegion           reg { .buffer = dst, .offset = 0, .size = size };
  SDL_UploadToGPUBuffer(copy, &src, &reg, true);

  SDL_EndGPUCopyPass(copy);
  SDL_ReleaseGPUTransferBuffer(dev, trans);
  SDL_SubmitGPUCommandBuffer(cmd);
}
