/*
 * Copyright (C) 2020-2026 Intel Corporation
 *
 * SPDX-License-Identifier: MIT
 *
 */

#pragma once

#include "shared/source/built_ins/built_in_ops_base.h"
#include "shared/source/command_container/cmdcontainer.h"
#include "shared/source/command_stream/preemption_mode.h"
#include "shared/source/command_stream/queue_throttle.h"
#include "shared/source/command_stream/stream_properties.h"
#include "shared/source/command_stream/thread_arbitration_policy.h"
#include "shared/source/helpers/blit_properties.h"
#include "shared/source/helpers/cache_policy.h"
#include "shared/source/helpers/definitions/command_encoder_args.h"
#include "shared/source/helpers/heap_base_address_model.h"
#include "shared/source/helpers/in_order_cmd_helpers.h"
#include "shared/source/helpers/private_allocs_to_reuse_container.h"
#include "shared/source/host_function/host_function.h"
#include "shared/source/memory_manager/graphics_allocation.h"
#include "shared/source/memory_manager/prefetch_manager.h"
#include "shared/source/unified_memory/unified_memory.h"
#include "shared/source/utilities/stackvec.h"

#include "level_zero/core/source/cmdlist/cmdlist_wait_parameters.h"
#include "level_zero/core/source/cmdlist/command_to_patch.h"
#include "level_zero/core/source/device/bcs_split_params.h"
#include "level_zero/core/source/helpers/api_handle_helper.h"
#include "level_zero/core/source/semaphore/external_semaphore_imp.h"
#include "level_zero/experimental/source/graph/graph.h"
#include "level_zero/include/level_zero/driver_experimental/zex_cmdlist.h"
#include "level_zero/include/level_zero/ze_intel_gpu.h"
#include <level_zero/ze_api.h>
#include <level_zero/zet_api.h>

#include "copy_offload_mode.h"

#include <map>
#include <mutex>
#include <optional>
#include <utility>
#include <vector>

struct _ze_command_list_handle_t : BaseHandleWithLoaderTranslation<ZEL_HANDLE_COMMAND_LIST> {};
static_assert(IsCompliantWithDdiHandlesExt<_ze_command_list_handle_t>);

namespace NEO {
class ScratchSpaceController;
class TagNodeBase;
struct EncodeDispatchKernelArgs;
class CommandStreamReceiver;
class GraphicsAllocation;
struct HostFunction;
enum class TransferDirection;
} // namespace NEO

namespace L0 {
struct Device;
struct EventPool;
struct Event;
struct Kernel;
struct CommandListExecutionInternalOptions;
struct CommandQueue;
struct CmdListKernelLaunchParams;
struct CmdListMemoryCopyParams;
struct CmdListHostFunctionParameters;
struct CmdListCustomOperationParams;
struct MemAllocInfo;

struct CmdListReturnPoint {
    NEO::StreamProperties configSnapshot;
    uint64_t gpuAddress = 0;
    NEO::GraphicsAllocation *currentCmdBuffer = nullptr;
};

struct MemAdviseOperation {
    ze_device_handle_t hDevice;
    const void *ptr;
    const size_t size;
    ze_memory_advice_t advice;
};

using AppendedMemAdviseOperations = std::vector<MemAdviseOperation>;

struct CommandList : _ze_command_list_handle_t {
    static constexpr uint32_t defaultNumIddsPerBlock = 64u;
    static constexpr uint32_t commandListimmediateIddsPerBlock = 1u;

    CommandList() = delete;
    CommandList(uint32_t numIddsPerBlock);

    template <typename Type>
    struct Allocator {
        static CommandList *allocate(uint32_t numIddsPerBlock) { return new Type(numIddsPerBlock); }
    };

    using CommandsToPatch = StackVec<CommandToPatchOnQueue, 16>;
    using CmdListReturnPoints = StackVec<CmdListReturnPoint, 32>;

