/***************************************************************************
 *
 * Copyright (c) 2015-2026 The Khronos Group Inc.
 * Copyright (c) 2015-2026 Valve Corporation
 * Copyright (c) 2015-2026 LunarG, Inc.
 * Copyright (c) 2015-2026 Google Inc.
 * Copyright (c) 2023-2024 RasterGrid Kft.
 * Copyright (C) 2026 Qualcomm Technologies, Inc.
 * Modifications Copyright (C) 2025-2026 Advanced Micro Devices, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ****************************************************************************/

#pragma once
#include <atomic>
#include <vector>

#include <vulkan/vulkan.h>
#include <vulkan/vk_enum_string_helper.h>
#include <vulkan/utility/vk_safe_struct.hpp>

#include "error_message/logging.h"
#include "containers/custom_containers.h"
#include "layer_options.h"
#include "gpuav/core/gpuav_settings.h"
#include "sync/sync_settings.h"
#include "gpu_dump/gpu_dump_settings.h"
#include "legacy/legacy_settings.h"
#include "generated/device_features.h"
#include "generated/vk_api_version.h"
#include "generated/vk_extension_helper.h"
#include "generated/vk_layer_dispatch_table.h"
#include "layer_object_id.h"
#include "state_tracker/special_supported.h"

// To avoid re-hashing unique ids on each use, we precompute the hash and store the
// hash's LSBs in the high 24 bits.
struct HashedUint64 {
    static const int HASHED_UINT64_SHIFT = 40;
    size_t operator()(const uint64_t& t) const { return t >> HASHED_UINT64_SHIFT; }

    static uint64_t hash(uint64_t id) {
        uint64_t h = (uint64_t)vvl::hash<uint64_t>()(id);
        id |= h << HASHED_UINT64_SHIFT;
        return id;
    }
};

