/* Copyright (c) 2019-2024, 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_view.h"

#include "core/image.h"
#include "device.h"

namespace vkb
{
namespace core
{
ImageView::ImageView(Image &img, VkImageViewType view_type, VkFormat format,
                     uint32_t mip_level, uint32_t array_layer,
                     uint32_t n_mip_levels, uint32_t n_array_layers) :
    VulkanResource{VK_NULL_HANDLE, &img.get_device()},
    image{&img},
    format{format}
{
	if (format == VK_FORMAT_UNDEFINED)
	{
		this->format = format = image->get_format();
	}

	subresource_range.baseMipLevel   = mip_level;
	subresource_range.baseArrayLayer = array_layer;
	subresource_range.levelCount     = n_mip_levels == 0 ? image->get_subresource().mipLevel : n_mip_levels;
	subresource_range.layerCount     = n_array_layers == 0 ? image->get_subresource().arrayLayer : n_array_layers;

	if (is_depth_format(format))
	{
		subresource_range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
	}
	else
	{
		subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
	}

	VkImageViewCreateInfo view_info{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
	view_info.image            = image->get_handle();
	view_info.viewType         = view_type;
	view_info.format           = format;
	view_info.subresourceRange = subresource_range;

	auto result = vkCreateImageView(get_device().get_handle(), &view_info, nullptr, &get_handle());

	if (result != VK_SUCCESS)
	{
		throw VulkanException{result, "Cannot create ImageView"};
	}

	// Register this image view to its image
	// in order to be notified when it gets moved
	image->get_views().emplace(this);
}

ImageView::ImageView(ImageView &&other) :
    VulkanResource{std::move(other)},
    image{other.image},
    format{other.format},
    subresource_range{other.subresource_range}
{
	// Remove old view from image set and add this new one
	auto &views = image->get_views();
	views.erase(&other);
	views.emplace(this);
}

ImageView::~ImageView()
{
	if (has_device())
	{
		vkDestroyImageView(get_device().get_handle(), get_handle(), nullptr);
	}
}

const Image &ImageView::get_image() const
{
	assert(image && "Image view is referring an invalid image");
	return *image;
}

void ImageView::set_image(Image &img)
{
	image = &img;
}

VkFormat ImageView::get_format() const
{
	return format;
}

VkImageSubresourceRange ImageView::get_subresource_range() const
{
	return subresource_range;
}

VkImageSubresourceLayers ImageView::get_subresource_layers() const
{
	VkImageSubresourceLayers subresource{};
	subresource.aspectMask     = subresource_range.aspectMask;
	subresource.baseArrayLayer = subresource_range.baseArrayLayer;
	subresource.layerCount     = subresource_range.layerCount;
	subresource.mipLevel       = subresource_range.baseMipLevel;
	return subresource;
}
}        // namespace core
}        // namespace vkb