    virtual ze_result_t close() = 0;
    virtual ze_result_t destroy();
    virtual ze_result_t appendEventReset(ze_event_handle_t hEvent) = 0;
    virtual ze_result_t appendBarrier(ze_event_handle_t hSignalEvent, uint32_t numWaitEvents,
                                      ze_event_handle_t *phWaitEvents, CmdListWaitEventParameters &waitEventsParameters) = 0;
    virtual ze_result_t appendCustomOperation(const void *pNext,
                                              ze_event_handle_t hSignalEvent,
                                              uint32_t numWaitEvents,
                                              ze_event_handle_t *phWaitEvents) = 0;
    virtual ze_result_t appendMemoryRangesBarrier(uint32_t numRanges, const size_t *pRangeSizes,
                                                  const void **pRanges,
                                                  ze_event_handle_t hSignalEvent,
                                                  uint32_t numWaitEvents,
                                                  ze_event_handle_t *phWaitEvents,
                                                  CmdListWaitEventParameters &waitEventParams) = 0;
    virtual ze_result_t appendImageCopyFromMemory(ze_image_handle_t hDstImage, const void *srcptr,
                                                  const ze_image_region_t *pDstRegion,
                                                  ze_event_handle_t hEvent, uint32_t numWaitEvents,
                                                  ze_event_handle_t *phWaitEvents, CmdListMemoryCopyParams &memoryCopyParams) = 0;
    virtual ze_result_t appendImageCopyToMemory(void *dstptr, ze_image_handle_t hSrcImage,
                                                const ze_image_region_t *pSrcRegion,
                                                ze_event_handle_t hEvent, uint32_t numWaitEvents,
                                                ze_event_handle_t *phWaitEvents, CmdListMemoryCopyParams &memoryCopyParams) = 0;
    virtual ze_result_t appendImageCopyFromMemoryExt(ze_image_handle_t hDstImage, const void *srcptr,
                                                     const ze_image_region_t *pDstRegion,
                                                     uint32_t srcRowPitch, uint32_t srcSlicePitch,
                                                     ze_event_handle_t hEvent, uint32_t numWaitEvents,
                                                     ze_event_handle_t *phWaitEvents, CmdListMemoryCopyParams &memoryCopyParams) = 0;
    virtual ze_result_t appendImageCopyToMemoryExt(void *dstptr, ze_image_handle_t hSrcImage,
                                                   const ze_image_region_t *pSrcRegion,
                                                   uint32_t destRowPitch, uint32_t destSlicePitch,
                                                   ze_event_handle_t hEvent, uint32_t numWaitEvents,
                                                   ze_event_handle_t *phWaitEvents, CmdListMemoryCopyParams &memoryCopyParams) = 0;
    virtual ze_result_t appendImageCopyRegion(ze_image_handle_t hDstImage, ze_image_handle_t hSrcImage,
                                              const ze_image_region_t *pDstRegion, const ze_image_region_t *pSrcRegion,
                                              ze_event_handle_t hSignalEvent, uint32_t numWaitEvents,
                                              ze_event_handle_t *phWaitEvents, CmdListMemoryCopyParams &memoryCopyParams) = 0;
    virtual ze_result_t appendImageCopy(ze_image_handle_t hDstImage, ze_image_handle_t hSrcImage,
                                        ze_event_handle_t hEvent, uint32_t numWaitEvents,
                                        ze_event_handle_t *phWaitEvents, CmdListMemoryCopyParams &memoryCopyParams) = 0;
    virtual ze_result_t appendLaunchKernel(ze_kernel_handle_t kernelHandle, const ze_group_count_t &threadGroupDimensions,
                                           ze_event_handle_t hEvent, uint32_t numWaitEvents, ze_event_handle_t *phWaitEvents,
                                           CmdListKernelLaunchParams &launchParams) = 0;
    virtual ze_result_t appendLaunchKernelIndirect(ze_kernel_handle_t kernelHandle,
                                                   const ze_group_count_t &pDispatchArgumentsBuffer,
                                                   ze_event_handle_t hEvent, uint32_t numWaitEvents,
                                                   ze_event_handle_t *phWaitEvents, bool relaxedOrderingDispatch) = 0;
    virtual ze_result_t appendLaunchMultipleKernelsIndirect(uint32_t numKernels, const ze_kernel_handle_t *kernelHandles,
                                                            const uint32_t *pNumLaunchArguments,
                                                            const ze_group_count_t *pLaunchArgumentsBuffer, ze_event_handle_t hEvent,
                                                            uint32_t numWaitEvents, ze_event_handle_t *phWaitEvents, bool relaxedOrderingDispatch) = 0;
    virtual ze_result_t appendLaunchKernelWithArguments(ze_kernel_handle_t hKernel,
                                                        const ze_group_count_t groupCounts,
                                                        const ze_group_size_t groupSizes,

                                                        void **pArguments,
                                                        const void *pNext,
                                                        ze_event_handle_t hSignalEvent,
                                                        uint32_t numWaitEvents,
                                                        ze_event_handle_t *phWaitEvents) = 0;

    virtual ze_result_t appendLaunchKernelWithParameters(ze_kernel_handle_t hKernel,
                                                         const ze_group_count_t *pGroupCounts,
                                                         const void *pNext,
                                                         ze_event_handle_t hSignalEvent,
                                                         uint32_t numWaitEvents,
                                                         ze_event_handle_t *phWaitEvents) = 0;

    virtual ze_result_t appendMemAdvise(ze_device_handle_t hDevice, const void *ptr, size_t size,
                                        ze_memory_advice_t advice) = 0;
    virtual ze_result_t executeMemAdvise(ze_device_handle_t hDevice,
                                         const void *ptr, size_t size,
                                         ze_memory_advice_t advice) = 0;
    virtual ze_result_t appendMemoryCopy(void *dstptr, const void *srcptr, size_t size,
                                         ze_event_handle_t hSignalEvent, uint32_t numWaitEvents,
                                         ze_event_handle_t *phWaitEvents, CmdListMemoryCopyParams &memoryCopyParams) = 0;
    virtual ze_result_t appendMemoryCopyWithParameters(void *dstptr, const void *srcptr, size_t size,
                                                       const void *pNext,
                                                       ze_event_handle_t hSignalEvent, uint32_t numWaitEvents,
                                                       ze_event_handle_t *phWaitEvents,
                                                       CmdListMemoryCopyParams &memoryCopyParams) = 0;
    virtual ze_result_t appendPageFaultCopy(NEO::GraphicsAllocation *dstptr, NEO::GraphicsAllocation *srcptr, size_t size, bool flushHost, size_t offset) = 0;
    virtual ze_result_t appendMemoryCopyRegion(void *dstPtr,
                                               const ze_copy_region_t *dstRegion,
                                               uint32_t dstPitch,
                                               uint32_t dstSlicePitch,
                                               const void *srcPtr,
                                               const ze_copy_region_t *srcRegion,
                                               uint32_t srcPitch,
                                               uint32_t srcSlicePitch,
                                               ze_event_handle_t hSignalEvent,
                                               uint32_t numWaitEvents,
                                               ze_event_handle_t *phWaitEvents, CmdListMemoryCopyParams &memoryCopyParams) = 0;
    virtual ze_result_t appendMemoryFill(void *ptr, const void *pattern,
                                         size_t patternSize, size_t size, ze_event_handle_t hSignalEvent,
                                         uint32_t numWaitEvents, ze_event_handle_t *phWaitEvents, CmdListMemoryCopyParams &memoryCopyParams) = 0;
    virtual ze_result_t appendMemoryFillWithParameters(void *ptr, const void *pattern,
                                                       size_t patternSize, size_t size, const void *pNext, ze_event_handle_t hSignalEvent,
                                                       uint32_t numWaitEvents, ze_event_handle_t *phWaitEvents, CmdListMemoryCopyParams &memoryCopyParams) = 0;
    virtual ze_result_t appendMemoryPrefetch(const void *ptr, size_t count) = 0;
    virtual ze_result_t appendSignalEvent(ze_event_handle_t hEvent, bool relaxedOrderingDispatch) = 0;
    virtual ze_result_t appendWaitOnEvents(uint32_t numEvents, ze_event_handle_t *phEvent, CmdListWaitEventParameters &waitEventParams) = 0;
    virtual ze_result_t appendWriteGlobalTimestamp(uint64_t *dstptr, ze_event_handle_t hSignalEvent,
                                                   uint32_t numWaitEvents, ze_event_handle_t *phWaitEvents,
                                                   CmdListWaitEventParameters &waitEventParams) = 0;
    virtual ze_result_t appendMemoryCopyFromContext(void *dstptr, ze_context_handle_t hContextSrc,
                                                    const void *srcptr, size_t size, ze_event_handle_t hSignalEvent,
                                                    uint32_t numWaitEvents, ze_event_handle_t *phWaitEvents,
                                                    CmdListMemoryCopyParams &memoryCopyParams) = 0;