namespace vvl {
class BaseInstance;
class BaseDevice;
class DispatchInstance;
class DispatchDevice;

// Device extension properties -- storing properties gathered from VkPhysicalDeviceProperties2::pNext chain
// TODO: this could be defined and initialized via generated code
struct DeviceExtensionProperties {
    VkPhysicalDevicePartitionedAccelerationStructurePropertiesNV partitioned_acceleration_structure_props;
    VkPhysicalDeviceClusterAccelerationStructurePropertiesNV cluster_acceleration_props;
    VkPhysicalDeviceShadingRateImagePropertiesNV shading_rate_image_props;
    VkPhysicalDeviceMeshShaderPropertiesNV mesh_shader_props_nv;
    VkPhysicalDeviceMeshShaderPropertiesEXT mesh_shader_props_ext;
    VkPhysicalDeviceCooperativeMatrixPropertiesNV cooperative_matrix_props;
    VkPhysicalDeviceCooperativeMatrixPropertiesKHR cooperative_matrix_props_khr;
    VkPhysicalDeviceCooperativeMatrix2PropertiesNV cooperative_matrix_props2_nv;
    VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback_props;
    VkPhysicalDeviceRayTracingPropertiesNV ray_tracing_props_nv;
    VkPhysicalDeviceRayTracingPipelinePropertiesKHR ray_tracing_props_khr;
    VkPhysicalDeviceAccelerationStructurePropertiesKHR acc_structure_props;
    VkPhysicalDeviceFragmentDensityMapPropertiesEXT fragment_density_map_props;
    VkPhysicalDeviceFragmentDensityMap2PropertiesEXT fragment_density_map2_props;
    VkPhysicalDeviceFragmentDensityMapOffsetPropertiesEXT fragment_density_map_offset_props;
    VkPhysicalDeviceFragmentDensityMapLayeredPropertiesVALVE fragment_density_map_layered_props;
    VkPhysicalDevicePerformanceQueryPropertiesKHR performance_query_props;
    VkPhysicalDeviceSampleLocationsPropertiesEXT sample_locations_props;
    VkPhysicalDeviceCustomBorderColorPropertiesEXT custom_border_color_props;
    VkPhysicalDeviceMultiviewProperties multiview_props;
    VkPhysicalDevicePortabilitySubsetPropertiesKHR portability_props;
    VkPhysicalDeviceFragmentShadingRatePropertiesKHR fragment_shading_rate_props;
    VkPhysicalDeviceProvokingVertexPropertiesEXT provoking_vertex_props;
    VkPhysicalDeviceMultiDrawPropertiesEXT multi_draw_props;
    VkPhysicalDeviceDiscardRectanglePropertiesEXT discard_rectangle_props;
    VkPhysicalDeviceBlendOperationAdvancedPropertiesEXT blend_operation_advanced_props;
    VkPhysicalDeviceConservativeRasterizationPropertiesEXT conservative_rasterization_props;
    VkPhysicalDeviceSubgroupProperties subgroup_props;
    VkPhysicalDeviceExtendedDynamicState3PropertiesEXT extended_dynamic_state3_props;
    VkPhysicalDeviceImageProcessingPropertiesQCOM image_processing_props;
    VkPhysicalDeviceImageAlignmentControlPropertiesMESA image_alignment_control_props;
    VkPhysicalDeviceMaintenance7PropertiesKHR maintenance7_props;
    VkPhysicalDeviceNestedCommandBufferPropertiesEXT nested_command_buffer_props;
    VkPhysicalDeviceDescriptorBufferPropertiesEXT descriptor_buffer_props;
    VkPhysicalDeviceDescriptorBufferDensityMapPropertiesEXT descriptor_buffer_density_props;
    VkPhysicalDeviceDeviceGeneratedCommandsPropertiesEXT device_generated_commands_props;
    VkPhysicalDevicePipelineBinaryPropertiesKHR pipeline_binary_props;
    VkPhysicalDeviceMapMemoryPlacedPropertiesEXT map_memory_placed_props;
    VkPhysicalDeviceComputeShaderDerivativesPropertiesKHR compute_shader_derivatives_props;
    VkPhysicalDeviceCooperativeVectorPropertiesNV cooperative_vector_props_nv;
    VkPhysicalDeviceRenderPassStripedPropertiesARM renderpass_striped_props;
    VkPhysicalDeviceExternalMemoryHostPropertiesEXT external_memory_host_props;
    VkPhysicalDeviceMaintenance9PropertiesKHR maintenance9_props;
    VkPhysicalDeviceMaintenance10PropertiesKHR maintenance10_props;
    VkPhysicalDeviceTensorPropertiesARM tensor_properties;
    VkPhysicalDeviceCopyMemoryIndirectPropertiesKHR copy_memory_indirect_props;
    VkPhysicalDeviceOpacityMicromapPropertiesKHR micromap_props;
    VkPhysicalDeviceTileMemoryHeapPropertiesQCOM tile_memory_heap_props;
    VkPhysicalDeviceDescriptorHeapPropertiesEXT descriptor_heap_props;
    VkPhysicalDeviceDescriptorHeapTensorPropertiesARM descriptor_heap_tensor_props;
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
    VkPhysicalDeviceExternalFormatResolvePropertiesANDROID android_format_resolve_props;
#endif
    VkPhysicalDeviceMemoryDecompressionPropertiesEXT memory_decompression_props;
    VkPhysicalDevicePerformanceCountersByRegionPropertiesARM renderpass_counter_by_region_props;
    VkPhysicalDeviceRayTracingInvocationReorderPropertiesEXT ray_tracing_invocation_reorder_props;
    VkPhysicalDeviceShaderLongVectorPropertiesEXT shader_long_vector_props;
    VkPhysicalDeviceTileShadingPropertiesQCOM tile_shading_props;
    VkPhysicalDeviceImageProcessing2PropertiesQCOM image_processing2_props;
};

// This object holds all static state for the device (device properties, enabled extensions/features, etc.)
// It is initialized atomically in the constructor based on the provided physical device and device create info
// and then used by all downstream users (state tracker, stateless SPIR-V validator, etc.).
class StatelessDeviceData {
  public:
    StatelessDeviceData(DispatchInstance* instance, VkPhysicalDevice physical_device, const VkDeviceCreateInfo* pCreateInfo);

