#include "device.h"
#include "device_creator.h"
#include "queue.h"
#include "zlog.h"
namespace vulkanapi {
    Device::Device(DeviceCreator& Creator)
    {
        //物理设备
        Creator.FindDevice(mPhysical);
        //队列信息
        vector<VkQueueFamilyProperties> queue_families;
        Creator.CheckAvailableQueueFamilies(mPhysical, queue_families);
        vector<VkDeviceQueueCreateInfo> queue_create_infos;
        vector<vector<float>> queue_prioritie;
        Creator.QueueCreateInfos(queue_create_infos, queue_prioritie, queue_families);
        //扩展
        auto extensions = Creator.EnabledExtensionNames();
        //特性
        VkPhysicalDeviceFeatures2 deviceFeatures = Creator.GetDeviceFeature2();
        VkPhysicalDeviceVulkan12Features deviceVulkan12Features = Creator.GetVulkan12Features();
        deviceFeatures.pNext = &deviceVulkan12Features;
        VkDeviceCreateInfo device_create_info = {
          VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,               // VkStructureType                  sType
          &deviceFeatures,                                    // const void                     * pNext
          0,                                                  // VkDeviceCreateFlags              flags
          static_cast<uint32_t>(queue_create_infos.size()),   // uint32_t                         queueCreateInfoCount
          queue_create_infos.data(),                          // const VkDeviceQueueCreateInfo  * pQueueCreateInfos
          0,                                                  // uint32_t                         enabledLayerCount
          nullptr,                                            // const char * const             * ppEnabledLayerNames
          static_cast<uint32_t>(extensions.size()),           // uint32_t                         enabledExtensionCount
          extensions.data(),                                  // const char * const             * ppEnabledExtensionNames
          nullptr                                             // const VkPhysicalDeviceFeatures * pEnabledFeatures
        };
#ifdef Z_USE_GRAPHIC_DEBUG
        auto layers = Creator.EnabledLayerNames();
        device_create_info.enabledLayerCount = layers.size();
        device_create_info.ppEnabledLayerNames = layers.data();
#endif // Z_USE_GRAPHIC_DEBUG
        VkResult result = vkCreateDevice(mPhysical, &device_create_info, nullptr, &mPtr);
        if ((result != VK_SUCCESS) || (mPtr == VK_NULL_HANDLE)) {
            zlog::error("Could not create logical device. VkResult {}", result);
        }
        volkLoadDevice(mPtr);
        for (auto& queue : Creator.desiredQueues) {
            Queue* gq = new Queue(queue.name, queue.queueFamilyIndex, VK_NULL_HANDLE);
            vkGetDeviceQueue(mPtr, queue.queueFamilyIndex, 0, &(gq->Ptr()));
            mQueueMap.emplace(&queue.name, gq);
        }
    }    
    Device::~Device() {
        for (auto& queue : mQueueMap) {
            delete queue.second;
        }
        mQueueMap.clear();
    }
    Queue* Device::GetQueue(const string& name)
    {
        auto it = mQueueMap.find(&name);
        if (it != mQueueMap.end()) {
            return it->second;
        }
        return nullptr;
    }
    VkFence Device::CreateFence()
    {
        VkFenceCreateInfo fenceInfo{};
        fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
        // 创建时立刻设置为signaled状态(否则第一次永远等不到)
        fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
        VkFence fence;
        VkResult result = vkCreateFence(mPtr, &fenceInfo, nullptr, &fence);
        return fence;
    }
    VkSemaphore Device::CreateSemaphore()
    {
        VkSemaphoreCreateInfo semaphoreInfo = {};
        semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
        VkSemaphore semaphore;
        VkResult result = vkCreateSemaphore(mPtr, &semaphoreInfo, nullptr, &semaphore);
        return semaphore;
    }
    VkShaderModule Device::CreateShaderModule(vector<char> code)
    {
        VkShaderModuleCreateInfo createInfo = {};
        createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
        // 这里需要确保数据满足uint32_t的对齐要求,存储在vector中，默认分配器已经确保数据满足最差情况下的对齐要求
        createInfo.codeSize = code.size();
        // 转换为Vulkan要求的uint32_t指针
        createInfo.pCode = reinterpret_cast<const uint32_t*>(code.data());
        VkShaderModule module;
        VkResult result = vkCreateShaderModule(mPtr, &createInfo, nullptr, &module);
        return module;
    }
}