    virtual void *asMutable() { return nullptr; };

    virtual ze_result_t reserveSpace(size_t size, void **ptr) = 0;
    virtual ze_result_t reset() = 0;

    virtual ze_result_t appendMetricMemoryBarrier();
    virtual ze_result_t appendMetricStreamerMarker(zet_metric_streamer_handle_t hMetricStreamer,
                                                   uint32_t value);
    virtual ze_result_t appendMetricQueryBegin(zet_metric_query_handle_t hMetricQuery);
    virtual ze_result_t appendMetricQueryEnd(zet_metric_query_handle_t hMetricQuery, ze_event_handle_t hSignalEvent,
                                             uint32_t numWaitEvents, ze_event_handle_t *phWaitEvents);

    virtual ze_result_t appendQueryKernelTimestamps(uint32_t numEvents, ze_event_handle_t *phEvents, void *dstptr,
                                                    const size_t *pOffsets, ze_event_handle_t hSignalEvent,
                                                    uint32_t numWaitEvents, ze_event_handle_t *phWaitEvents,
                                                    CmdListWaitEventParameters &waitEventsParameters) = 0;

    virtual ze_result_t appendMIBBStart(uint64_t address, size_t predication, bool secondLevel) = 0;
    virtual ze_result_t appendMIBBEnd() = 0;
    virtual ze_result_t appendMINoop() = 0;
    virtual ze_result_t appendPipeControl(void *dstPtr, uint64_t value) = 0;
    virtual ze_result_t appendSoftwareTag(const char *data) = 0;
    virtual ze_result_t appendWaitOnMemory(void *desc, void *ptr, uint64_t data, ze_event_handle_t signalEventHandle, bool useQwordData) = 0;
    virtual ze_result_t appendWriteToMemory(void *desc, void *ptr,
                                            uint64_t data) = 0;
    virtual ze_result_t appendWaitExternalSemaphores(uint32_t numExternalSemaphores, const ze_external_semaphore_ext_handle_t *hSemaphores,
                                                     const ze_external_semaphore_wait_params_ext_t *params, ze_event_handle_t hSignalEvent,
                                                     uint32_t numWaitEvents, ze_event_handle_t *phWaitEvents) = 0;
    virtual ze_result_t appendSignalExternalSemaphores(uint32_t numExternalSemaphores, const ze_external_semaphore_ext_handle_t *hSemaphores,
                                                       const ze_external_semaphore_signal_params_ext_t *params, ze_event_handle_t hSignalEvent,
                                                       uint32_t numWaitEvents, ze_event_handle_t *phWaitEvents) = 0;
    virtual ze_result_t hostSynchronize(uint64_t timeout) = 0;

    MOCKABLE_VIRTUAL ze_result_t getDeviceHandle(ze_device_handle_t *phDevice);
    MOCKABLE_VIRTUAL ze_result_t getContextHandle(ze_context_handle_t *phContext);
    virtual uint32_t getOrdinal() const;
    ze_result_t getFlags(ze_command_list_flags_t *pFlags);
    virtual ze_result_t getImmediateIndex(uint32_t *pIndex);
    ze_result_t getImmediateFlags(ze_command_queue_flags_t *pFlags);
    ze_result_t getImmediateMode(ze_command_queue_mode_t *pMode);
    ze_result_t getImmediatePriority(ze_command_queue_priority_t *pPriority);
    virtual ze_result_t isImmediate(ze_bool_t *pIsImmediate);
    ze_result_t isMutableExp(ze_bool_t *pIsMutable);
    MOCKABLE_VIRTUAL ze_result_t visit(const ze_visit_ext_desc_t *desc) {
        if (nullptr == this->flatCapture) {
            return ZE_RESULT_ERROR_UNSUPPORTED_FEATURE;
        }
        return flatCapture->visit(desc);
    }

    virtual ze_result_t appendCommandLists(uint32_t numCommandLists, ze_command_list_handle_t *phCommandLists,
                                           ze_event_handle_t hSignalEvent, uint32_t numWaitEvents, ze_event_handle_t *phWaitEvents,
                                           CommandListExecutionInternalOptions &internalOptions) = 0;

