// ==========================================================
// Poe2VisualTool - Bộ kiểm thử đơn vị độc lập (Standalone Unit Tests)
// C++23 Native — Kiểm chứng 100% không cần game thật và không cần Admin
//
// Chạy: bin/Release/Poe2VisualTool_Tests.exe
// ==========================================================

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif

#include <windows.h>

#include "common/protocol.hpp"
#include "common/window_utils.hpp"
#include "common/string_utils.hpp"
#include "common/time_utils.hpp"
#include "common/toml_parser.hpp"
#include "memory/imemory_reader.hpp"
#include "memory/memory_reader.hpp"
#include "memory/simulated_reader.hpp"
#include "memory/aob_scanner.hpp"
#include "memory/pe_fingerprint.hpp"
#include "memory/offsets_store.hpp"
#include "memory/visual_mods.hpp"
#include "visual/world_to_screen.hpp"
#include "visual/poi_radar.hpp"
#include "visual/mod_colorizer.hpp"
#include "visual/particle_reducer.hpp"
#include "inspector/item_parser.hpp"
#include "inspector/waystone_rules.hpp"
#include "security/hwid_generator.hpp"
#include "security/license_validator.hpp"
#include "platform/cloud_sync.hpp"
#include "platform/sound_effects.hpp"
#include "platform/i18n.hpp"
#include "native_ui/ui_types.hpp"
#include "native_ui/tooltip_view.hpp"
#include "native_ui/radar_hud.hpp"
#include "native_ui/settings_panel.hpp"
#include "native_ui/overlay_window.hpp"
#include "native_ui/system_tray.hpp"

#include <algorithm>
#include <chrono>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