    APIVersion api_version;

    DeviceExtensions extensions{};
    DeviceFeatures enabled_features{};

    VkPhysicalDeviceMemoryProperties phys_dev_mem_props{};
    VkPhysicalDeviceProperties phys_dev_props{};
    VkPhysicalDeviceVulkan11Properties phys_dev_props_core11{};
    VkPhysicalDeviceVulkan12Properties phys_dev_props_core12{};
    VkPhysicalDeviceVulkan13Properties phys_dev_props_core13{};
    VkPhysicalDeviceVulkan14Properties phys_dev_props_core14{};
    // To store the 2 lists from VkPhysicalDeviceHostImageCopyProperties
    std::vector<VkImageLayout> host_image_copy_props_copy_src_layouts{};
    std::vector<VkImageLayout> host_image_copy_props_copy_dst_layouts{};
    DeviceExtensionProperties phys_dev_ext_props = {};

    SpecialSupported special_supported;
};

class DispatchInstance;
void SetDispatchInstance(VkInstance instance, std::unique_ptr<DispatchInstance>&&);
DispatchInstance* GetDispatchInstance(VkInstance);
DispatchInstance* GetDispatchInstance(VkPhysicalDevice);
void FreeDispatchInstance(void* key);

class DispatchDevice;
void SetDispatchDevice(VkDevice dev, std::unique_ptr<DispatchDevice>&&);
DispatchDevice* GetDispatchDevice(VkDevice);
DispatchDevice* GetDispatchDevice(VkQueue);
DispatchDevice* GetDispatchDevice(VkCommandBuffer);
DispatchDevice* GetDispatchDevice(VkExternalComputeQueueNV);
void FreeDispatchDevice(void* key);

void FreeAllDispatchObjects();

struct TemplateState {
    VkDescriptorUpdateTemplate desc_update_template;
    vku::safe_VkDescriptorUpdateTemplateCreateInfo create_info;
    bool destroyed;

    TemplateState(VkDescriptorUpdateTemplate update_template, vku::safe_VkDescriptorUpdateTemplateCreateInfo* pCreateInfo)
        : desc_update_template(update_template), create_info(*pCreateInfo), destroyed(false) {}
};

struct Settings {
    GlobalSettings global_settings = {};
    GpuAVSettings gpuav_settings = {};
    SyncValSettings syncval_settings = {};
    GpuDumpSettings gpu_dump_settings = {};
    LegacyDetectionSettings legacy_detection_settings = {};

    ValidationDisabled disabled = {};
    ValidationEnabled enabled = {};
};

class HandleWrapper : public Logger {
  public:
    HandleWrapper(DebugReport* dr);
    ~HandleWrapper();

    // Unwrap a handle.
    template <typename HandleType>
    HandleType Unwrap(HandleType wrapped_handle) {
        if (wrapped_handle == (HandleType)VK_NULL_HANDLE) return wrapped_handle;
        auto iter = unique_id_mapping.find(CastToUint64(wrapped_handle));
        if (iter == unique_id_mapping.end()) return (HandleType)0;
        return (HandleType)iter->second;
    }

    // Wrap a newly created handle with a new unique ID, and return the new ID.
    template <typename HandleType>
    HandleType WrapNew(HandleType new_created_handle) {
        if (new_created_handle == (HandleType)VK_NULL_HANDLE) return new_created_handle;
        auto unique_id = global_unique_id++;
        unique_id = HashedUint64::hash(unique_id);
        assert(unique_id != 0);  // can't be 0, otherwise unwrap will apply special rule for VK_NULL_HANDLE
        unique_id_mapping.insert_or_assign(unique_id, CastToUint64(new_created_handle));
        return (HandleType)unique_id;
    }

    template <typename HandleType>
    HandleType Find(HandleType wrapped_handle) const {
        uint64_t id = CastToUint64(wrapped_handle);
        auto iter = unique_id_mapping.find(id);
        if (iter != unique_id_mapping.end()) {
            return CastFromUint64<HandleType>(iter->second);
        } else {
            return CastFromUint<HandleType>(0ULL);
        }
    }

