/*
 * 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 "Base/Render/IRenderer.hpp"
#include "Base/UI/IWindow.hpp"
#include "Base/Logger.hpp"
#include "Context.hpp"
#include "UiRenderer.hpp"
#include "Resources/ResourceManager.hpp"
#include "Data/ReadOnlyAtomicArrayQueue.hpp"
#include "CommandHelper.hpp"
#include "Base/EngineConfiguration.hpp"
#include "Resources/ResourceManager.hpp"
#include "DepthBufferQuery.hpp"
#include <vector>
#include <thread>

namespace OpenVulkano::Vulkan
{
	struct WaitSemaphores
	{
		std::vector<vk::Semaphore> renderReady, renderComplete;
	};

	class Renderer final : public IRenderer
	{
		Context context;
		std::shared_ptr<spdlog::logger> logger;
		std::vector<vk::Semaphore> semaphores;
		std::vector<WaitSemaphores> waitSemaphores;
		Scene::Scene* scene = nullptr;
		ResourceManager resourceManager;
		uint32_t currentImageId = -1;
		std::vector<std::thread> threadPool;
		std::vector<std::vector<CommandHelper>> commands;
		std::vector<std::vector<vk::CommandBuffer>> submitBuffers;
		UiRenderer uiRenderer;
		std::vector<ICloseable*> closeables;
		DepthBufferQuery depthBufferQuery;

	public:
		Renderer();
		~Renderer() override = default;

		void Init(IGraphicsAppManager* graphicsAppManager, IWindow* window) override;

		void Tick() override;

		void Close() override;

		std::string GetMainRenderDeviceName() override;

		void Resize(uint32_t newWidth, uint32_t newHeight) override;

		void SetScene(Scene::Scene* scene) override { this->scene = scene; }

		Scene::Scene* GetScene() override { return scene; }

		void SetActiveUi(Scene::UI::Ui* ui) override { uiRenderer.SetActiveUi(ui); }

		Scene::UI::Ui* GetActiveUi() override { return uiRenderer.GetActiveUi(); }

		CommandHelper* GetCommandData(size_t poolId);

		static void RunThread(Renderer* renderer, Data::ReadOnlyAtomicArrayQueue<Scene::Drawable*>* jobQueue, uint32_t id);

		void StartThreads(Data::ReadOnlyAtomicArrayQueue<Scene::Drawable*>* jobQueue);

		void RecordPrimaryBuffer();

		void Submit();

		void Render();

		void RecordSecondaryBuffer(Data::ReadOnlyAtomicArrayQueue<Scene::Drawable*>* jobQueue, size_t poolId);

		ResourceManager& GetResourceManager() { return resourceManager; }
		
		Context& GetContext() { return context; }

		void RegisterCloseable(ICloseable* closeable) { closeables.push_back(closeable); }

		void UnregisterCloseable(ICloseable* closeable) { Utils::Remove(closeables, closeable); }

		IResourceManager* GetIResourceManager() override { return &resourceManager; }


		float GetLastQueriedDepthValue() override { return depthBufferQuery.GetQueriedValue(); }
		void SetQueryDepthValue(const Math::Vector2f& depthCoordinates) override { depthBufferQuery.SetQueryCoordinates(depthCoordinates); }
	};
}