    virtual ze_result_t appendHostFunction(ze_host_function_callback_t pHostFunction,
                                           void *pUserData,
                                           const void *pNext,
                                           ze_event_handle_t hSignalEvent,
                                           uint32_t numWaitEvents,
                                           ze_event_handle_t *phWaitEvents,
                                           CmdListHostFunctionParameters &parameters) = 0;

    static CommandList *create(uint32_t productFamily, Device *device, NEO::EngineGroupType engineGroupType,
                               ze_command_list_flags_t flags, ze_result_t &resultValue,
                               bool internalUsage) {
        return create(productFamily, device, engineGroupType, flags, resultValue, internalUsage, 0u);
    }
    static CommandList *create(uint32_t productFamily, Device *device, NEO::EngineGroupType engineGroupType,
                               ze_command_list_flags_t flags, ze_result_t &resultValue,
                               bool internalUsage, uint32_t estimatedNumberOfCommands);
    static CommandList *createImmediate(uint32_t productFamily, Device *device,
                                        const ze_command_queue_desc_t *desc,
                                        bool internalUsage, NEO::EngineGroupType engineGroupType,
                                        ze_result_t &resultValue) {
        return createImmediate(productFamily, device, desc, internalUsage, engineGroupType, resultValue, 0u);
    }
    static CommandList *createImmediate(uint32_t productFamily, Device *device,
                                        const ze_command_queue_desc_t *desc,
                                        bool internalUsage, NEO::EngineGroupType engineGroupType,
                                        ze_result_t &resultValue,
                                        uint8_t powerHint);

    static CommandList *createImmediate(uint32_t productFamily, Device *device,
                                        const ze_command_queue_desc_t *desc,
                                        bool internalUsage, NEO::EngineGroupType engineGroupType, NEO::CommandStreamReceiver *csr,
                                        ze_result_t &resultValue) {
        return createImmediate(productFamily, device, desc, internalUsage, engineGroupType, csr, resultValue, 0u);
    }
    static CommandList *createImmediate(uint32_t productFamily, Device *device,
                                        const ze_command_queue_desc_t *desc,
                                        bool internalUsage, NEO::EngineGroupType engineGroupType, NEO::CommandStreamReceiver *csr,
                                        ze_result_t &resultValue,
                                        uint8_t powerHint);

    static CommandList *fromHandle(ze_command_list_handle_t handle) {
        return static_cast<CommandList *>(handle);
    }

    static bool isExternalHostPtrAlloc(NEO::GraphicsAllocation *alloc) {
        return alloc && alloc->getAllocationType() == NEO::AllocationType::externalHostPtr;
    }

    static ze_result_t setKernelState(Kernel *kernel, const ze_group_size_t groupSizes, void **arguments);
    static uint32_t getLimitIsaPrefetchSize();
    static ze_result_t cloneAppendKernelExtensions(const ze_base_desc_t *desc, void *&outPnext);
    static void freeClonedAppendKernelExtensions(void *pNext);
    static ze_result_t cloneAppendMemoryCopyExtensions(const ze_base_desc_t *desc, void *&outPnext);
    static void freeClonedAppendMemoryCopyExtensions(void *pNext);

    inline ze_command_list_handle_t toHandle() { return this; }

    uint32_t getCommandListPerThreadScratchSize(uint32_t slotId) const {
        return commandListPerThreadScratchSize[slotId];
    }

    MOCKABLE_VIRTUAL void setAdditionalDispatchKernelArgsFromLaunchParams(NEO::EncodeDispatchKernelArgs &dispatchKernelArgs, const CmdListKernelLaunchParams &launchParams) const;
    void setAdditionalDispatchKernelArgsFromKernel(NEO::EncodeDispatchKernelArgs &dispatchKernelArgs, const Kernel *kernel) const;

    ze_result_t validateLaunchParams(const Kernel &kernel, const CmdListKernelLaunchParams &launchParams, const ze_group_count_t &threadGroupDimensions) const;

    void setAdditionalBlitPropertiesFromMemoryCopyParams(NEO::BlitProperties &blitProperties, const CmdListMemoryCopyParams &memoryCopyParams) const;

    void setOrdinal(uint32_t ord) { ordinal = ord; }
    void setCommandListPerThreadScratchSize(uint32_t slotId, uint32_t size) {
        UNRECOVERABLE_IF(slotId > 1);
        commandListPerThreadScratchSize[slotId] = size;
    }

    NEO::ScratchSpaceController *getCommandListUsedScratchController() const {
        return usedScratchController;
    }

    void setCommandListUsedScratchController(NEO::ScratchSpaceController *scratchController) {
        usedScratchController = scratchController;
    }

    uint32_t getCommandListSLMEnable() const {
        return commandListSLMEnabled;
    }

    void setCommandListSLMEnable(bool isSLMEnabled) {
        commandListSLMEnabled = isSLMEnabled;
    }

    NEO::PreemptionMode getCommandListPreemptionMode() const {
        return commandListPreemptionMode;
    }

    UnifiedMemoryControls getUnifiedMemoryControls() const {
        return unifiedMemoryControls;
    }

    bool hasIndirectAllocationsAllowed() const {
        return indirectAllocationsAllowed;
    }

    std::vector<std::weak_ptr<Kernel>> &getPrintfKernelContainer() {
        return this->printfKernelContainer;
    }

