/*
 * 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/.
 */

#pragma once

#include "Data/Containers/Array.hpp"
#include <functional>
#include <memory>
#include <vector>
#include <vulkan/vulkan.hpp>

namespace OpenVulkano::Vulkan
{
	class Device;
	class MemoryAllocation;
	class ManagedBuffer;

	struct ManagedBufferDeleter
	{
		void operator()(ManagedBuffer* buffer);
	};

	class MemoryPool
	{
		Device* device;
		std::vector<std::unique_ptr<MemoryAllocation>> allocations;
		Array<std::vector<ManagedBuffer*>> toFree;
		std::vector<ManagedBuffer*> recycleBuffers;
		std::function<void(ManagedBuffer*)> freeFunction;

		uint64_t currentBuffer = 0;

		void DoFree(ManagedBuffer* buffer);

		bool FreeBuffer(ManagedBuffer* buffer);
	public:
		using ManagedBufferPtr = std::unique_ptr<ManagedBuffer, ManagedBufferDeleter>;

		MemoryPool() = default;

		~MemoryPool();

		void Init(Device* dev, int bufferCount);

		void Close();

		void StartFrame(uint64_t bufferId);

		MemoryAllocation* CreateMemoryAllocation(size_t size, uint32_t type, bool addToCache = true);

		MemoryAllocation* GetFreeMemoryAllocation(size_t size, vk::DeviceSize alignment, uint32_t type, bool createIfAllFull = true);

		ManagedBufferPtr CreateBuffer(vk::DeviceSize size, const vk::BufferUsageFlags& usage, const vk::MemoryPropertyFlags& properties);

		ManagedBufferPtr CreateSharedMemoryBuffer(size_t size);

		static void ReleaseBuffer(ManagedBuffer* buffer);
	};
}