/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

#include "MetalTextureCache.h"
#include "Vulkan/Renderer.hpp"

namespace OpenVulkano::Vulkan
{
	void MetalTextureCache::Init(IRenderer* renderer)
	{
		if (m_resourceManager) Close();
		if (!renderer) return;
		m_renderer = dynamic_cast<Vulkan::Renderer*>(renderer);
		if (!m_renderer) throw std::invalid_argument("The Metal Texture Cache only supports Vulkan renderer!");
		// Setup texture cache
		VkExportMetalDeviceInfoEXT metalDeviceInfo {};
		metalDeviceInfo.sType = VK_STRUCTURE_TYPE_EXPORT_METAL_DEVICE_INFO_EXT;
		metalDeviceInfo.pNext = nullptr;
		VkExportMetalObjectsInfoEXT exportInfo { VK_STRUCTURE_TYPE_EXPORT_METAL_OBJECTS_INFO_EXT, &metalDeviceInfo };
		VkDevice vkDevice = m_renderer->GetContext().device->device;
		vkExportMetalObjectsEXT(vkDevice, &exportInfo);
		CVReturn result = CVMetalTextureCacheCreate(nil, nil, metalDeviceInfo.mtlDevice, nil, &m_textureCache);
		if (result != kCVReturnSuccess)
		{
			Logger::AR->error("Failed to create metal texture cache! Status code: {}", result);
		}
		m_resourceManager = &m_renderer->GetResourceManager();
		m_renderer->RegisterCloseable(this);
	}

	Scene::Texture* MetalTextureCache::Get(CVPixelBufferRef pixelBuffer, MTLPixelFormat pixelFormat)
	{
		CVMetalTextureRef mtlTextureRef;
		auto width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
		auto height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
		//TODO check pixel format from buffer?
		CVMetalTextureCacheCreateTextureFromImage(nil, m_textureCache, pixelBuffer, nil, pixelFormat, width, height, 0, &mtlTextureRef);
		id<MTLTexture> mtlTexture = CVMetalTextureGetTexture(mtlTextureRef);
		CVBufferRelease(mtlTextureRef);
		MetalBackedTexture& texture = m_mtlToVkTextureMap[static_cast<void*>(mtlTexture)];
		if (!texture)
		{ // Init texture
			texture.Init(m_resourceManager, mtlTexture, false);
			Logger::RENDER->info("Metal Texture Cache grew to: {}", m_mtlToVkTextureMap.size());
		}
		return &texture;
	}

	void MetalTextureCache::ReturnTexture(Scene::Texture* texture)
	{

	}

	void MetalTextureCache::Close()
	{
		m_mtlToVkTextureMap.clear();
		m_renderer->UnregisterCloseable(this);
		m_renderer = nullptr;
		m_resourceManager = nullptr;
		//TODO delete the texture cache object?
		m_textureCache = nullptr;
	}
}