    void storePrintfKernel(Kernel *kernel);
    void removeDeallocationContainerData();
    void removeHostPtrAllocations();
    void removeMemoryPrefetchAllocations();
    void storeFillPatternResourcesForReuse();
    void eraseDeallocationContainerEntry(NEO::GraphicsAllocation *allocation);
    void eraseResidencyContainerEntry(NEO::GraphicsAllocation *allocation);
    bool isCopyOnly(bool copyOffloadOperation) const {
        return NEO::EngineHelper::isCopyOnlyEngineType(engineGroupType) || (copyOffloadOperation && this->isCopyOffloadEnabled());
    }
    bool isCopyOffloadEnabled() const { return copyOffloadMode != CopyOffloadModes::disabled; }
    CopyOffloadMode getCopyOffloadModeForOperation(bool offloadAllowed) const { return offloadAllowed ? copyOffloadMode : CopyOffloadModes::disabled; }

    bool isInternal() const {
        return internalUsage;
    }
    bool containsCooperativeKernels() const {
        return containsCooperativeKernelsFlag;
    }
    bool isMemoryPrefetchRequested() const {
        return performMemoryPrefetch;
    }

    enum class CommandListType : uint32_t {
        typeRegular = 0u,
        typeImmediate = 1u
    };

    virtual ze_result_t initialize(Device *device, NEO::EngineGroupType engineGroupType, ze_command_list_flags_t flags) = 0;
    virtual ~CommandList();

    bool getContainsStatelessUncachedResource() { return containsStatelessUncachedResource; }
    std::map<const void *, NEO::GraphicsAllocation *> &getHostPtrMap() {
        return hostPtrMap;
    };

    const NEO::StreamProperties &getRequiredStreamState() const {
        return requiredStreamState;
    }
    const NEO::StreamProperties &getFinalStreamState() const {
        return finalStreamState;
    }
    NEO::StreamProperties &getRequiredStreamState() {
        return requiredStreamState;
    }
    NEO::StreamProperties &getFinalStreamState() {
        return finalStreamState;
    }

    auto getCommandsToPatch() {
        return std::span(commandsToPatch.begin(), commandsToPatch.end());
    }

    CmdListReturnPoints &getReturnPoints() {
        return returnPoints;
    }

    uint32_t getReturnPointsSize() const {
        return static_cast<uint32_t>(returnPoints.size());
    }

    void migrateSharedAllocations();

    bool getSystolicModeSupport() const {
        return systolicModeSupport;
    }

    NEO::PrefetchContext &getPrefetchContext() {
        return prefetchContext;
    }

    AppendedMemAdviseOperations &getMemAdviseOperations() {
        return memAdviseOperations;
    }

    NEO::HeapAddressModel getCmdListHeapAddressModel() const {
        return this->cmdListHeapAddressModel;
    }

    void setCmdListContext(ze_context_handle_t contextHandle) {
        this->hContext = contextHandle;
    }

    ze_context_handle_t getCmdListContext() const {
        return this->hContext;
    }

    uint32_t getPartitionCount() const {
        return this->partitionCount;
    }

    bool isImmediateType() const {
        return (this->cmdListType == CommandListType::typeImmediate);
    }

    bool isRequiredQueueUncachedMocs() const {
        return requiresQueueUncachedMocs;
    }

    Device *getDevice() const {
        return this->device;
    }

    NEO::CommandContainer &getCmdContainer() {
        return this->commandContainer;
    }

    void setEstimatedNumberOfCommands(uint32_t estimatedNumberOfCommands) {
        this->estimatedNumberOfCommands = estimatedNumberOfCommands;
    }

    NEO::CommandStreamReceiver *getCsr(bool copyOffload) const;

    bool hasKernelWithAssert() {
        return kernelWithAssertAppended;
    }

    bool isHeaplessModeEnabled() const {
        return heaplessModeEnabled;
    }

    bool getCmdListBatchBufferFlag() const {
        return dispatchCmdListBatchBufferAsPrimary;
    }

    bool getCmdListStateBaseAddressTracking() const {
        return stateBaseAddressTracking;
    }

    bool getCmdListScratchAddressPatchingEnabled() const {
        return scratchAddressPatchingEnabled;
    }
    bool isTaskCountUpdateFenceRequired() const {
        return taskCountUpdateFenceRequired;
    }

    bool isStatelessBuiltinsEnabled() const {
        return statelessBuiltinsEnabled;
    }

    bool isTextureCacheFlushPending() const {
        return textureCacheFlushPending;
    }

    void setTextureCacheFlushPending(bool isPending) {
        textureCacheFlushPending = isPending;
    }

    bool consumeTextureCacheFlushPending() {
        bool wasPending = textureCacheFlushPending;
        textureCacheFlushPending = false;
        return wasPending;
    }

    NEO::EngineGroupType getEngineGroupType() const {
        return engineGroupType;
    }

    bool isClosed() const {
        return closedCmdList;
    }
    ze_result_t obtainLaunchParamsFromExtensions(const ze_base_desc_t *desc, CmdListKernelLaunchParams &launchParams, ze_kernel_handle_t kernelHandle) const;
    ze_result_t obtainMemoryCopyParamsFromExtensions(const ze_base_desc_t *desc, CmdListMemoryCopyParams &memoryCopyParams, bool writesOnly) const;
    ze_result_t obtainCustomOperationParamsFromExtensions(const ze_base_desc_t *desc, CmdListCustomOperationParams &customOperationParams) const;

    void setGraphCaptureTarget(Graph *graph) {
        this->graphCapture = graph;
    }

    Graph *getGraphCaptureTarget() const {
        return this->graphCapture;
    }

    bool isCapturingGraph() const {
        return this->graphCapture != nullptr;
    }