    template <typename HandleType>
    HandleType Erase(HandleType wrapped_handle) {
        uint64_t id = CastToUint64(wrapped_handle);
        auto iter = unique_id_mapping.pop(id);
        if (iter != unique_id_mapping.end()) {
            return CastFromUint64<HandleType>(iter->second);
        } else {
            return CastFromUint<HandleType>(0ULL);
        }
    }

    // Replaces the "driver handle" in the "wrapped handle" to "driver handle" mapping.
    // Returns the old "driver handle" if found.
    template <typename HandleType>
    HandleType Replace(HandleType wrapped_handle, HandleType new_driver_handle) {
        const HandleType old_driver_handle = Find(wrapped_handle);
        const uint64_t wrapped_handle_id = CastToUint64(wrapped_handle);
        assert(wrapped_handle_id != 0);  // can't be 0, otherwise unwrap will apply special rule for VK_NULL_HANDLE
        unique_id_mapping.insert_or_assign(wrapped_handle_id, CastToUint64(new_driver_handle));
        return old_driver_handle;
    }

    void UnwrapPnextChainHandles(const void* pNext);
    void UnwrapComputePipelineCreateInfoHandles(vku::safe_VkComputePipelineCreateInfo& safe_ci);
    void UnwrapGraphicsPipelineCreateInfoHandles(vku::safe_VkGraphicsPipelineCreateInfo& safe_ci);

    static std::atomic<uint64_t> global_unique_id;
    static vvl::concurrent_unordered_map<uint64_t, uint64_t, 4, HashedUint64> unique_id_mapping;
    static bool wrap_handles;
};

class DispatchInstance : public HandleWrapper {
  public:
    DispatchInstance(const VkInstanceCreateInfo* pCreateInfo);
    ~DispatchInstance();

    void InitValidationObjects();
    void FindSupportedExtensions();

    // VkDisplayKHR objects are statically created in the driver at VkCreateInstance.
    // They live with the PhyiscalDevice and apps never created/destroy them.
    // Apps needs will query for them and the first time we see it we wrap it
    VkDisplayKHR MaybeWrapDisplay(VkDisplayKHR handle) {
        // See if this display is already known
        auto it = display_id_reverse_mapping.find(handle);
        if (it != display_id_reverse_mapping.end()) return (VkDisplayKHR)it->second;

        // First time see this VkDisplayKHR, so wrap
        const uint64_t unique_id = (uint64_t)WrapNew(handle);
        display_id_reverse_mapping.insert_or_assign(handle, unique_id);
        return (VkDisplayKHR)unique_id;
    }
    BaseInstance* GetValidationObject(LayerObjectTypeId object_type) const;

    Settings settings;

    APIVersion api_version;
    DeviceExtensions extensions{};

    mutable std::vector<std::unique_ptr<BaseInstance>> object_dispatch;

    VkInstance instance = VK_NULL_HANDLE;
    VkLayerInstanceDispatchTable instance_dispatch_table;
    // Reverse map display handles
    vvl::concurrent_unordered_map<VkDisplayKHR, uint64_t, 0> display_id_reverse_mapping;

#include "generated/dispatch_object_instance_methods.h"

    template <bool init = true, typename ExtProp>
    void GetPhysicalDeviceExtProperties(VkPhysicalDevice gpu, ExtEnabled enabled, ExtProp* ext_prop) {
        assert(ext_prop);
        // Extensions that use two calls to get properties don't want to init on the second call
        if constexpr (init) {
            *ext_prop = vku::InitStructHelper();
        }
        if (IsExtEnabled(enabled)) {
            VkPhysicalDeviceProperties2 prop2 = vku::InitStructHelper(ext_prop);
            if (api_version >= VK_API_VERSION_1_1) {
                GetPhysicalDeviceProperties2(gpu, &prop2);
            } else {
                GetPhysicalDeviceProperties2KHR(gpu, &prop2);
            }
        }
    }

