/*
 * 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 "Task.hpp"
#include "Base/Logger.hpp"
#include "Base/Wrapper.hpp"
#include <concurrentqueue.h>

namespace OpenVulkano
{
	class ITaskPool
	{
	protected:
		void ExecuteTask(const Ptr<Task>& task)
		{
			try
			{
				task->Execute();
			}
			catch(const std::exception& e)
			{
				Logger::MANAGER->error("Failed to execute task! With exception: {}", e.what());
			}
			catch(...)
			{
				Logger::MANAGER->error("Failed to execute task! With unknown throwable");
			}
		}

	public:
		/*template<class T, typename... Args>
		void EmplaceTask(Args&&... args)
		{
			m_tasks.enqueue(std::make_shared<T>((std::forward<Args>(args)...)));
		}*/

		virtual void Enqueue(const Ptr<Task>& task) = 0;

		void Enqueue(const std::function<void()>& taskFunction)
		{
			Enqueue(std::make_shared<FunctionalTask>(taskFunction));
		}

		void operator +=(const std::function<void()>& taskFunction)
		{
			Enqueue(taskFunction);
		}
	};

	class MainThreadTaskPool final : public ITaskPool
	{
		static MainThreadTaskPool* INSTANCE;
		moodycamel::ConcurrentQueue<Ptr<Task>> m_tasks;

	public:
		MainThreadTaskPool()
		{
			if (!INSTANCE) INSTANCE = this;
		}

		~MainThreadTaskPool()
		{
			if (INSTANCE == this) INSTANCE = nullptr;
		}

		static MainThreadTaskPool& GetInstance() { return *INSTANCE; }

		void Tick()
		{
			Ptr<Task> task;
			while (m_tasks.try_dequeue(task))
			{
				ExecuteTask(task);
			}
		}

		void Enqueue(const Ptr<Task>& task) override
		{
			m_tasks.enqueue(task);
		}
	};
}
