// Copyright 2023 The clvk authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "image_format.hpp"
#include "cl_headers.hpp"
#include "device.hpp"

format_mapping_map FormatMaps = {
    // R formats
    {{CL_R, CL_UNORM_INT8}, VK_FORMAT_R8_UNORM},
    {{CL_R, CL_SNORM_INT8}, VK_FORMAT_R8_SNORM},
    {{CL_R, CL_UNSIGNED_INT8}, VK_FORMAT_R8_UINT},
    {{CL_R, CL_SIGNED_INT8}, VK_FORMAT_R8_SINT},
    {{CL_R, CL_UNORM_INT16}, VK_FORMAT_R16_UNORM},
    {{CL_R, CL_SNORM_INT16}, VK_FORMAT_R16_SNORM},
    {{CL_R, CL_UNSIGNED_INT16}, VK_FORMAT_R16_UINT},
    {{CL_R, CL_SIGNED_INT16}, VK_FORMAT_R16_SINT},
    {{CL_R, CL_HALF_FLOAT}, VK_FORMAT_R16_SFLOAT},
    {{CL_R, CL_UNSIGNED_INT32}, VK_FORMAT_R32_UINT},
    {{CL_R, CL_SIGNED_INT32}, VK_FORMAT_R32_SINT},
    {{CL_R, CL_FLOAT}, VK_FORMAT_R32_SFLOAT},

    // LUMINANCE formats
    {{CL_LUMINANCE, CL_UNORM_INT8},
     {image_format_support::ROWO, VK_FORMAT_R8_UNORM}},
    {{CL_LUMINANCE, CL_SNORM_INT8},
     {image_format_support::ROWO, VK_FORMAT_R8_SNORM}},
    {{CL_LUMINANCE, CL_UNSIGNED_INT8},
     {image_format_support::ROWO, VK_FORMAT_R8_UINT}},
    {{CL_LUMINANCE, CL_SIGNED_INT8},
     {image_format_support::ROWO, VK_FORMAT_R8_SINT}},
    {{CL_LUMINANCE, CL_UNORM_INT16},
     {image_format_support::ROWO, VK_FORMAT_R16_UNORM}},
    {{CL_LUMINANCE, CL_SNORM_INT16},
     {image_format_support::ROWO, VK_FORMAT_R16_SNORM}},
    {{CL_LUMINANCE, CL_UNSIGNED_INT16},
     {image_format_support::ROWO, VK_FORMAT_R16_UINT}},
    {{CL_LUMINANCE, CL_SIGNED_INT16},
     {image_format_support::ROWO, VK_FORMAT_R16_SINT}},
    {{CL_LUMINANCE, CL_HALF_FLOAT},
     {image_format_support::ROWO, VK_FORMAT_R16_SFLOAT}},
    {{CL_LUMINANCE, CL_UNSIGNED_INT32},
     {image_format_support::ROWO, VK_FORMAT_R32_UINT}},
    {{CL_LUMINANCE, CL_SIGNED_INT32},
     {image_format_support::ROWO, VK_FORMAT_R32_SINT}},
    {{CL_LUMINANCE, CL_FLOAT},
     {image_format_support::ROWO, VK_FORMAT_R32_SFLOAT}},

    // INTENSITY formats
    {{CL_INTENSITY, CL_UNORM_INT8},
     {image_format_support::ROWO, VK_FORMAT_R8_UNORM}},
    {{CL_INTENSITY, CL_SNORM_INT8},
     {image_format_support::ROWO, VK_FORMAT_R8_SNORM}},
    {{CL_INTENSITY, CL_UNSIGNED_INT8},
     {image_format_support::ROWO, VK_FORMAT_R8_UINT}},
    {{CL_INTENSITY, CL_SIGNED_INT8},
     {image_format_support::ROWO, VK_FORMAT_R8_SINT}},
    {{CL_INTENSITY, CL_UNORM_INT16},
     {image_format_support::ROWO, VK_FORMAT_R16_UNORM}},
    {{CL_INTENSITY, CL_SNORM_INT16},
     {image_format_support::ROWO, VK_FORMAT_R16_SNORM}},
    {{CL_INTENSITY, CL_UNSIGNED_INT16},
     {image_format_support::ROWO, VK_FORMAT_R16_UINT}},
    {{CL_INTENSITY, CL_SIGNED_INT16},
     {image_format_support::ROWO, VK_FORMAT_R16_SINT}},
    {{CL_INTENSITY, CL_HALF_FLOAT},
     {image_format_support::ROWO, VK_FORMAT_R16_SFLOAT}},
    {{CL_INTENSITY, CL_UNSIGNED_INT32},
     {image_format_support::ROWO, VK_FORMAT_R32_UINT}},
    {{CL_INTENSITY, CL_SIGNED_INT32},
     {image_format_support::ROWO, VK_FORMAT_R32_SINT}},
    {{CL_INTENSITY, CL_FLOAT},
     {image_format_support::ROWO, VK_FORMAT_R32_SFLOAT}},

    // RG formats
    {{CL_RG, CL_UNORM_INT8}, VK_FORMAT_R8G8_UNORM},
    {{CL_RG, CL_SNORM_INT8}, VK_FORMAT_R8G8_SNORM},
    {{CL_RG, CL_UNSIGNED_INT8}, VK_FORMAT_R8G8_UINT},
    {{CL_RG, CL_SIGNED_INT8}, VK_FORMAT_R8G8_SINT},
    {{CL_RG, CL_UNORM_INT16}, VK_FORMAT_R16G16_UNORM},
    {{CL_RG, CL_SNORM_INT16}, VK_FORMAT_R16G16_SNORM},
    {{CL_RG, CL_UNSIGNED_INT16}, VK_FORMAT_R16G16_UINT},
    {{CL_RG, CL_SIGNED_INT16}, VK_FORMAT_R16G16_SINT},
    {{CL_RG, CL_HALF_FLOAT}, VK_FORMAT_R16G16_SFLOAT},
    {{CL_RG, CL_UNSIGNED_INT32}, VK_FORMAT_R32G32_UINT},
    {{CL_RG, CL_SIGNED_INT32}, VK_FORMAT_R32G32_SINT},
    {{CL_RG, CL_FLOAT}, VK_FORMAT_R32G32_SFLOAT},

    // RGB formats
    {{CL_RGB, CL_UNORM_INT8}, VK_FORMAT_R8G8B8_UNORM},
    {{CL_RGB, CL_SNORM_INT8}, VK_FORMAT_R8G8B8_SNORM},
    {{CL_RGB, CL_UNSIGNED_INT8}, VK_FORMAT_R8G8B8_UINT},
    {{CL_RGB, CL_SIGNED_INT8}, VK_FORMAT_R8G8B8_SINT},
    {{CL_RGB, CL_UNORM_INT16}, VK_FORMAT_R16G16B16_UNORM},
    {{CL_RGB, CL_SNORM_INT16}, VK_FORMAT_R16G16B16_SNORM},
    {{CL_RGB, CL_UNSIGNED_INT16}, VK_FORMAT_R16G16B16_UINT},
    {{CL_RGB, CL_SIGNED_INT16}, VK_FORMAT_R16G16B16_SINT},
    {{CL_RGB, CL_HALF_FLOAT}, VK_FORMAT_R16G16B16_SFLOAT},
    {{CL_RGB, CL_UNSIGNED_INT32}, VK_FORMAT_R32G32B32_UINT},
    {{CL_RGB, CL_SIGNED_INT32}, VK_FORMAT_R32G32B32_SINT},
    {{CL_RGB, CL_FLOAT}, VK_FORMAT_R32G32B32_SFLOAT},
    {{CL_RGB, CL_UNORM_SHORT_565}, VK_FORMAT_R5G6B5_UNORM_PACK16},

    // RGBA formats
    {{CL_RGBA, CL_UNORM_INT8}, VK_FORMAT_R8G8B8A8_UNORM},
    {{CL_RGBA, CL_SNORM_INT8}, VK_FORMAT_R8G8B8A8_SNORM},
    {{CL_RGBA, CL_UNSIGNED_INT8}, VK_FORMAT_R8G8B8A8_UINT},
    {{CL_RGBA, CL_SIGNED_INT8}, VK_FORMAT_R8G8B8A8_SINT},
    {{CL_RGBA, CL_UNORM_INT16}, VK_FORMAT_R16G16B16A16_UNORM},
    {{CL_RGBA, CL_SNORM_INT16}, VK_FORMAT_R16G16B16A16_SNORM},
    {{CL_RGBA, CL_UNSIGNED_INT16}, VK_FORMAT_R16G16B16A16_UINT},
    {{CL_RGBA, CL_SIGNED_INT16}, VK_FORMAT_R16G16B16A16_SINT},
    {{CL_RGBA, CL_HALF_FLOAT}, VK_FORMAT_R16G16B16A16_SFLOAT},
    {{CL_RGBA, CL_UNSIGNED_INT32}, VK_FORMAT_R32G32B32A32_UINT},
    {{CL_RGBA, CL_SIGNED_INT32}, VK_FORMAT_R32G32B32A32_SINT},
    {{CL_RGBA, CL_FLOAT}, VK_FORMAT_R32G32B32A32_SFLOAT},

    // BGRA formats
    {{CL_BGRA, CL_UNORM_INT8}, VK_FORMAT_B8G8R8A8_UNORM},
    {{CL_BGRA, CL_SNORM_INT8}, VK_FORMAT_B8G8R8A8_SNORM},
    {{CL_BGRA, CL_UNSIGNED_INT8}, VK_FORMAT_B8G8R8A8_UINT},
    {{CL_BGRA, CL_SIGNED_INT8}, VK_FORMAT_B8G8R8A8_SINT},
};