    void ReportErrorFeatureNotPresent(VkPhysicalDevice gpu, const VkDeviceCreateInfo& create_info);
};

class DispatchDevice : public HandleWrapper {
  public:
    DispatchDevice(DispatchInstance* instance, VkPhysicalDevice gpu, const VkDeviceCreateInfo* pCreateInfo);
    ~DispatchDevice();

    void InitObjectDispatchVectors();
    void InitValidationObjects();
    void ReleaseValidationObject(LayerObjectTypeId type_id) const;
    BaseDevice* GetValidationObject(LayerObjectTypeId object_type) const;

    bool IsSecondary(VkCommandBuffer cb) const;

    Settings& settings;
    DispatchInstance* dispatch_instance;

    const StatelessDeviceData stateless_device_data;

    const APIVersion api_version;

    const DeviceExtensions& extensions;
    const DeviceFeatures& enabled_features;

    const VkPhysicalDeviceMemoryProperties& phys_dev_mem_props;
    const VkPhysicalDeviceProperties& phys_dev_props;
    const VkPhysicalDeviceVulkan11Properties& phys_dev_props_core11;
    const VkPhysicalDeviceVulkan12Properties& phys_dev_props_core12;
    const VkPhysicalDeviceVulkan13Properties& phys_dev_props_core13;
    const VkPhysicalDeviceVulkan14Properties& phys_dev_props_core14;
    // To store the 2 lists from VkPhysicalDeviceHostImageCopyProperties
    const std::vector<VkImageLayout>& host_image_copy_props_copy_src_layouts;
    const std::vector<VkImageLayout>& host_imape_copy_props_copy_dst_layouts;
    const DeviceExtensionProperties& phys_dev_ext_props;

    VkPhysicalDevice physical_device = VK_NULL_HANDLE;
    VkDevice device = VK_NULL_HANDLE;
    VkLayerDispatchTable device_dispatch_table;

    mutable std::vector<std::unique_ptr<BaseDevice>> object_dispatch;
    mutable std::vector<std::unique_ptr<BaseDevice>> aborted_object_dispatch;
    mutable std::vector<std::vector<BaseDevice*>> intercept_vectors;
    // Handle Wrapping Data
    // Wrapping Descriptor Template Update structures requires access to the template createinfo structs
    vvl::unordered_map<uint64_t, std::unique_ptr<TemplateState>> desc_template_createinfo_map;
    struct SubpassesUsageStates {
        vvl::unordered_set<uint32_t> subpasses_using_color_attachment;
        vvl::unordered_set<uint32_t> subpasses_using_depthstencil_attachment;
    };
    // Uses unwrapped handles
    vvl::unordered_map<VkRenderPass, SubpassesUsageStates> renderpasses_states;
    // Map of wrapped swapchain handles to arrays of wrapped swapchain image IDs
    // Each swapchain has an immutable list of wrapped swapchain image IDs -- always return these IDs if they exist
    vvl::unordered_map<VkSwapchainKHR, std::vector<VkImage>> swapchain_wrapped_image_handle_map;
    // Map of wrapped descriptor pools to set of wrapped descriptor sets allocated from each pool
    vvl::unordered_map<VkDescriptorPool, vvl::unordered_set<VkDescriptorSet>> pool_descriptor_sets_map;

    vvl::concurrent_unordered_map<VkDeferredOperationKHR, std::vector<std::function<void()>>, 0> deferred_operation_post_completion;
    vvl::concurrent_unordered_map<VkDeferredOperationKHR, std::vector<std::function<void(std::pair<uint32_t, VkPipeline*>)>>, 0>
        deferred_operation_post_check;
    vvl::concurrent_unordered_map<VkDeferredOperationKHR, std::pair<uint32_t, VkPipeline*>, 0> deferred_operation_pipelines;

    // State we track in order to populate HandleData for things such as ignored pointers
    vvl::unordered_map<VkCommandBuffer, VkCommandPool> secondary_cb_map{};
    mutable std::shared_mutex secondary_cb_map_mutex;

#include "generated/dispatch_object_device_methods.h"
};
}  // namespace vvl