    void setIsGraphInstantiationTarget(bool value) {
        this->isGraphInstantiationTarget = value;
    }

    bool getIsGraphInstantiationTarget() const {
        return this->isGraphInstantiationTarget;
    }

    Graph *releaseGraphCaptureTarget() {
        return std::exchange(this->graphCapture, nullptr);
    }

    template <CaptureApi api, typename... TArgs>
    ze_result_t capture(TArgs... apiArgs) {
        return L0::captureCommand<api>(*this, this->graphCapture, this->flatCapture.get(), apiArgs...);
    }

    inline bool getIsWalkerWithProfilingEnqueued() {
        return this->isWalkerWithProfilingEnqueued;
    }

    inline bool getAndClearIsWalkerWithProfilingEnqueued() {
        bool retVal = this->isWalkerWithProfilingEnqueued;
        this->isWalkerWithProfilingEnqueued = false;
        return retVal;
    }
    uint32_t getFrontEndPatchListCount() const {
        return frontEndPatchListCount;
    }

    uint32_t getHostFunctionWithMemorySynchronizationCount() const {
        return hostFunctionWithMemorySynchronizationCount;
    }

    uint32_t getHostFunctionWithoutMemorySynchronizationCount() const {
        return hostFunctionWithoutMemorySynchronizationCount;
    }

    size_t getTotalNoopSpace() const {
        return totalNoopSpace;
    }

    void forceDisableInOrderWaits() { inOrderWaitsDisabled = true; }

    ze_command_list_flags_t getCmdListFlags() const {
        return flags;
    }

    void setPatchingPreamble(bool patching);

    uint32_t getActiveScratchPatchElements() const {
        return activeScratchPatchElements;
    }
    bool isDualStreamCopyOffloadOperation(bool offloadOperation) const { return (getCopyOffloadModeForOperation(offloadOperation) == CopyOffloadModes::dualStream); }
    void saveLatestTagAndTaskCount(NEO::GraphicsAllocation *tagGpuAllocation, TaskCountType submittedTaskCount) {
        this->latesTagGpuAllocation = tagGpuAllocation;
        this->latestTaskCount = submittedTaskCount;
    }
    uint64_t getLatestTagGpuAddress() const {
        return this->latesTagGpuAllocation == nullptr ? 0 : this->latesTagGpuAllocation->getGpuAddress();
    }
    NEO::GraphicsAllocation *getLatestTagGpuAllocation() const {
        return this->latesTagGpuAllocation;
    }
    TaskCountType getLatestTaskCount() const {
        return this->latestTaskCount;
    }

    bool isInSynchronousMode() const {
        return this->isSyncModeQueue;
    }

    void addCleanupCallback(zex_command_list_cleanup_callback_fn_t callback, void *userData) {
        this->cleanupCallbacks.emplace_back(callback, userData);
    }

    void executeCleanupCallbacks();
    bool verifyMemory(const void *allocationPtr,
                      const void *expectedData,
                      size_t sizeOfComparison,
                      uint32_t comparisonMode) const;

    bool isBcsSplitEnabled() const { return (bcsSplitMode != BcsSplitParams::BcsSplitMode::disabled); }
    void setForSubCopyBcsSplit();
    bool isPatchPreambleEnabled() const { return patchPreambleEnabled; }
    void setupPatchPreambleEnabled(bool initValue);

    virtual void appendMultiPartitionPrologue(uint32_t partitionDataSize) = 0;
    virtual void appendMultiPartitionEpilogue() = 0;
    virtual bool kernelMemoryPrefetchEnabled() const = 0;

    void setStreamPropertiesDefaultSettings(NEO::StreamProperties &streamProperties);
    void enableInOrderExecution();
    bool isInOrderExecutionEnabled() const { return inOrderExecInfo.get(); }
    void storeReferenceTsToMappedEvents(bool clear);
    void addToMappedEventList(Event *event);
    const std::vector<Event *> &peekMappedEventList() { return mappedTsEventList; }
    void clearInOrderExecCounterAllocation();
    void enableSynchronizedDispatch(NEO::SynchronizedDispatchMode mode);
    NEO::SynchronizedDispatchMode getSynchronizedDispatchMode() const { return synchronizedDispatchMode; }
    void enableCopyOperationOffload();
    void setInterruptEventsCsr(NEO::CommandStreamReceiver &csr);
    std::shared_ptr<NEO::InOrderExecInfo> &getInOrderExecInfo() { return inOrderExecInfo; }
    size_t getInOrderExecDeviceRequiredSize() const;
    uint64_t getInOrderExecDeviceGpuAddress() const;
    size_t getInOrderExecHostRequiredSize() const;
    uint64_t getInOrderExecHostGpuAddress() const;
    void enableBcsSplit();
    void storeEventsForBcsSplit(const BcsSplitParams::MarkerEvent *markerEvent);
    BcsSplitParams::CmdListsForSplitContainer getRegularCmdListsForSplit(size_t totalTransferSize, size_t perEngineMaxSize, size_t splitQueuesCount);
    void dispatchRecordedBcsSplit();

    void disableFlatCapture() {
        flatCapture.reset();
    }

    RecordedApiCommands *getFlatCapture() {
        return flatCapture.get();
    }

    const RecordedApiCommands *getFlatCapture() const {
        return flatCapture.get();
    }

    void getPatchPreambleFullData(uint64_t &outCounterValue,
                                  uint64_t *&outHostAddress,
                                  uint64_t &outDeviceAddress,
                                  NEO::GraphicsAllocation *&outGraphicsAllocation);

    virtual void handlePostSyncPrintfAndAssert(bool hangDetected) {};

