#!/usr/bin/python3 -i
#
# Copyright (c) 2021-2025 The Khronos Group Inc.
#
# 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.

import os
import sys
from generators.generator_utils import buildListVUID, getVUID
from vulkan_object import CommandScope
from base_generator import BaseGenerator
#
# CommandValidationOutputGenerator - Generate implicit vkCmd validation for CoreChecks
class CommandValidationOutputGenerator(BaseGenerator):
    def __init__(self,
                 valid_usage_file):
        BaseGenerator.__init__(self)
        self.valid_vuids = buildListVUID(valid_usage_file)
    #
    # Called at beginning of processing as file is opened
    def generate(self):
        self.write(f'''// *** THIS FILE IS GENERATED - DO NOT EDIT ***
            // See {os.path.basename(__file__)} for modifications

            /***************************************************************************
            *
            * Copyright (c) 2021-2025 Valve Corporation
            * Copyright (c) 2021-2025 LunarG, Inc.
            *
            * 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.
            ****************************************************************************/\n''')
        self.write('// NOLINTBEGIN') # Wrap for clang-tidy to ignore

        if self.filename == 'command_validation.h':
            self.generateHeader()
        elif self.filename == 'command_validation.cpp':
            self.generateSource()
        else:
            self.write(f'\nFile name {self.filename} has no code to generate\n')

        self.write('// NOLINTEND') # Wrap for clang-tidy to ignore

    def generateHeader(self):
        out = []
        out.append('''
            #pragma once

            #include "generated/error_location_helper.h"

            enum class CommandScope { Inside, Outside, Both };

            struct CommandValidationInfo {
                const char* recording_vuid;
                const char* buffer_level_vuid;

                VkQueueFlags queue_flags;
                const char* queue_vuid;

                CommandScope render_pass_scope;
                const char* render_pass_vuid;

                CommandScope video_coding_scope;
                const char* video_coding_vuid;

                const char* suspended_vuid;

                bool state;
                bool action;
                bool synchronization;
            };

            const CommandValidationInfo& GetCommandValidationInfo(vvl::Func command);
            ''')
        self.write("".join(out))

    def generateSource(self):
        out = []
        out.append('''
            #include "command_validation.h"
            #include "containers/custom_containers.h"

            extern const char *kVUIDUndefined;

            using Func = vvl::Func;
            ''')
        out.append('// clang-format off\n')
        out.append('static const auto &GetCommandValidationTable() {\n')
        out.append('static const vvl::unordered_map<Func, CommandValidationInfo> kCommandValidationTable {\n')
        for command in [x for x in self.vk.commands.values() if x.name.startswith('vkCmd')]:
            out.append(f'{{Func::{command.name}, {{\n')
            # recording_vuid
            alias_name = command.name if command.alias is None else command.alias
            vuid = getVUID(self.valid_vuids, f'VUID-{alias_name}-commandBuffer-recording')
            out.append(f'    {vuid},\n')

            # buffer_level_vuid
            if command.primary and command.secondary:
                out.append('    nullptr,\n')
            elif command.primary:
                vuid = getVUID(self.valid_vuids, f'VUID-{alias_name}-bufferlevel')
                out.append(f'    {vuid},\n')
            else:
                # Currently there is only "primary" or "primary,secondary" in XML
                # Hard to predict what might change, so will error out instead if assumption breaks
                print('cmdbufferlevel attribute was and not known, need to update generation code')
                sys.exit(1)

            queue_flags = ' | '.join(command.queues)
            vuid = getVUID(self.valid_vuids, f'VUID-{alias_name}-commandBuffer-cmdpool')
            out.append(f'    {queue_flags}, {vuid},\n')

            # render_pass / render_pass_vuid
            renderPassType = 'CommandScope::Both'
            vuid = 'kVUIDUndefined' # Only will be a VUID if not BOTH
            if command.renderPass is CommandScope.INSIDE:
                renderPassType = 'CommandScope::Inside'
                vuid = getVUID(self.valid_vuids, f'VUID-{alias_name}-renderpass')
            elif command.renderPass is CommandScope.OUTSIDE:
                renderPassType = 'CommandScope::Outside'
                vuid = getVUID(self.valid_vuids, f'VUID-{alias_name}-renderpass')
            out.append(f'    {renderPassType}, {vuid},\n')

            # video_coding / video_coding_vuid
            videoCodingType = 'CommandScope::Both'
            vuid = 'kVUIDUndefined' # Only will be a VUID if not BOTH
            if command.videoCoding is CommandScope.INSIDE:
                videoCodingType = 'CommandScope::Inside'
                vuid = getVUID(self.valid_vuids, f'VUID-{alias_name}-videocoding')
            elif command.videoCoding is CommandScope.OUTSIDE or command.videoCoding is CommandScope.NONE:
                videoCodingType = 'CommandScope::Outside'
                vuid = getVUID(self.valid_vuids, f'VUID-{alias_name}-videocoding')
            out.append(f'    {videoCodingType}, {vuid},\n')

            # command type
            is_state = 'true' if 'state' in command.tasks else 'false'
            is_action = 'true' if 'action' in command.tasks else 'false'
            is_synchronization = 'true' if 'synchronization' in command.tasks else 'false'

            vuid = 'kVUIDUndefined'
            if is_action == 'true' or is_synchronization == 'true':
                vuid = getVUID(self.valid_vuids, f'VUID-{alias_name}-suspended')
            out.append(f'    {vuid},\n')

            out.append(f'    {is_state}, {is_action}, {is_synchronization},\n')

            out.append('}},\n')
        out.append('};\n')
        out.append('return kCommandValidationTable;\n')
        out.append('}\n')
        out.append('// clang-format on\n')

        out.append('''
            const CommandValidationInfo& GetCommandValidationInfo(vvl::Func command) {
                auto info_it = GetCommandValidationTable().find(command);
                assert(info_it != GetCommandValidationTable().end());
                return info_it->second;
            }
            ''')
        self.write("".join(out))