namespace {

int g_failures = 0;
int g_checks = 0;

#define CHECK(cond, msg)                                                       \
    do {                                                                       \
        ++g_checks;                                                            \
        if (!(cond)) {                                                         \
            ++g_failures;                                                      \
            std::cerr << "  [FAIL] " << msg << " (" << __FILE__ << ":"        \
                      << __LINE__ << ")" << std::endl;                         \
        }                                                                      \
    } while (0)

std::string TestName(const char* prefix) {
    return std::string(prefix) + "_" + std::to_string(GetCurrentProcessId());
}

// ==========================================================
// 1. Kiểm thử VisualModsEngine: Zoom Cap 8x, zFar Culling & Volumetric Fog
// ==========================================================
void TestVisualModsEngine() {
    std::cout << "[Test 1] VisualModsEngine: Camera Zoom Hack (Zoom Cap 8x, zFar Culling) & Volumetric Fog..." << std::endl;

    SimulatedMemoryReader sim;
    CHECK(sim.Initialize() && sim.Attach(game_layout::kSimulatedPid), "Khoi tao SimulatedMemoryReader phai thanh cong");

    VisualModsEngine visual(sim);
    visual.SetCameraAddress(sim.CameraAddress());
    visual.SetFogAddress(sim.FogAddress());

    CHECK(visual.CameraAddress() == sim.CameraAddress() && visual.FogAddress() == sim.FogAddress(), "Addresses phai khop simulated address");

    CHECK(!visual.IsZoomActive() && !visual.IsFogDisabled() &&
          visual.OrigMaxDistance() == 45.0f && visual.OrigZFar() == 150.0f && visual.OrigFogDensity() == 1.0f,
          "Trang thai ban dau VisualModsEngine sai");

    float readMaxDist = 0.0f, readZFar = 0.0f;
    const float expectedZFar = 150.0f * (2.2f * 1.35f);
    CHECK(visual.ApplyZoom(2.2f) && visual.IsZoomActive() && std::fabs(visual.CurrentZoomMultiplier() - 2.2f) < 0.001f &&
          sim.ReadValue<float>(sim.CameraAddress() + 0x34, readMaxDist) && std::fabs(readMaxDist - 99.0f) < 0.01f &&
          sim.ReadValue<float>(sim.CameraAddress() + 0x40, readZFar) && std::fabs(readZFar - expectedZFar) < 0.01f,
          "ApplyZoom(2.2f) va zFar Culling Fix failed");

    visual.ApplyZoom(0.5f);
    float clampedMin = visual.CurrentZoomMultiplier();
    visual.ApplyZoom(12.0f);
    float clampedMax = visual.CurrentZoomMultiplier();
    CHECK(std::fabs(clampedMin - 1.0f) < 0.001f && std::fabs(clampedMax - 8.0f) < 0.001f,
          "Zoom multiplier clamping [1.0, 8.0] failed");

    bool restored = visual.RestoreCamera() && !visual.IsZoomActive();
    sim.ReadValue<float>(sim.CameraAddress() + 0x34, readMaxDist);
    sim.ReadValue<float>(sim.CameraAddress() + 0x40, readZFar);
    CHECK(restored && std::fabs(readMaxDist - 45.0f) < 0.01f && std::fabs(readZFar - 150.0f) < 0.01f,
          "RestoreCamera failed");

    float readFog = 1.0f;
    bool disableFogOk = visual.DisableFog() && visual.IsFogDisabled() &&
                        sim.ReadValue<float>(sim.FogAddress(), readFog) && std::fabs(readFog - 0.0f) < 0.0001f;
    bool restoreFogOk = visual.RestoreFog() && !visual.IsFogDisabled() &&
                        sim.ReadValue<float>(sim.FogAddress(), readFog) && std::fabs(readFog - 1.0f) < 0.0001f;
    CHECK(disableFogOk && restoreFogOk, "DisableFog / RestoreFog failed");

    // UnlockZoomCap và StepZoom
    CHECK(visual.UnlockZoomCap(8.0f) && visual.ZoomCap() == 8.0f, "UnlockZoomCap(8.0f) phai thanh cong");
    CHECK(visual.StepZoom(0.2f) && visual.CurrentZoomMultiplier() <= 8.0f, "StepZoom(0.2f) failed");

    // PanicRestoreAll
    visual.ApplyZoom(2.8f);
    visual.DisableFog();
    visual.PanicRestoreAll();
    sim.ReadValue<float>(sim.CameraAddress() + 0x34, readMaxDist);
    sim.ReadValue<float>(sim.CameraAddress() + 0x40, readZFar);
    sim.ReadValue<float>(sim.FogAddress(), readFog);
    CHECK(!visual.IsZoomActive() && !visual.IsFogDisabled() &&
          std::fabs(readMaxDist - 45.0f) < 0.01f && std::fabs(readZFar - 150.0f) < 0.01f && std::fabs(readFog - 1.0f) < 0.0001f,
          "PanicRestoreAll failed");

    std::cout << "  -> VisualModsEngine: Zoom Cap 8x, zFar Culling & Panic Restore OK" << std::endl;
}

// ==========================================================
// 2. Kiểm thử POE2 Real Camera Zoom Clamp (Pattern, RVA 0x16D5FC) & 4x4 Matrix
// ==========================================================
void TestPoe2RealCameraZoomClampAndMatrix() {
    std::cout << "[Test 2] POE2 Real Camera Zoom Clamp & 4x4 Matrix (RVA 0x16D5FC, Matrix +0x1A0)..." << std::endl;

    const uintptr_t poe2Base = 0x7FF6AFC20000ULL;
    bool constantsOk = (kPoe2CameraZoomOffset == 0x520) && (kPoe2CameraZoomTargetOffset == 0x528)
                    && (kPoe2CameraDistanceOffset == 0x438) && (kPoe2CameraMatrixOffset == 0x1A0)
                    && (kPoe2InGameStateStaticRva == 0x4430108) && (kPoe2CameraInGameStateOffset == 0x368)
                    && (kPoe2RealZoomClampRva == 0x16D5FC)
                    && (poe2Base + kPoe2RealZoomClampRva == 0x7FF6AFD8D5FCULL);
    CHECK(constantsOk, "Hang so kien truc POE2 Camera Zoom va Real Clamp RVA phai chinh xac");

    auto clampPat = AobPattern::Parse(kPoe2RealZoomClampPattern);
    bool patOk = clampPat.Valid() && clampPat.Size() == 28
              && clampPat.bytes[12] == 0xF3 && clampPat.bytes[13] == 0x0F && clampPat.bytes[14] == 0x5D && clampPat.bytes[15] == 0x0D
              && clampPat.wildcard[16] && clampPat.wildcard[17] && clampPat.wildcard[18] && clampPat.wildcard[19];
    CHECK(patOk, "kPoe2RealZoomClampPattern 28 byte va minss opcode voi disp32 wildcard phai hop le");

    SimulatedMemoryReader sim;
    CHECK(sim.Initialize(0x5000000) && sim.Attach(game_layout::kSimulatedPid), "sim.Initialize va Attach (80MB)");

    const uintptr_t inGameStateAddr = sim.BaseAddress() + 0x300000;
    const uintptr_t realCamAddr = sim.BaseAddress() + 0x350000;
    sim.WriteValue<uintptr_t>(sim.BaseAddress() + kPoe2InGameStateStaticRva, inGameStateAddr);
    sim.WriteValue<uintptr_t>(inGameStateAddr + kPoe2CameraInGameStateOffset, realCamAddr);

    sim.WriteValue<float>(realCamAddr + kPoe2CameraMatrixOffset, 1.25f);
    sim.WriteValue<float>(realCamAddr + kPoe2CameraMatrixOffset + 0x14, 1.50f);
    sim.WriteValue<float>(realCamAddr + kPoe2CameraZoomOffset, 1.0f);
    sim.WriteValue<float>(realCamAddr + kPoe2CameraZoomTargetOffset, 1.0f);
    sim.WriteValue<float>(realCamAddr + kPoe2CameraDistanceOffset, 42.0f);

    float readMatrixVal1 = 0.0f, readMatrixVal2 = 0.0f;
    sim.ReadValue<float>(realCamAddr + kPoe2CameraMatrixOffset, readMatrixVal1);
    sim.ReadValue<float>(realCamAddr + kPoe2CameraMatrixOffset + 0x14, readMatrixVal2);
    CHECK(readMatrixVal1 == 1.25f && readMatrixVal2 == 1.50f, "Doc 4x4 matrix tu camera address thanh cong");

    std::cout << "  -> POE2 Real Camera Zoom Clamp & Matrix OK" << std::endl;
}

// ==========================================================
// 3. Kiểm thử POE2 Minimap Shader Patches & AOB Scanner (GridFill, Uniform, Bound)
// ==========================================================
void TestPoe2MinimapShaderPatchesAndAOB() {
    std::cout << "[Test 3] POE2 Minimap Shader Patches & AOB Scanner (Build 6AA2213C)..." << std::endl;

    // 1. Hằng số RVA và cấu trúc opcode theo Doc 43
    bool rvaOk = (kPoe2MinimapGridFillRva == 0x120D8F8) && (kPoe2MinimapUniformRva == 0x120E480) && (kPoe2MinimapBoundRva == 0x120E40F);
    auto gridPat = AobPattern::Parse(kPoe2MinimapGridFillPattern);
    auto uniformPat = AobPattern::Parse(kPoe2MinimapUniformPattern);
    auto boundPat = AobPattern::Parse(kPoe2MinimapBoundPattern);
    CHECK(rvaOk && gridPat.Valid() && gridPat.Size() == 19 &&
          uniformPat.Valid() && uniformPat.Size() == 15 &&
          boundPat.Valid() && boundPat.Size() == 16,
          "Minimap Shader Patches RVA va AOB pattern (19, 15, 16 byte) phai hop le");

    // 2. Thiết lập Simulated Memory Reader (32 MB)
    SimulatedMemoryReader sim;
    CHECK(sim.Initialize(32 * 1024 * 1024) && sim.Attach(game_layout::kSimulatedPid), "sim.Initialize (32MB) va Attach");

    const uintptr_t modBase = sim.BaseAddress();
    const uintptr_t gridAddr = modBase + kPoe2MinimapGridFillRva;
    const uintptr_t uniformAddr = modBase + kPoe2MinimapUniformRva;
    const uintptr_t boundAddr = modBase + kPoe2MinimapBoundRva;

    const uint8_t origGridBytes[6] = { 0x44, 0x38, 0x59, 0x61, 0x75, 0x1F };
    const uint8_t origUniformBytes[2] = { 0x74, 0x05 };
    const uint8_t origBoundBytes[3] = { 0x0F, 0x97, 0xC0 };

    sim.Write(gridAddr, origGridBytes, sizeof(origGridBytes));
    sim.Write(uniformAddr, origUniformBytes, sizeof(origUniformBytes));
    sim.Write(boundAddr, origBoundBytes, sizeof(origBoundBytes));

    VisualModsEngine visual(sim);
    visual.SetModuleBounds(modBase, 32 * 1024 * 1024);
    CHECK(!visual.AreMinimapShaderPatchesActive() &&
          visual.MinimapGridPatchRecord().address == gridAddr &&
          visual.MinimapUniformPatchRecord().address == uniformAddr &&
          visual.MinimapBoundPatchRecord().address == boundAddr,
          "Trang thai khoi tao patch records phai khop RVA");

    // 3. Thực thi ApplyMinimapShaderPatches()
    CHECK(visual.ApplyMinimapShaderPatches() && visual.AreMinimapShaderPatchesActive() &&
          visual.MinimapGridPatchRecord().isPatched && visual.MinimapUniformPatchRecord().isPatched &&
          visual.MinimapBoundPatchRecord().isPatched,
          "ApplyMinimapShaderPatches phai thanh cong va set isPatched");

    uint8_t readGrid[6] = {};
    uint8_t readUniform[2] = {};
    uint8_t readBound[3] = {};
    sim.Read(gridAddr, readGrid, 6);
    sim.Read(uniformAddr, readUniform, 2);
    sim.Read(boundAddr, readBound, 3);
    const uint8_t expectedGrid[6] = { 0xEB, 0x23, 0x90, 0x90, 0x90, 0x90 };
    const uint8_t expectedUniform[2] = { 0x90, 0x90 };
    const uint8_t expectedBound[3] = { 0x30, 0xC0, 0x90 };
    CHECK(std::memcmp(readGrid, expectedGrid, 6) == 0 &&
          std::memcmp(readUniform, expectedUniform, 2) == 0 &&
          std::memcmp(readBound, expectedBound, 3) == 0,
          "RAM sau patch phai khop byte va (EB 23 90..., 90 90, 30 C0 90)");

    // 4. RestoreMinimapShaderPatches()
    CHECK(visual.RestoreMinimapShaderPatches() && !visual.AreMinimapShaderPatchesActive() &&
          !visual.MinimapGridPatchRecord().isPatched,
          "RestoreMinimapShaderPatches phai thanh cong");

    sim.Read(gridAddr, readGrid, 6);
    sim.Read(uniformAddr, readUniform, 2);
    sim.Read(boundAddr, readBound, 3);
    CHECK(std::memcmp(readGrid, origGridBytes, 6) == 0 &&
          std::memcmp(readUniform, origUniformBytes, 2) == 0 &&
          std::memcmp(readBound, origBoundBytes, 3) == 0,
          "Byte goc duoc khoi phuc 100% sau Restore");

    // 5. AOB Pattern Matching trong không gian bộ nhớ
    const uintptr_t plantGridAob = modBase + 0x100000;
    const uintptr_t plantUniformAob = modBase + 0x100100;
    const uintptr_t plantBoundAob = modBase + 0x100200;

    const uint8_t gridFullAobBytes[19] = { 0x48, 0x8B, 0x41, 0x70, 0x45, 0x33, 0xDB, 0x48, 0x39, 0x41, 0x68, 0x75, 0x1F, 0x44, 0x38, 0x59, 0x61, 0x75, 0x1F };
    const uint8_t uniformFullAobBytes[15] = { 0x41, 0x80, 0x7F, 0x61, 0x00, 0x74, 0x05, 0x0F, 0x28, 0xC7, 0xEB, 0x03, 0x0F, 0x28, 0xC8 };
    const uint8_t boundFullAobBytes[16] = { 0x0F, 0x2F, 0x15, 0xAA, 0xBB, 0xCC, 0xDD, 0x0F, 0x97, 0xC0, 0x89, 0x85, 0x78, 0x0B, 0x00, 0x00 };

    sim.Write(plantGridAob, gridFullAobBytes, sizeof(gridFullAobBytes));
    sim.Write(plantUniformAob, uniformFullAobBytes, sizeof(uniformFullAobBytes));
    sim.Write(plantBoundAob, boundFullAobBytes, sizeof(boundFullAobBytes));

    ModuleInfo simMod{ "sim", modBase, 32 * 1024 * 1024 };
    uintptr_t hitGrid = AobScanner::ScanModule(sim, simMod, gridPat, 4 * 1024 * 1024);
    uintptr_t hitUniform = AobScanner::ScanModule(sim, simMod, uniformPat, 4 * 1024 * 1024);
    uintptr_t hitBound = AobScanner::ScanModule(sim, simMod, boundPat, 4 * 1024 * 1024);
    CHECK(hitGrid == plantGridAob && hitUniform == plantUniformAob && hitBound == plantBoundAob,
          "AOB Scanner phai tim thay ca 3 patterns (Grid, Uniform, Bound)");

    // 6. PanicRestoreAll() tích hợp
    visual.ApplyMinimapShaderPatches();
    visual.PanicRestoreAll();
    CHECK(!visual.AreMinimapShaderPatchesActive(), "PanicRestoreAll phai hoan nguyen Minimap Shader Patches");

    std::cout << "  -> POE2 Minimap Shader Patches & AOB Scanner OK" << std::endl;
}

// ==========================================================
// 4. Kiểm thử Chọn đường dẫn game 4 tầng & IPC Protocol Contract
// ==========================================================
void TestFindOrPromptPoe2ClientExeAndConfig() {
    std::cout << "[Test 4] 4-Tier Game Exe Detection, Config TOML & Protocol Contract..." << std::endl;

    // 1. Kiểm chứng kích thước và offset của VisualToolSharedState
    bool sizeOk = (sizeof(VisualToolSharedState) == 48);
    CHECK(sizeOk, "VisualToolSharedState phai dung 48 bytes");
    bool magicOk = (kVisualToolMagic == 0x56543031u);
    CHECK(magicOk, "kVisualToolMagic == 'VT01'");
    bool flag1Ok = (kVisualFlagEnableZoom == 1u);
    CHECK(flag1Ok, "kVisualFlagEnableZoom == 1");
    bool flag2Ok = (kVisualFlagDisableFog == 2u);
    CHECK(flag2Ok, "kVisualFlagDisableFog == 2");
    bool flag3Ok = (kVisualFlagWheelDrive == 4u);
    CHECK(flag3Ok, "kVisualFlagWheelDrive == 4");
    bool flag4Ok = (kVisualFlagPanic == 8u);
    CHECK(flag4Ok, "kVisualFlagPanic == 8");
    bool flag5Ok = (kVisualFlagAutoReveal == 32u);
    CHECK(flag5Ok, "kVisualFlagAutoReveal == 32");

    // 2. Tạo mock exe file trên đĩa để test Tier 1 Config TOML
    const std::wstring tempDir = common::GetExecutableDirectory();
    const std::wstring mockExePath = tempDir + L"\\mock_PathOfExile2_test.exe";
    const std::wstring testConfigPath = tempDir + L"\\test_config_" + std::to_wstring(GetCurrentProcessId()) + L".toml";

    {
        std::ofstream mockFile(mockExePath, std::ios::binary);
        mockFile << "MOCK_EXE_PAYLOAD";
        mockFile.close();
    }

    // Ghi vào config.toml
    CHECK(common::SaveClientExeToConfig(testConfigPath, mockExePath), "SaveClientExeToConfig phai thanh cong");

    // Gọi FindOrPromptPoe2ClientExe -> Phải trúng Tier 1 ngay lập tức, không bật popup
    std::wstring detected = common::FindOrPromptPoe2ClientExe(testConfigPath);
    CHECK(detected == mockExePath, "FindOrPromptPoe2ClientExe phai tra ve dung mock exe tu config.toml (Tier 1)");

    std::wstring resolvedCfg = common::ResolveConfigPath();
    CHECK(!resolvedCfg.empty() && std::filesystem::exists(resolvedCfg), "ResolveConfigPath phai tim thay config.toml");

    // Dọn dẹp file tạm
    _wremove(testConfigPath.c_str());
    _wremove(mockExePath.c_str());

    std::cout << "  -> 4-Tier Exe Detection & IPC Protocol OK" << std::endl;
}

// ==========================================================
// 5. Kiểm thử World-To-Screen 4x4 Math Engine & Frustum Projection
// ==========================================================
void TestWorldToScreenMath() {
    std::cout << "[Test 5] World-To-Screen 4x4 Math Engine & Projection (v1.2)..." << std::endl;

    // Direct3D style standard perspective matrix
    // FOV = 60 deg, Aspect = 16/9, Near = 1.0, Far = 1000.0
    // Simple projection test with known coordinates
    visual::Matrix4x4 identity;
    visual::Vector3 centerPoint(0.0f, 0.0f, 10.0f);
    visual::Vector2 screenPos;

    // Row-major test: point at (0, 0, 10) with identity viewProj
    // clip: X=0, Y=0, Z=10, W=10
    // NDC: X=0, Y=0 -> Screen: (1920/2, 1080/2) = (960, 540)
    identity.m[0] = 1.0f;
    identity.m[5] = 1.0f;
    identity.m[10] = 1.0f;
    identity.m[11] = 1.0f; // W receives Z
    identity.m[15] = 0.0f;

    bool projected = visual::WorldToScreen::Project(identity, centerPoint, 1920, 1080, screenPos);
    CHECK(projected, "Center point project phai thanh cong");
    CHECK(std::fabs(screenPos.x - 960.0f) < 0.1f && std::fabs(screenPos.y - 540.0f) < 0.1f,
          "Center point phai nam chinh giua man hinh (960, 540)");

    // Point behind camera test (Z <= 0 -> W <= 0)
    visual::Vector3 behindPoint(0.0f, 0.0f, -5.0f);
    visual::Vector2 behindScreen;
    bool behindProjected = visual::WorldToScreen::Project(identity, behindPoint, 1920, 1080, behindScreen);
    CHECK(!behindProjected, "Point behind camera phai bi tu choi (W <= 0)");

    // Screen bounds check
    CHECK(visual::WorldToScreen::IsOnScreen(screenPos, 1920, 1080), "screenPos phai o trong man hinh");
    CHECK(!visual::WorldToScreen::IsOnScreen(visual::Vector2(-50.0f, 500.0f), 1920, 1080), "Outside X phai tra ve false");
    CHECK(!visual::WorldToScreen::IsOnScreen(visual::Vector2(500.0f, 1200.0f), 1920, 1080), "Outside Y phai tra ve false");

    // Minimap radar offset projection test
    visual::Vector3 player(100.0f, 200.0f, 0.0f);
    visual::Vector3 boss(150.0f, 200.0f, 0.0f); // 50 units East
    visual::Vector2 radarOffset = visual::WorldToScreen::ProjectToMinimapRadar(player, boss, 0.0f, 1.0f, 150.0f);
    CHECK(std::fabs(radarOffset.x - 50.0f) < 0.1f && std::fabs(radarOffset.y - 0.0f) < 0.1f,
          "Minimap radar offset phai la (50, 0)");

    std::cout << "  -> World-To-Screen Math OK" << std::endl;
}

// ==========================================================
// 6. Kiểm thử POI Radar Scanner & Shared State Seqlock
// ==========================================================
void TestPoiRadarAndSharedState() {
    std::cout << "[Test 6] POI Radar Scanner, Filter & Shared State Seqlock (v1.2)..." << std::endl;

    SimulatedMemoryReader sim;
    CHECK(sim.Initialize(4 * 1024 * 1024) && sim.Attach(game_layout::kSimulatedPid), "sim initialize cho POI Radar");

    visual::PoiRadar radar(sim);
    radar.SetScreenDimensions(1920, 1080);
    radar.SetPlayerPosition(visual::Vector3(0.0f, 0.0f, 0.0f), 0.0f);

    visual::Matrix4x4 viewProj;
    viewProj.m[0] = 1.0f; viewProj.m[5] = 1.0f; viewProj.m[10] = 1.0f; viewProj.m[11] = 1.0f; viewProj.m[15] = 0.0f;
    radar.SetCameraMatrix(viewProj);

    // Thêm các thực thể: Boss, Strongbox, Shrine, Quái quá xa (>250)
    bool bossAdded = radar.AddEntity(101, PoiType::Boss, visual::Vector3(10.0f, 10.0f, 50.0f), 0.85f, "Hillock");
    bool boxAdded = radar.AddEntity(102, PoiType::Strongbox, visual::Vector3(-20.0f, 30.0f, 40.0f), 1.0f, "Cartographer's Strongbox");
    bool shrineAdded = radar.AddEntity(103, PoiType::Shrine, visual::Vector3(5.0f, -15.0f, 30.0f), 1.0f, "Diamond Shrine");
    bool farMobAdded = radar.AddEntity(104, PoiType::RareMonster, visual::Vector3(500.0f, 500.0f, 50.0f), 1.0f, "Far Rare Mob");

    CHECK(bossAdded && boxAdded && shrineAdded, "Them Boss, Strongbox, Shrine hop le phai thanh cong");
    CHECK(!farMobAdded, "Quai o khoang cach 707 units > 250 units phai bi loc loai bo");
    CHECK(radar.EntityCount() == 3, "EntityCount phai bang 3");

    // Xuất ra Shared State IPC
    PoiRadarSharedState sharedState{};
    radar.ExportToSharedState(sharedState);

    CHECK(sharedState.magic == kVisualToolPoiMagic, "Shared state magic phai la 'POI1'");
    CHECK(sharedState.count == 3, "Shared state count phai la 3");
    CHECK(sharedState.seqlock % 2 == 0, "Seqlock phai la so chan sau khi hoan tat ghi");

    // Kiểm tra Safe Reader
    PoiRadarSharedState safeCopy{};
    bool readSuccess = visual::PoiRadar::ReadSharedStateSafely(sharedState, safeCopy);
    CHECK(readSuccess && safeCopy.count == 3, "ReadSharedStateSafely phai tra ve du lieu toan ven");
    CHECK(std::string(safeCopy.entities[0].name) == "Hillock", "Entity 0 phai la Hillock");
    CHECK(safeCopy.entities[0].type == static_cast<uint32_t>(PoiType::Boss), "Entity 0 type phai la Boss");

    std::cout << "  -> POI Radar Scanner & Shared State Seqlock OK" << std::endl;
}

// ==========================================================
// 7. Kiểm thử Mod Colorizer & 1-Frame Panic Restore
// ==========================================================
void TestModColorizerAndPanicRestore() {
    std::cout << "[Test 7] Mod Colorizer Classification & RAM Panic Restore (v1.2)..." << std::endl;

    SimulatedMemoryReader sim;
    CHECK(sim.Initialize(1024 * 1024) && sim.Attach(game_layout::kSimulatedPid), "sim initialize cho ModColorizer");

    // 1. Phân loại Mod theo nội dung chuỗi (Classification)
    auto catPhys = visual::ModColorizer::ClassifyMod("Adds 45 to 85 Physical Damage to Attacks");
    auto catSpell = visual::ModColorizer::ClassifyMod("75% increased Spell Damage");
    auto catResist = visual::ModColorizer::ClassifyMod("+42% to Fire Resistance");
    auto catT1 = visual::ModColorizer::ClassifyMod("+1 to Level of all Spell Skill Gems (Tier 1)");
    auto catFractured = visual::ModColorizer::ClassifyMod("Fractured Mod: +115 to Maximum Life");

    CHECK(catPhys == visual::ModCategory::PhysicalAttack, "Phys mod phai thuoc PhysicalAttack");
    CHECK(catSpell == visual::ModCategory::SpellElemental, "Spell mod phai thuoc SpellElemental");
    CHECK(catResist == visual::ModCategory::DefenseResistSpeed, "Resist mod phai thuoc DefenseResistSpeed");
    CHECK(catT1 == visual::ModCategory::Tier1Rare, "Tier 1 mod phai thuoc Tier1Rare");
    CHECK(catFractured == visual::ModCategory::Tier1Rare, "Fractured mod phai thuoc Tier1Rare");

    // 2. Định dạng BBCode hiển thị
    std::string formatted = visual::ModColorizer::FormatColoredBBCode("Physical Damage", catPhys);
    CHECK(formatted.find("[color=#FF4D4D]") != std::string::npos, "BBCode phai chua ma mau do #FF4D4D");

    // 3. RAM Color Table Patching & 1-Frame Panic Restore (< 8.33ms)
    visual::ModColorizer colorizer(sim);
    const uintptr_t fontTableAddr = sim.BaseAddress() + 0x50000;
    const uint8_t origTable[16] = {
        0xAA, 0xAA, 0xAA, 0xFF,
        0xBB, 0xBB, 0xBB, 0xFF,
        0xCC, 0xCC, 0xCC, 0xFF,
        0xDD, 0xDD, 0xDD, 0xFF
    };
    sim.Write(fontTableAddr, origTable, sizeof(origTable));

    CHECK(colorizer.SetFontColorTableAddress(fontTableAddr, sizeof(origTable)), "SetFontColorTableAddress thanh cong");
    CHECK(colorizer.ApplyColorPatches() && colorizer.IsPatched(), "ApplyColorPatches phai thanh cong");

    uint8_t patchedTable[16] = {};
    sim.Read(fontTableAddr, patchedTable, sizeof(patchedTable));
    CHECK(patchedTable[0] == 255 && patchedTable[1] == 77 && patchedTable[2] == 77, "Slot 0 phai duoc va thanh mau do (255, 77, 77)");

    // Panic restore
    auto start = std::chrono::high_resolution_clock::now();
    colorizer.PanicRestoreAll();
    auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(
        std::chrono::high_resolution_clock::now() - start).count();

    uint8_t restoredTable[16] = {};
    sim.Read(fontTableAddr, restoredTable, sizeof(restoredTable));
    CHECK(!colorizer.IsPatched() && std::memcmp(restoredTable, origTable, sizeof(origTable)) == 0,
          "PanicRestoreAll phai tra ve byte ban dau");
    CHECK(elapsed < 8330, "PanicRestoreAll phai thuc thi duoi 8.33ms (1 khung hinh 120Hz)");

    std::cout << "  -> Mod Colorizer & Panic Restore (" << elapsed << " us) OK" << std::endl;
}

// ==========================================================
// 8. Kiểm thử Particle & Weather Reducer (FPS Booster)
// ==========================================================
void TestParticleReducerAndPanicRestore() {
    std::cout << "[Test 8] Particle & Weather Reducer & Panic Restore (v1.2)..." << std::endl;

    SimulatedMemoryReader sim;
    CHECK(sim.Initialize(1024 * 1024) && sim.Attach(game_layout::kSimulatedPid), "sim initialize cho ParticleReducer");

    const uintptr_t weatherAddr = sim.BaseAddress() + 0x60000;
    const uintptr_t shakeAddr = sim.BaseAddress() + 0x60010;
    const uintptr_t bloomAddr = sim.BaseAddress() + 0x60020;

    sim.WriteValue<float>(weatherAddr, 1.0f);
    sim.WriteValue<uint8_t>(shakeAddr, 1);
    sim.WriteValue<float>(bloomAddr, 1.0f);

    visual::ParticleReducer reducer(sim);
    reducer.SetAddresses(weatherAddr, shakeAddr, bloomAddr);

    visual::ReducerConfig cfg;
    cfg.disableWeather = true;
    cfg.disableScreenShake = true;
    cfg.reduceBloom = true;
    cfg.targetBloomScale = 0.15f;

    CHECK(reducer.ApplyOptimizations(cfg) && reducer.IsActive(), "ApplyOptimizations phai thanh cong");

    float curWeather = 1.0f, curBloom = 1.0f;
    uint8_t curShake = 1;
    sim.ReadValue<float>(weatherAddr, curWeather);
    sim.ReadValue<uint8_t>(shakeAddr, curShake);
    sim.ReadValue<float>(bloomAddr, curBloom);

    CHECK(curWeather == 0.0f, "Weather density phai giam ve 0.0f de tat bao tuyet/bao cat");
    CHECK(curShake == 0, "Screen shake phai tat ve 0");
    CHECK(std::fabs(curBloom - 0.15f) < 0.001f, "Bloom scale phai giam ve 0.15f");

    // Panic restore
    auto start = std::chrono::high_resolution_clock::now();
    reducer.PanicRestoreAll();
    auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(
        std::chrono::high_resolution_clock::now() - start).count();

    sim.ReadValue<float>(weatherAddr, curWeather);
    sim.ReadValue<uint8_t>(shakeAddr, curShake);
    sim.ReadValue<float>(bloomAddr, curBloom);

    CHECK(!reducer.IsActive() && curWeather == 1.0f && curShake == 1 && curBloom == 1.0f,
          "PanicRestoreAll phai khoi phuc 100% thiet lap do hoa goc");
    CHECK(elapsed < 8330, "PanicRestoreAll phai thuc thi duoi 8.33ms");

    std::cout << "  -> Particle Reducer & FPS Booster (" << elapsed << " us) OK" << std::endl;
}

// ==========================================================
// 9. Kiểm thử Native C++23 Item Inspector & Waystone Rules (v2.0)
// ==========================================================
void TestNativeItemInspectorAndWaystoneRules() {
    std::cout << "[Test 9] Native C++23 Item Inspector & Waystone Rules (v2.0)..." << std::endl;

    // Test 9.1: Waystone Brick Mod (Reflect Elemental & Cannot Regenerate)
    const char* kWaystoneBrick =
        "Item Class: Waystones\n"
        "Rarity: Rare\n"
        "Bramble Valley Waystone (Tier 14)\n"
        "--------\n"
        "Waystone Tier: 14\n"
        "--------\n"
        "Item Level: 82\n"
        "--------\n"
        "Monsters reflect 18% of Elemental Damage\n"
        "Players cannot Regenerate Life, Mana or Energy Shield\n"
        "35% increased Monster Pack Size\n";

    auto rep1 = inspector::ItemParser::Parse(kWaystoneBrick);
    CHECK(rep1.category == inspector::ItemCategory::Waystone, "rep1 phai la Waystone");
    CHECK(rep1.rarity == inspector::ItemRarity::Rare, "rep1 phai la Rare");
    CHECK(rep1.isBrick == true, "rep1 co Reflect va No Regen phai danh dau isBrick == true");
    CHECK(rep1.safetyScore <= 10, "rep1 safety score phai rat thap do 2 mod brick");
    CHECK(rep1.redMods.size() == 2, "rep1 phai co dung 2 red mods");
    CHECK(rep1.greenMods.size() == 1, "rep1 phai co 1 green mod (Pack Size)");

    // Test 9.2: High Quantity & Pack Size Reward Waystone
    const char* kWaystoneReward =
        "Item Class: Waystones\n"
        "Rarity: Rare\n"
        "Citadel Waystone (Tier 16)\n"
        "--------\n"
        "Waystone Tier: 16\n"
        "--------\n"
        "Item Level: 84\n"
        "--------\n"
        "42% increased Quantity of Items found\n"
        "28% increased Monster Pack Size\n"
        "Monsters have 25% increased Area of Effect\n";

    auto rep2 = inspector::ItemParser::Parse(kWaystoneReward);
    CHECK(rep2.category == inspector::ItemCategory::Waystone, "rep2 phai la Waystone");
    CHECK(!rep2.isBrick, "rep2 khong phai la map brick");
    CHECK(rep2.greenMods.size() == 2, "rep2 phai co 2 reward mods (Quantity & Pack Size)");
    CHECK(rep2.qualityScore >= 50, "rep2 quality score phai >= 50");

    // Test 9.3: Rare Body Armour Open Affixes Calculation
    const char* kBodyArmour =
        "Item Class: Body Armours\n"
        "Rarity: Rare\n"
        "Dread Carapace\n"
        "Conquest Lamellar\n"
        "--------\n"
        "Quality: +20%\n"
        "Armour: 850\n"
        "Energy Shield: 240\n"
        "--------\n"
        "Requirements:\n"
        "Level: 68\n"
        "Str: 108\n"
        "Int: 108\n"
        "--------\n"
        "Sockets: S S S\n"
        "--------\n"
        "Item Level: 83\n"
        "--------\n"
        "+115 to maximum Life\n"
        "+44% to Fire Resistance\n";

    auto rep3 = inspector::ItemParser::Parse(kBodyArmour);
    CHECK(rep3.category == inspector::ItemCategory::BodyArmour, "rep3 phai la BodyArmour");
    CHECK(rep3.quality == 20, "rep3 quality phai la 20%");
    CHECK(rep3.itemLevel == 83, "rep3 item level phai la 83");
    CHECK(rep3.sockets == "S S S", "rep3 sockets phai la 'S S S'");
    CHECK(rep3.maxPrefixes == 3 && rep3.maxSuffixes == 3, "Rare body armour max 3P / 3S");
    // +115 Life is Prefix (1 detected), +44% Fire Res is Suffix (1 detected)
    // Open prefixes = 3 - 1 = 2, Open suffixes = 3 - 1 = 2
    CHECK(rep3.openPrefixes == 2, "rep3 openPrefixes phai la 2 (3 - 1)");
    CHECK(rep3.openSuffixes == 2, "rep3 openSuffixes phai la 2 (3 - 1)");

    // Test 9.4: Magic Item Open Affixes
    const char* kMagicRing =
        "Item Class: Rings\n"
        "Rarity: Magic\n"
        "Virile Gold Ring of the Magma\n"
        "--------\n"
        "Requirements:\n"
        "Level: 45\n"
        "--------\n"
        "Item Level: 70\n"
        "--------\n"
        "+75 to maximum Life\n";

    auto rep4 = inspector::ItemParser::Parse(kMagicRing);
    CHECK(rep4.category == inspector::ItemCategory::Ring, "rep4 phai la Ring");
    CHECK(rep4.rarity == inspector::ItemRarity::Magic, "rep4 phai la Magic");
    CHECK(rep4.maxPrefixes == 1 && rep4.maxSuffixes == 1, "Magic item max 1P / 1S");
    CHECK(rep4.openPrefixes == 0, "Magic ring voi Life da het slot Prefix (1/1 -> 0 open)");
    CHECK(rep4.openSuffixes == 1, "Magic ring con 1 open Suffix");

    std::cout << "  -> Native C++23 Item Inspector & Waystone Rules OK" << std::endl;
}

// ==========================================================
// 10. Kiểm thử Security & Platform Services C++23 Native (v2.0)
// ==========================================================
void TestSecurityAndPlatformServices() {
    std::cout << "[Test 10] Security & Platform Services C++23 Native (v2.0)..." << std::endl;
    using namespace poe2;

    // 10.1: Deterministic HWID Generation
    std::string hwid1 = security::GetMachineHWID();
    std::string hwid2 = security::GetMachineHWID();
    CHECK(hwid1.starts_with("PVT-") && hwid1.size() == 23, "HWID phai bat dau bang PVT- va co do dai 23 ky tu");
    CHECK(hwid1 == hwid2, "HWID phai mang tinh deterministic tuyet doi tren cung 1 may");

    auto details = security::GetHardwareDetails();
    CHECK(!details.cpuId.empty() || !details.machineGuid.empty(), "Hardware details phai thu thap duoc it nhat CPU hoac MachineGuid");

    // 10.2: Cryptographic Licensing & HMAC-SHA256
    // Lifetime Key
    std::string keyLifetime = security::GenerateProductKey(hwid1, security::LicenseTier::LIFETIME);
    CHECK(keyLifetime.starts_with("PVTK-") && keyLifetime.size() == 46, "Key phai co dang PVTK-XXXXX-... do dai 46");
    auto verLifetime = security::VerifyProductKey(keyLifetime, hwid1);
    CHECK(verLifetime.isValid, "Lifetime key phai hop le tren may dich");
    CHECK(verLifetime.tier == security::LicenseTier::LIFETIME, "Tier phai la LIFETIME");
    CHECK(verLifetime.daysLeft == 99999, "Lifetime daysLeft phai la 99999");

    // Subscription Key (30 days)
    std::string keySub = security::GenerateProductKey(hwid1, security::LicenseTier::SUBSCRIPTION, 30);
    auto verSub = security::VerifyProductKey(keySub, hwid1);
    CHECK(verSub.isValid, "30-Day Subscription key phai hop le");
    CHECK(verSub.tier == security::LicenseTier::SUBSCRIPTION, "Tier phai la SUBSCRIPTION");
    CHECK(verSub.daysLeft >= 29, "Subscription daysLeft phai >= 29");

    // Machine Mismatch Key
    auto verMismatch = security::VerifyProductKey(keyLifetime, "PVT-1111-2222-3333-4444");
    CHECK(!verMismatch.isValid, "Key dung tren may khac phai bi tu choi");

    // Anti-Crack: Zero Wildcard Backdoor (Key with foreign HWID must be rejected even if FFFFFFFFFFFFFFFF)
    std::string keyWildcard = security::GenerateProductKey("FFFFFFFFFFFFFFFF", security::LicenseTier::LIFETIME);
    auto verWild = security::VerifyProductKey(keyWildcard, hwid1);
    CHECK(!verWild.isValid, "Key voi HWID FFFFFFFFFFFFFFFF phai bi tu choi tren may khac (Zero Backdoor)");

    // Tampered Key Signature
    std::string tamperedKey = keyLifetime;
    tamperedKey.back() = (tamperedKey.back() == 'A') ? 'B' : 'A';
    auto verTampered = security::VerifyProductKey(tamperedKey, hwid1);
    CHECK(!verTampered.isValid, "Tampered key phai bi tu choi do sai HMAC-SHA256 signature");

    // 10.3: Cloud Sync Version Parsing
    const char* tomlMock =
        "# Config Offsets\n"
        "version = \"2.0.1\"\n"
        "timestamp = 1773400000\n"
        "[offsets]\n"
        "Camera = 0x16D5FC\n";
    std::string verExtracted = platform::CloudOffsetsSync::ExtractVersionOrTimestamp(tomlMock);
    CHECK(verExtracted == "2.0.1", "CloudOffsetsSync::ExtractVersionOrTimestamp phai bóc tách dung '2.0.1'");

    // 10.4: Bilingual Localization (i18n)
    auto& i18n = platform::I18n::Instance();
    i18n.SetLanguage(platform::Language::Vietnamese);
    CHECK(i18n.Translate("mod_brick") == "NGUY HIEM CHET NGUOI (BRICK)", "i18n Tieng Viet phai chinh xac");
    i18n.SetLanguage(platform::Language::English);
    CHECK(i18n.Translate("mod_brick") == "DEADLY BRICK MOD", "i18n English phai chinh xac");
    // Fallback key
    CHECK(i18n.Translate("non_existing_key_xyz") == "non_existing_key_xyz", "i18n key khong ton tai phai fallback ve chinh key");

    // 10.5: Haptic Audio Feedback
    auto& sound = platform::SoundEffects::Instance();
    sound.SetEnabled(false); // Disable beep during automated testing
    CHECK(!sound.IsEnabled(), "SoundEffects phai co the toggle tat");
    sound.PlayAttachSuccess();
    sound.PlayPanicRestore();
    sound.PlayBrickAlert();
    sound.PlayToggle(true);
    sound.SetEnabled(true);
    CHECK(sound.IsEnabled(), "SoundEffects phai co the toggle bat lai");

    std::cout << "  -> Security & Platform Services C++23 Native OK" << std::endl;
}

// ==========================================================
// 11. Kiểm thử In-Game HUD & Native UI Components (v2.0)
// ==========================================================
void TestNativeUiAndOverlayLayout() {
    std::cout << "[Test 11] In-Game HUD & Native UI Components (v2.0)..." << std::endl;
    using namespace poe2;

    // 11.1: Tooltip Layout & Brick Alert
    inspector::ItemInspectionReport repBrick;
    repBrick.category = inspector::ItemCategory::Waystone;
    repBrick.rarity = inspector::ItemRarity::Rare;
    repBrick.itemName = "Bramble Valley Waystone";
    repBrick.isBrick = true;
    repBrick.redMods.push_back("Monsters reflect 18% of Elemental Damage");

    auto toolLayout = ui::TooltipView::BuildLayout(repBrick, 500, 300, 1920, 1080);
    CHECK(toolLayout.hasBrickAlert, "Tooltip layout phai danh dau hasBrickAlert == true");
    CHECK(toolLayout.borderColor.r == ui::colors::BrickRed.r, "Border phai mau do Brick");
    CHECK(!toolLayout.lines.empty() && toolLayout.lines.front().text.contains("CANH BAO"), "Dong dau phai la banner canh bao");
    CHECK(toolLayout.bounds.x >= 500, "Tooltip phai hien thi lech ve ben phai con tro chuot");

    // Clamping anti-overflow test
    auto toolClamp = ui::TooltipView::BuildLayout(repBrick, 1900, 1050, 1920, 1080);
    CHECK(toolClamp.bounds.x + toolClamp.bounds.width <= 1920, "Tooltip khong duoc tran ra ngoai man hinh ben phai");
    CHECK(toolClamp.bounds.y + toolClamp.bounds.height <= 1080, "Tooltip khong duoc tran xuong duoi man hinh");

    // 11.2: Radar Compass Layout & POI Categorization
    std::vector<PoiSharedEntity> testPois;
    PoiSharedEntity p1 = {};
    p1.type = static_cast<uint32_t>(PoiType::Boss);
    p1.world_x = 100.0f; p1.world_z = 150.0f;
    strncpy_s(p1.name, "Kraglin, the Unbroken", sizeof(p1.name) - 1);
    testPois.push_back(p1);

    PoiSharedEntity p2 = {};
    p2.type = static_cast<uint32_t>(PoiType::Strongbox);
    p2.world_x = 50.0f; p2.world_z = -80.0f;
    strncpy_s(p2.name, "Arcanist's Strongbox", sizeof(p2.name) - 1);
    testPois.push_back(p2);

    PoiSharedEntity p3 = {};
    p3.type = static_cast<uint32_t>(PoiType::Shrine);
    p3.world_x = -120.0f; p3.world_z = 60.0f;
    strncpy_s(p3.name, "Brutal Shrine", sizeof(p3.name) - 1);
    testPois.push_back(p3);

    auto radarLayout = ui::RadarHud::ComputeLayout(testPois, 0.0f, 0.0f, 0.0f, 1920, 1080, 100, 1500.0f);
    CHECK(radarLayout.bossCount == 1, "Radar layout phai phan loai dung 1 Boss");
    CHECK(radarLayout.strongboxCount == 1, "Radar layout phai phan loai dung 1 Strongbox");
    CHECK(radarLayout.shrineCount == 1, "Radar layout phai phan loai dung 1 Shrine");
    CHECK(radarLayout.markers.size() == 3, "Radar phai chua dung 3 markers");

    // 11.3: Settings Panel Layout & License Status
    ui::SettingsPanelState state;
    state.zoomEnabled = true;
    state.fogDisabled = true;
    state.licenseInfo.isValid = true;
    state.licenseInfo.tier = security::LicenseTier::LIFETIME;

    auto panelLayout = ui::SettingsPanel::BuildLayout(state, 1920, 1080);
    CHECK(panelLayout.toggles.size() == 8, "Settings panel phai co 8 toggles tinh nang");
    CHECK(panelLayout.panicButtonBox.width > 0 && panelLayout.panicButtonBox.height > 0, "Panic button hitbox phai hop le");
    CHECK(panelLayout.statusColor.r == ui::colors::GreenSuccess.r, "Status color cho Lifetime phai la mau xanh success");

    // 11.4: Overlay Window & Streamer Mode Protection
    ui::OverlayWindow overlay;
    overlay.SetStreamerMode(true);
    CHECK(overlay.IsStreamerMode(), "OverlayWindow streamer mode phai bat");
    overlay.SetStreamerMode(false);
    CHECK(!overlay.IsStreamerMode(), "OverlayWindow streamer mode phai tat");

    // 11.5: System Tray Lifecycle & Move Semantics (Commercial Silent Mode)
    ui::SystemTray tray;
    CHECK(!tray.IsCreated(), "SystemTray mac dinh phai chua duoc created");
    ui::SystemTray movedTray = std::move(tray);
    CHECK(!movedTray.IsCreated(), "Moved SystemTray phai o trang thai hop le");
    movedTray.Destroy();
    CHECK(!movedTray.IsCreated(), "Destroy SystemTray phai an toan va dat created = false");

    std::cout << "  -> In-Game HUD & Native UI Components OK" << std::endl;
}

} // namespace

int main() {
    std::cout << "==========================================================" << std::endl;
    std::cout << "  Poe2VisualTool C++ Core Test Suite (13/09/2026)" << std::endl;
    std::cout << "==========================================================" << std::endl;

    TestVisualModsEngine();
    TestPoe2RealCameraZoomClampAndMatrix();
    TestPoe2MinimapShaderPatchesAndAOB();
    TestFindOrPromptPoe2ClientExeAndConfig();
    TestWorldToScreenMath();
    TestPoiRadarAndSharedState();
    TestModColorizerAndPanicRestore();
    TestParticleReducerAndPanicRestore();
    TestNativeItemInspectorAndWaystoneRules();
    TestSecurityAndPlatformServices();
    TestNativeUiAndOverlayLayout();

    std::cout << "==========================================================" << std::endl;
    std::cout << "  Summary: " << g_checks << " checks run, " << g_failures << " failures." << std::endl;
    std::cout << "==========================================================" << std::endl;

    return (g_failures == 0) ? 0 : 1;
}