    AsyncPatchContainer &getAsyncPatchContainer() {
        return asyncPatchContainer;
    }

  protected:
    using CleanupCallbackT = std::pair<zex_command_list_cleanup_callback_fn_t, void *>;

    virtual void dispatchHostFunction(ze_host_function_callback_t pHostFunction,
                                      void *pUserData,
                                      bool memorySynchronizationRequired) = 0;

    virtual void addHostFunctionToPatchCommands(const NEO::HostFunction &hostFunction, bool memorySynchronizationRequired) = 0;

    static void NEO_HOST_FUNCTION_CALLBACK semaphoreWaitHostFunction(void *data) {
        DEBUG_BREAK_IF(data == nullptr);
        auto extSemaphoreHostFnData = static_cast<ExternalSemaphoreHostFunctionData *>(data);
        auto &cmdList = extSemaphoreHostFnData->cmdList;

        ExternalSemaphoreImp::semaphoreWait(extSemaphoreHostFnData->operationData);

        if (cmdList.isImmediateType()) {
            std::lock_guard<std::mutex> lock(cmdList.externalSemaphoreHostFunctionDataMutex);
            cmdList.externalSemaphoreHostFunctionData.erase(extSemaphoreHostFnData->self);
        }
    };
    static void NEO_HOST_FUNCTION_CALLBACK semaphoreSignalHostFunction(void *data) {
        DEBUG_BREAK_IF(data == nullptr);
        auto extSemaphoreHostFnData = static_cast<ExternalSemaphoreHostFunctionData *>(data);
        auto &cmdList = extSemaphoreHostFnData->cmdList;

        ExternalSemaphoreImp::semaphoreSignal(extSemaphoreHostFnData->operationData);

        if (cmdList.isImmediateType()) {
            std::lock_guard<std::mutex> lock(cmdList.externalSemaphoreHostFunctionDataMutex);
            cmdList.externalSemaphoreHostFunctionData.erase(extSemaphoreHostFnData->self);
        }
    };

    NEO::GraphicsAllocation *getAllocationFromHostPtrMap(const void *buffer, uint64_t bufferSize, bool copyOffload);
    NEO::GraphicsAllocation *getAllocationFromHostPtrMap(const void *buffer, uint64_t bufferSize, bool copyOffload, bool *nonUsmHostPtrPartialOverlapFound);
    NEO::GraphicsAllocation *getHostPtrAlloc(const void *buffer, uint64_t bufferSize, bool hostCopyAllowed, bool copyOffload);
    bool setupTimestampEventForMultiTile(Event *signalEvent);
    bool isTimestampEventForMultiTile(Event *signalEvent);
    bool getDcFlushRequired(bool externalCondition) const {
        return externalCondition ? dcFlushSupport : false;
    }
    MOCKABLE_VIRTUAL void synchronizeEventList(uint32_t numWaitEvents, ze_event_handle_t *waitEventList);

    bool isNonDualStreamCopyOffloadOperation(bool offloadOperation) const { return offloadOperation && !isDualStreamCopyOffloadOperation(offloadOperation); }
    void registerWalkerWithProfilingEnqueued(Event *event);
    bool forceStateless(size_t size) const {
        return (this->cmdListHeapAddressModel == NEO::HeapAddressModel::globalStateless) || this->isStatelessBuiltinsEnabled() || size >= 4ull * MemoryConstants::gigaByte;
    }

    void resetBcsSplitEvents(bool release);
    void ensureSubCmdLists(size_t count);
    void destroyRecordedBcsSplitResources();

    std::map<const void *, NEO::GraphicsAllocation *> hostPtrMap;
    NEO::PrivateAllocsToReuseContainer ownedPrivateAllocations;
    std::vector<NEO::GraphicsAllocation *> patternAllocations;
    std::vector<NEO::TagNodeBase *> patternTags;
    std::vector<std::weak_ptr<Kernel>> printfKernelContainer;
    std::vector<CleanupCallbackT> cleanupCallbacks;
    std::vector<Event *> mappedTsEventList;
    std::vector<Event *> interruptEvents;
    std::vector<const BcsSplitParams::MarkerEvent *> eventsForRecordedBcsSplit;
    std::vector<CommandList *> subCmdListsForRecordedBcsSplit;

    struct ExternalSemaphoreHostFunctionData {
        CommandList &cmdList;
        const ExternalSemaphoreOperationData operationData;
        std::list<ExternalSemaphoreHostFunctionData>::iterator self;
    };
    std::list<ExternalSemaphoreHostFunctionData> externalSemaphoreHostFunctionData;
    std::mutex externalSemaphoreHostFunctionDataMutex;

    std::shared_ptr<NEO::InOrderExecInfo> inOrderExecInfo;

    NEO::CommandContainer commandContainer;

    CmdListReturnPoints returnPoints;
    NEO::StreamProperties requiredStreamState{};
    NEO::StreamProperties finalStreamState{};
    CommandsToPatch commandsToPatch{};
    UnifiedMemoryControls unifiedMemoryControls;
    NEO::PrefetchContext prefetchContext;
    AppendedMemAdviseOperations memAdviseOperations;
    NEO::L1CachePolicy l1CachePolicyData{};
    NEO::EncodeDummyBlitWaArgs dummyBlitWa{};
    AsyncPatchContainer asyncPatchContainer{};

    int64_t currentSurfaceStateBaseAddress = NEO::StreamProperty64::initValue;
    int64_t currentDynamicStateBaseAddress = NEO::StreamProperty64::initValue;
    int64_t currentIndirectObjectBaseAddress = NEO::StreamProperty64::initValue;
    int64_t currentBindingTablePoolBaseAddress = NEO::StreamProperty64::initValue;

