#include "precomp.h"
#include "vulkan/graphics.h"

namespace veng {

static VKAPI_ATTR VkBool32 VKAPI_CALL
ValidationCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
                   VkDebugUtilsMessageTypeFlagsEXT messageTypes,
                   const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
                   void* pUserData) {
  if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
    spdlog::warn("Vulkan Validation: {}", pCallbackData->pMessage);
  } else {
    spdlog::error("Vulkan Validation: {}", pCallbackData->pMessage);
  }
  return VK_FALSE;
}

VkDebugUtilsMessengerCreateInfoEXT GetCreateMessengerInfo() {
  VkDebugUtilsMessengerCreateInfoEXT creation_info = {};
  creation_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
  creation_info.messageSeverity =
      VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
      VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
  creation_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
                              VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
  creation_info.pfnUserCallback = ValidationCallback;
  creation_info.pUserData = nullptr;

  return creation_info;
}

std::vector<VkLayerProperties> Graphics::GetSupprotedValidationLayers() {
  std::uint32_t count;
  vkEnumerateInstanceLayerProperties(&count, nullptr);

  if (count == 0) return {};

  std::vector<VkLayerProperties> properties(count);
  vkEnumerateInstanceLayerProperties(&count, properties.data());
  return properties;
}

bool LayerMatchesName(gsl::czstring name, const VkLayerProperties& properties) {
  return streq(properties.layerName, name);
}

bool IsLayerSupported(gsl::span<VkLayerProperties> layers, gsl::czstring name) {
  return std::any_of(layers.begin(), layers.end(),
                     std::bind_front(LayerMatchesName, name));
}

bool Graphics::AreAllLayersSupported(gsl::span<gsl::czstring> layers) {
  std::vector<VkLayerProperties> supported_layers =
      GetSupprotedValidationLayers();

  return std::all_of(layers.begin(), layers.end(),
                     std::bind_front(IsLayerSupported, supported_layers));
}

void Graphics::SetupDebugMessenger() {
  if (!validation_enabled_) return;

  VkDebugUtilsMessengerCreateInfoEXT info = GetCreateMessengerInfo();
  VkResult result = vkCreateDebugUtilsMessengerEXT(instance_, &info, nullptr,
                                                   &debug_messenger_);
  if (result != VK_SUCCESS) {
    spdlog::error("Cannot create debug messenger");
    return;
  }
}

}  // namespace veng
