#include "backend.h"
#include "wrapper/queue.h"
#include "thread/worker.h"
#include "wrapper/descriptorpool.h"
#include <iostream>
#include "zlog.h"
namespace vulkanapi {
	CommandWorker* Backend::TransferWorker = nullptr;
	CommandWorker* Backend::RenderWorker = nullptr;
	CommandWorker* Backend::PresentWorker = nullptr;
	Backend::Backend(const string appName)
	{
		auto instanceCreator = InstanceCreator();
		mInstance = new Instance(instanceCreator);
		
		auto deviceCreator = DeviceCreator(*mInstance);
		deviceCreator.AddWindowExtension();
		deviceCreator.AddQueue(Queue::TransferQueue, VkQueueFlagBits::VK_QUEUE_GRAPHICS_BIT, 1.0);
		deviceCreator.AddQueue(Queue::RenderQueue, VkQueueFlagBits::VK_QUEUE_GRAPHICS_BIT, 1.0);
		deviceCreator.AddQueue(Queue::ComputeQueue, VkQueueFlagBits::VK_QUEUE_GRAPHICS_BIT, 1.0);
		deviceCreator.AddQueue(Queue::PresentQueue, VkQueueFlagBits::VK_QUEUE_GRAPHICS_BIT, 1.0);
		mDevice = new Device(deviceCreator);

		InitWorker(Queue::TransferQueue, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT);
		InitWorker(Queue::RenderQueue, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT);
		InitWorker(Queue::ComputeQueue, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT);
		InitWorker(Queue::PresentQueue, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT);

		Backend::TransferWorker = GetWorker(Queue::TransferQueue);
		Backend::RenderWorker = GetWorker(Queue::RenderQueue);
		Backend::PresentWorker = GetWorker(Queue::PresentQueue);

		auto poolSizes = DescriptorPool::DefaultDescriptorPoolSize();
		mPool = new DescriptorPool(*mDevice, poolSizes, 1000);
	}
	Backend::~Backend()
	{
		if (mInstance) {
			delete mInstance;
		}
		if (mDevice) {
			delete mDevice;
		}
		for (auto it : mWorkerMap) {
			delete it.second;
		}
		mWorkerMap.clear();
	}
	void Backend::InitWorker(const string& name, VkCommandPoolCreateFlags flag)
	{
		auto queue = mDevice->GetQueue(name);
		if (queue) {
			auto worker = new CommandWorker(name, *mDevice, *queue, flag);
			mWorkerMap.emplace(&name, worker);
		}
	}
	CommandWorker* Backend::GetWorker(const string& name)
	{
		auto it = mWorkerMap.find(&name);
		if (it != mWorkerMap.end()) {
			return it->second;
		}
		return nullptr;
	}
}
