// =====================================================================================================================
//  fennec, a free and open source game engine
//  Copyright © 2025  Medusa Slockbower
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================

///
/// \file debug.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///


#ifndef FENNEC_RENDERERS_VULKAN_LIB_DEBUG_H
#define FENNEC_RENDERERS_VULKAN_LIB_DEBUG_H

#include <fennec/containers/dynarray.h>
#include <fennec/format/format.h>
#include <fennec/lang/ranges.h>
#include <fennec/renderers/vulkan/lib/forward.h>
#include <fennec/renderers/vulkan/lib/enum.h>
#include <fennec/string/string.h>

namespace fennec::vk
{

///
/// \brief Vulkan Debugger Interface
class debugger {

// Definitions =========================================================================================================
public:

	///
	/// \brief Info for attaching a debugger to an instance.
	struct info
		: private VkDebugUtilsMessengerCreateInfoEXT
	{
		explicit info(const debugger& dbg)
			: VkDebugUtilsMessengerCreateInfoEXT {
				.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
				.pNext = nullptr,
				.flags = 0,
				.messageSeverity = debug_message_severity_all,
				.messageType = debug_message_type_all,
				.pfnUserCallback = _debug_message,
				.pUserData = const_cast<debugger*>(&dbg),
			} {
		}

		operator const VkDebugUtilsMessengerCreateInfoEXT*() const {
			return this;
		}
	};


// Properties ==========================================================================================================
public:

	///
	/// \brief Instance Creation Info
	/// \returns An info object for attaching this debugger to an instance.
	info get_info() const {
		return info(*this);
	}


// Debug Callbacks =====================================================================================================
public:

	///
	/// \brief
	/// \param severity The message severity
	/// \param type The message type
	/// \param id The message id
	/// \param message The message string
	/// \param queue The labels in the queue
	/// \param commands The labels in the command buffer
	/// \param objects The related objects
	virtual void message(uint32_t                 severity,
                         uint32_t                 type,
                         const cstring&           id,
                         const cstring&           message,
                         const dynarray<cstring>& queue,
                         const dynarray<cstring>& commands,
                         const dynarray<string>&  objects) = 0;


// Private Helper Functions ============================================================================================
private:

	static VkBool32 _debug_message(
		VkDebugUtilsMessageSeverityFlagBitsEXT      messageSeverity,
		VkDebugUtilsMessageTypeFlagsEXT             messageType,
		const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
		void*                                       pUserData
	) {
		const cstring id       = cstring(pCallbackData->pMessageIdName, strlen(pCallbackData->pMessageIdName));
		const cstring message  = cstring(pCallbackData->pMessage,       strlen(pCallbackData->pMessage));

		dynarray<cstring> queue;
		dynarray<cstring> commands;
		dynarray<string>  objects;

		debugger* dbg = static_cast<debugger*>(pUserData);

		for (const auto& label : range(pCallbackData->pQueueLabels, pCallbackData->pQueueLabels + pCallbackData->queueLabelCount)) {
			queue.emplace_back(label.pLabelName, strlen(label.pLabelName));
		}

		for (const auto& label : range(pCallbackData->pCmdBufLabels, pCallbackData->pCmdBufLabels + pCallbackData->cmdBufLabelCount)) {
			commands.emplace_back(label.pLabelName, strlen(label.pLabelName));
		}

		for (const auto& obj : range(pCallbackData->pObjects, pCallbackData->pObjects + pCallbackData->objectCount)) {
			objects.push_back(format("{:#016x} ({})", obj.objectHandle, object_string(obj.objectType)));
		}

		dbg->message(messageSeverity, messageType, id, message, queue, commands, objects);

		return VK_FALSE;
	}
};

}

#endif // FENNEC_RENDERERS_VULKAN_LIB_DEBUG_H