    TaskCountType latestTaskCount = 0;

    NEO::GraphicsAllocation *latesTagGpuAllocation = nullptr;
    ze_context_handle_t hContext = nullptr;
    CommandQueue *cmdQImmediate = nullptr;
    CommandQueue *cmdQImmediateCopyOffload = nullptr;
    Device *device = nullptr;
    NEO::ScratchSpaceController *usedScratchController = nullptr;
    Graph *graphCapture = nullptr;                    // immediate cmdlist graph capturing
    std::unique_ptr<RecordedApiCommands> flatCapture; // regular cmdlist capturing
    bool isGraphInstantiationTarget = false;          // regular cmdlist used to replay recorded (immediate) graph commands

    size_t minimalSizeForBcsSplit = 4 * MemoryConstants::megaByte;
    size_t cmdListCurrentStartOffset = 0;
    size_t maxFillPatternSizeForCopyEngine = 0;
    size_t totalNoopSpace = 0;

    static constexpr bool cmdListDefaultCoherency = false;
    static constexpr bool cmdListDefaultDisableOverdispatch = true;
    static constexpr bool cmdListDefaultPipelineSelectModeSelected = true;

    uint32_t commandListPerThreadScratchSize[2]{};

    ze_command_list_flags_t flags = 0u;
    NEO::PreemptionMode commandListPreemptionMode = NEO::PreemptionMode::Initial;
    NEO::EngineGroupType engineGroupType = NEO::EngineGroupType::maxEngineGroups;
    NEO::HeapAddressModel cmdListHeapAddressModel = NEO::HeapAddressModel::privateHeaps;
    std::optional<uint32_t> ordinal = std::nullopt;

    CommandListType cmdListType = CommandListType::typeRegular;
    CopyOffloadMode copyOffloadMode = CopyOffloadModes::disabled;
    uint8_t powerHint = 0u;
    BcsSplitParams::BcsSplitMode bcsSplitMode = BcsSplitParams::BcsSplitMode::disabled;
    NEO::SynchronizedDispatchMode synchronizedDispatchMode = NEO::SynchronizedDispatchMode::disabled;
    uint32_t partitionCount = 1;
    uint32_t defaultMocsIndex = 0;
    int32_t defaultPipelinedThreadArbitrationPolicy = NEO::ThreadArbitrationPolicy::NotPresent;
    uint32_t maxLocalSubRegionSize = 0;
    uint32_t frontEndPatchListCount = 0;
    uint32_t activeScratchPatchElements = 0;
    uint32_t hostFunctionWithMemorySynchronizationCount = 0;
    uint32_t hostFunctionWithoutMemorySynchronizationCount = 0;
    uint32_t syncDispatchQueueId = std::numeric_limits<uint32_t>::max();
    uint32_t estimatedNumberOfCommands = 0;

    bool isSyncModeQueue = false;
    bool isTbxMode = false;
    bool commandListSLMEnabled = false;
    bool requiresQueueUncachedMocs = false;
    bool immediateCmdListHeapSharing = false;
    bool indirectAllocationsAllowed = false;
    bool internalUsage = false;
    bool containsCooperativeKernelsFlag = false;
    bool containsStatelessUncachedResource = false;
    bool performMemoryPrefetch = false;
    bool frontEndStateTracking = false;
    bool dcFlushSupport = false;
    bool systolicModeSupport = false;
    bool pipelineSelectStateTracking = false;
    bool stateComputeModeTracking = false;
    bool signalAllEventPackets = false;
    bool stateBaseAddressTracking = false;
    bool doubleSbaWa = false;
    bool containsAnyKernel = false;
    bool pipeControlMultiKernelEventSync = false;
    bool compactL3FlushEventPacket = false;
    bool dynamicHeapRequired = false;
    bool kernelWithAssertAppended = false;
    bool dispatchCmdListBatchBufferAsPrimary = false;
    bool copyThroughLockedPtrEnabled = false;
    bool isSmallBarConfigPresent = false;
    bool useOnlyGlobalTimestamps = false;
    bool heaplessModeEnabled = false;
    NEO::BuiltIn::AddressingMode defaultBuiltInMode;
    bool scratchAddressPatchingEnabled = false;
    bool taskCountUpdateFenceRequired = false;
    bool statelessBuiltinsEnabled = false;
    bool l3FlushAfterPostSyncEnabled = false;
    bool systemMemoryFenceInPostSyncRequired = false;
    bool textureCacheFlushPending = false;
    bool closedCmdList = false;
    bool isWalkerWithProfilingEnqueued = false;
    bool shouldRegisterEnqueuedWalkerWithProfiling = false;
    bool inOrderWaitsDisabled = false;
    bool swTagsEnabled = false;
    bool patchPreambleEnabled = false;

    NEO::QueueThrottle queueThrottle = NEO::QueueThrottle::MEDIUM;
};

using CommandListAllocatorFn = CommandList *(*)(uint32_t);
extern CommandListAllocatorFn commandListFactory[];
extern CommandListAllocatorFn commandListFactoryImmediate[];

template <uint32_t productFamily, typename CommandListType>
struct CommandListPopulateFactory {
    CommandListPopulateFactory() {
        commandListFactory[productFamily] = CommandList::Allocator<CommandListType>::allocate;
    }
};

template <uint32_t productFamily, typename CommandListType>
struct CommandListImmediatePopulateFactory {
    CommandListImmediatePopulateFactory() {
        commandListFactoryImmediate[productFamily] = CommandList::Allocator<CommandListType>::allocate;
    }
};

} // namespace L0
