/*
Crafter®.Graphics
Copyright (C) 2026 Catcrafts®
catcrafts.net

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License version 3.0 as published by the Free Software Foundation;

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
*/

// Regression test for issue #60: VulkanBuffer::FlushDevice / FlushHost used to
// call vkFlushMappedMemoryRanges / vkInvalidateMappedMemoryRanges
// unconditionally. They now record the *chosen* memory type's propertyFlags at
// Create time and skip the flush/invalidate when the memory is HOST_COHERENT
// (coherent memory makes host/device writes mutually visible with no explicit
// flush).
//
// The gate is pure logic over the recorded flags, so this test drives it
// directly with no GPU device: it stamps memoryPropertyFlagsChosen and verifies
// the coherent path returns without issuing any Vulkan call. With no device
// created, Device::device is VK_NULL_HANDLE — so if the gate were removed, the
// flush call would dereference a null dispatch handle and crash. Reaching the
// line after the calls is the assertion that the gate held.

#include <cstdlib>
#include "vulkan/vulkan.h"

import Crafter.Graphics;
import std;
using namespace Crafter;

namespace {

int failures = 0;

void Check(bool ok, std::string_view what) {
    std::println("{}  {}", ok ? "PASS" : "FAIL", what);
    if (!ok) ++failures;
}

constexpr auto DEVICE_LOCAL  = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
constexpr auto HOST_VISIBLE  = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
constexpr auto HOST_COHERENT = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;

} // namespace

int main() {
    // No device is created, so Device::device stays VK_NULL_HANDLE; any real
    // flush/invalidate call below would fault. The coherent gate must prevent
    // it.
    Check(Device::device == VK_NULL_HANDLE,
          "no Vulkan device created — a real flush would fault");

    {
        VulkanBuffer<float, true> buf;
        // Coherent type chosen (even though no COHERENT bit was requested):
        // both flush paths must early-return without touching Vulkan.
        buf.memoryPropertyFlagsChosen = HOST_VISIBLE | HOST_COHERENT;
        buf.FlushDevice();
        buf.FlushHost();
        Check(true, "FlushDevice/FlushHost skip the Vulkan call on coherent memory");
    }

    {
        // A non-coherent type would require the flush — verify the recorded
        // flags reflect the gate's decision input rather than the request. The
        // bit test is the exact condition FlushDevice/FlushHost branch on.
        VulkanBuffer<float, true> buf;
        buf.memoryPropertyFlagsChosen = HOST_VISIBLE | DEVICE_LOCAL;
        Check(!(buf.memoryPropertyFlagsChosen & HOST_COHERENT),
              "non-coherent chosen type is not gated as coherent (flush still required)");
    }

    if (failures != 0) {
        std::println("{} check(s) failed", failures);
        return EXIT_FAILURE;
    }
    std::println("all checks passed");
    return EXIT_SUCCESS;
}
