// Standalone tests for reasampler::prune_button — no REAPER, no test framework.
// Same fast loop as the sibling pure tests (mode_switch / tab_strip): assert the
// footer prune-button placement math and hit-testing directly.
//
// Covers (R3 brief §button pure module): right-anchored layout in a wide footer;
// vertical inset; SUPPRESSION (empty rect) when the footer is too narrow to clear the
// tail-label inset or is degenerate; hit-test in/out/edge (half-open bounds); a
// suppressed/empty button claims no point; draw and hit-test agree over the whole rect.

#include "../src/core/ui/prune_button.h"

#include <cstdio>

using namespace reasampler;
using namespace reasampler::ui;

static int g_fail = 0;
#define CHECK(cond) do { if(!(cond)) { \
    std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0)

// --- Layout: wide footer, right-anchored -------------------------------------

// Footer 400 wide at origin (0, 100), height 26. Default spec: buttonWidth 72,
// rightInset 84, verticalInset 4, minLeftInset 120. Right edge = 0+400-84 = 316,
// left = 316-72 = 244 (>= 0+120, so placed). Top = 100+4 = 104, height = 26-8 = 18.
static void testWideFooterRightAnchored() {
    FooterRect f{0, 100, 400, 26};
    const ButtonRect b = computePruneButton(f, PruneButtonSpec{});
    CHECK(!b.empty());
    CHECK((b == ButtonRect{244, 104, 72, 18}));
    // Right edge sits at the rightInset from the footer's right.
    CHECK(b.x + b.width == f.x + f.width - 84);
    // Left edge clears the reserved tail-label inset.
    CHECK(b.x >= f.x + 120);
}

// Origin offset is honoured (button anchors to THIS footer's right, not 0).
static void testOffsetFooterAnchors() {
    FooterRect f{10, 200, 400, 26};
    const ButtonRect b = computePruneButton(f, PruneButtonSpec{});
    CHECK(!b.empty());
    CHECK(b.x + b.width == f.x + f.width - 84);  // = 10+400-84 = 326
    CHECK(b.x == 254);
}

// --- Suppression: too narrow / degenerate ------------------------------------

// A footer just wide enough that the button's left edge would fall past the
// minLeftInset is suppressed (empty). left = x + width - rightInset - buttonWidth.
// Need left < x + minLeftInset -> width < rightInset + buttonWidth + minLeftInset
// = 84 + 72 + 120 = 276. Width 275 suppresses; 276 places (boundary).
static void testNarrowFooterSuppressed() {
    CHECK(computePruneButton(FooterRect{0, 0, 275, 26}, PruneButtonSpec{}).empty());
    CHECK(!computePruneButton(FooterRect{0, 0, 276, 26}, PruneButtonSpec{}).empty());
}

static void testDegenerateFooterSuppressed() {
    CHECK(computePruneButton(FooterRect{0, 0, 0, 26}, PruneButtonSpec{}).empty());   // no width
    CHECK(computePruneButton(FooterRect{0, 0, 400, 0}, PruneButtonSpec{}).empty());  // no height
    PruneButtonSpec zero{}; zero.buttonWidth = 0;
    CHECK(computePruneButton(FooterRect{0, 0, 400, 26}, zero).empty());              // zero button
}

// A very thin footer (height <= 2*verticalInset) still places a button but clamps its
// height to the footer's own, rather than yielding a negative height.
static void testThinFooterClampsHeight() {
    FooterRect f{0, 0, 400, 6};  // 6 <= 2*4, so height would be negative -> clamp
    const ButtonRect b = computePruneButton(f, PruneButtonSpec{});
    CHECK(!b.empty());
    CHECK(b.y == f.y);
    CHECK(b.height == f.height);
}

// --- Hit-test ----------------------------------------------------------------

static void testHitTestInside() {
    FooterRect f{0, 100, 400, 26};
    const ButtonRect b = computePruneButton(f, PruneButtonSpec{});  // {244,104,72,18}
    CHECK(hitTestPruneButton(b.x, b.y, b));                          // top-left corner (inclusive)
    CHECK(hitTestPruneButton(b.x + b.width - 1, b.y + b.height - 1, b));  // bottom-right inclusive
    CHECK(hitTestPruneButton(b.x + b.width / 2, b.y + b.height / 2, b)); // centre
}

// Half-open bounds: the far edges (x+width, y+height) are EXCLUDED, matching the draw.
static void testHitTestEdgesExcluded() {
    FooterRect f{0, 100, 400, 26};
    const ButtonRect b = computePruneButton(f, PruneButtonSpec{});
    CHECK(!hitTestPruneButton(b.x - 1, b.y, b));               // just left
    CHECK(!hitTestPruneButton(b.x + b.width, b.y, b));         // right edge excluded
    CHECK(!hitTestPruneButton(b.x, b.y - 1, b));               // just above
    CHECK(!hitTestPruneButton(b.x, b.y + b.height, b));        // bottom edge excluded
}

// A suppressed (empty) button never claims a point — a click in the footer where the
// button would have been falls through to the tail cycle, never a phantom prune.
static void testEmptyButtonClaimsNothing() {
    ButtonRect empty{};
    CHECK(!hitTestPruneButton(0, 0, empty));
    CHECK(!hitTestPruneButton(5, 5, empty));
    // A "no button" from a narrow footer also claims nothing at any point.
    const ButtonRect suppressed = computePruneButton(FooterRect{0, 0, 200, 26}, PruneButtonSpec{});
    CHECK(suppressed.empty());
    CHECK(!hitTestPruneButton(150, 13, suppressed));
}

// Draw/hit-test agreement: every point inside the computed rect hit-tests true, and the
// four immediate outside neighbours hit-test false (the load-bearing consistency).
static void testHitTestMatchesLayout() {
    FooterRect f{3, 7, 377, 22};  // awkward origin/size
    const ButtonRect b = computePruneButton(f, PruneButtonSpec{});
    CHECK(!b.empty());
    for (int py = b.y; py < b.y + b.height; ++py)
        for (int px = b.x; px < b.x + b.width; ++px)
            CHECK(hitTestPruneButton(px, py, b));
    CHECK(!hitTestPruneButton(b.x - 1, b.y, b));
    CHECK(!hitTestPruneButton(b.x + b.width, b.y, b));
}

int main() {
    testWideFooterRightAnchored();
    testOffsetFooterAnchors();
    testNarrowFooterSuppressed();
    testDegenerateFooterSuppressed();
    testThinFooterClampsHeight();
    testHitTestInside();
    testHitTestEdgesExcluded();
    testEmptyButtonClaimsNothing();
    testHitTestMatchesLayout();

    if (g_fail == 0) std::printf("prune_button: all tests passed\n");
    else std::printf("prune_button: %d CHECK(s) FAILED\n", g_fail);
    return g_fail == 0 ? 0 : 1;
}