const format_mapping_map& get_format_maps() { return FormatMaps; }

static bool get_equivalent_bgra_format_for_image1d_buffer(
    VkFormat& fmt, VkComponentMapping* components_sampled,
    VkComponentMapping* components_storage) {
    const std::unordered_map<VkFormat, VkFormat> map = {
        {VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM},
    };
    const VkComponentMapping BGRA_mapping = {
        VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_IDENTITY,
        VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_IDENTITY};

    auto it = map.find(fmt);
    if (it != map.end()) {
        fmt = it->second;
        *components_sampled = *components_storage = BGRA_mapping;
        return true;
    }
    return false;
}

static void get_component_mappings_for_channel_order(
    cl_channel_order order, VkComponentMapping* components_sampled,
    VkComponentMapping* components_storage) {
    if (order == CL_LUMINANCE) {
        *components_sampled = {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R,
                               VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_A};
    } else if (order == CL_INTENSITY) {
        *components_sampled = {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R,
                               VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R};
    } else {
        *components_sampled = {
            VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
            VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY};
    }

    *components_storage = {
        VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
        VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY};
}

bool cl_image_format_to_vulkan_format(cl_image_format clformat,
                                      cl_mem_object_type image_type,
                                      cvk_device* device,
                                      image_format_support* fmt_support,
                                      VkComponentMapping* components_sampled,
                                      VkComponentMapping* components_storage) {
    if (device->is_image_format_disabled(clformat)) {
        return false;
    }

    auto m = FormatMaps.find(clformat);
    if (m == FormatMaps.end()) {
        return false;
    }
    *fmt_support = (*m).second;

    get_component_mappings_for_channel_order(
        clformat.image_channel_order, components_sampled, components_storage);

    if (image_type == CL_MEM_OBJECT_IMAGE1D_BUFFER &&
        clformat.image_channel_order == CL_BGRA &&
        device->is_bgra_format_not_supported_for_image1d_buffer()) {
        get_equivalent_bgra_format_for_image1d_buffer(
            fmt_support->vkfmt, components_sampled, components_storage);
    }

    return true;
}
