/* Copyright (c) 2019-2025, Arm Limited and Contributors
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * 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.h"

#include "device.h"
#include "image_view.h"

namespace vkb
{
namespace
{
inline VkImageType find_image_type(VkExtent3D extent)
{
	VkImageType result{};

	uint32_t dim_num{0};

	if (extent.width >= 1)
	{
		dim_num++;
	}

	if (extent.height >= 1)
	{
		dim_num++;
	}

	if (extent.depth > 1)
	{
		dim_num++;
	}

	switch (dim_num)
	{
		case 1:
			result = VK_IMAGE_TYPE_1D;
			break;
		case 2:
			result = VK_IMAGE_TYPE_2D;
			break;
		case 3:
			result = VK_IMAGE_TYPE_3D;
			break;
		default:
			throw std::runtime_error("No image type found.");
			break;
	}

	return result;
}
}        // namespace

namespace core
{

Image ImageBuilder::build(vkb::core::DeviceC &device) const
{
	return Image(device, *this);
}

ImagePtr ImageBuilder::build_unique(vkb::core::DeviceC &device) const
{
	return std::make_unique<Image>(device, *this);
}

Image::Image(vkb::core::DeviceC   &device,
             const VkExtent3D     &extent,
             VkFormat              format,
             VkImageUsageFlags     image_usage,
             VmaMemoryUsage        memory_usage,
             VkSampleCountFlagBits sample_count,
             const uint32_t        mip_levels,
             const uint32_t        array_layers,
             VkImageTiling         tiling,
             VkImageCreateFlags    flags,
             uint32_t              num_queue_families,
             const uint32_t       *queue_families) :
    // Pass through to the ImageBuilder ctor
    Image(device,
          ImageBuilder(extent)
              .with_format(format)
              .with_image_type(find_image_type(extent))
              .with_usage(image_usage)
              .with_mip_levels(mip_levels)
              .with_array_layers(array_layers)
              .with_tiling(tiling)
              .with_flags(flags)
              .with_vma_usage(memory_usage)
              .with_sample_count(sample_count)
              .with_queue_families(num_queue_families, queue_families)
              .with_implicit_sharing_mode())
{
}

Image::Image(vkb::core::DeviceC &device, ImageBuilder const &builder) :
    vkb::allocated::AllocatedC<VkImage>{builder.get_allocation_create_info(), VK_NULL_HANDLE, &device}, create_info(builder.get_create_info())
{
	set_handle(create_image(create_info));
	subresource.arrayLayer = create_info.arrayLayers;
	subresource.mipLevel   = create_info.mipLevels;
	if (!builder.get_debug_name().empty())
	{
		set_debug_name(builder.get_debug_name());
	}
}

Image::Image(
    vkb::core::DeviceC &device, VkImage handle, const VkExtent3D &extent, VkFormat format, VkImageUsageFlags image_usage, VkSampleCountFlagBits sample_count) :
    vkb::allocated::AllocatedC<VkImage>{handle, &device}
{
	create_info.extent     = extent;
	create_info.imageType  = find_image_type(extent);
	create_info.format     = format;
	create_info.samples    = sample_count;
	create_info.usage      = image_usage;
	subresource.arrayLayer = create_info.arrayLayers = 1;
	subresource.mipLevel = create_info.mipLevels = 1;
}

Image::Image(Image &&other) noexcept
    :
    vkb::allocated::AllocatedC<VkImage>{std::move(other)}, create_info{std::exchange(other.create_info, {})}, subresource{std::exchange(other.subresource, {})}, views(std::exchange(other.views, {}))
{
	// Update image views references to this image to avoid dangling pointers
	for (auto &view : views)
	{
		view->set_image(*this);
	}
}

Image::~Image()
{
	destroy_image(get_handle());
}

VkImageType Image::get_type() const
{
	return create_info.imageType;
}

const VkExtent3D &Image::get_extent() const
{
	return create_info.extent;
}

VkFormat Image::get_format() const
{
	return create_info.format;
}

VkSampleCountFlagBits Image::get_sample_count() const
{
	return create_info.samples;
}

VkImageUsageFlags Image::get_usage() const
{
	return create_info.usage;
}

VkImageTiling Image::get_tiling() const
{
	return create_info.tiling;
}

const VkImageSubresource &Image::get_subresource() const
{
	return subresource;
}

uint32_t Image::get_array_layer_count() const
{
	return create_info.arrayLayers;
}

std::unordered_set<ImageView *> &Image::get_views()
{
	return views;
}

VkDeviceSize Image::get_image_required_size() const
{
	VkMemoryRequirements memory_requirements;

	vkGetImageMemoryRequirements(get_device().get_handle(), get_handle(), &memory_requirements);

	return memory_requirements.size;
}

VkImageCompressionPropertiesEXT Image::get_applied_compression() const
{
	return query_applied_compression(get_device().get_handle(), get_handle());
}
}        // namespace core
}        // namespace vkb
