#include <Vk/Sampler/Sampler.hpp>
#include <Utilities/Utilities.hpp>

std::unique_ptr<Vk::Sampler> Vk::Sampler::m_pixelated;
std::unique_ptr<Vk::Sampler> Vk::Sampler::m_cubemap;
std::unique_ptr<Vk::Sampler> Vk::Sampler::m_bilinear;

Vk::Sampler::Builder::Builder() : m_createInfo{}
{
    m_createInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
    m_createInfo.magFilter = VK_FILTER_NEAREST;
    m_createInfo.minFilter = VK_FILTER_NEAREST;
    m_createInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
    m_createInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
    m_createInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
    m_createInfo.anisotropyEnable = VK_FALSE;
    m_createInfo.maxAnisotropy = 0;
    m_createInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
    m_createInfo.unnormalizedCoordinates = VK_FALSE;
    m_createInfo.compareEnable = VK_FALSE;
    m_createInfo.compareOp = VK_COMPARE_OP_ALWAYS;
    m_createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
    m_createInfo.mipLodBias = 0.0f;
    m_createInfo.minLod = 0.0f;
    m_createInfo.maxLod = 0.0f;
}

Vk::Sampler::Builder& Vk::Sampler::Builder::cubemap()
{
    m_createInfo.magFilter = VK_FILTER_LINEAR;
    m_createInfo.minFilter = VK_FILTER_LINEAR;
    m_createInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
    m_createInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
    m_createInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
    m_createInfo.anisotropyEnable = VK_FALSE;
    m_createInfo.maxAnisotropy = 0;
    m_createInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
    m_createInfo.unnormalizedCoordinates = VK_FALSE;
    m_createInfo.compareEnable = VK_FALSE;
    m_createInfo.compareOp = VK_COMPARE_OP_ALWAYS;
    m_createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
    m_createInfo.mipLodBias = 0.0f;
    m_createInfo.minLod = 0.0f;
    m_createInfo.maxLod = 0.0f;

    return *this;
}

Vk::Sampler::Builder& Vk::Sampler::Builder::bilinear()
{
    m_createInfo.magFilter = VK_FILTER_LINEAR;
    m_createInfo.minFilter = VK_FILTER_LINEAR;

    return *this;
}

std::unique_ptr<Vk::Sampler> Vk::Sampler::Builder::build(std::shared_ptr<Vk::Device> device)
{
    std::unique_ptr<Vk::Sampler> sampler = std::make_unique<Vk::Sampler>();
    sampler->m_device = device;

    if (vkCreateSampler(device->inner(), &m_createInfo, nullptr, &sampler->m_sampler) != VK_SUCCESS)
    {
        crash("Failed to create a texture sampler!");
    }

    return sampler;
}

Vk::Sampler::~Sampler()
{
    vkDestroySampler(m_device->inner(), m_sampler, nullptr);
}

VkSampler Vk::Sampler::inner()
{
    return m_sampler;
}

void Vk::Sampler::createAll(std::shared_ptr<Vk::Device> device)
{
    m_pixelated = Builder().build(device);
    m_cubemap = Builder().cubemap().build(device);
    m_bilinear = Builder().bilinear().build(device);
}

Vk::Sampler& Vk::Sampler::pixelated()
{
    return *m_pixelated;
}

Vk::Sampler& Vk::Sampler::cubemap()
{
    return *m_cubemap;
}

Vk::Sampler& Vk::Sampler::bilinear()
{
    return *m_bilinear;
}