// Standalone tests for reasampler::buildFolderTree — no REAPER, no framework.
// The D2 view shell is DAW-bound and only verifiable in REAPER; this covers the
// one genuinely pure piece: the I_FOLDERDEPTH walk that turns REAPER's linear
// track stream into the parent<->child FolderTree the pure model consumes.

#include "../src/core/view/view_tree.h"

#include <cstdio>
#include <string>

using namespace reasampler;
using namespace reasampler::view;

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

// Find a node by guid; returns nullptr if absent.
static const FolderNode* nodeOf(const FolderTree& t, const std::string& guid) {
    for (const auto& n : t.nodes)
        if (n.guid == guid) return &n;
    return nullptr;
}

// All top-level leaves: three depth-0 tracks, no folders. Every parentGuid empty,
// nothing is a parent, order preserved.
static void testFlatLeaves() {
    FolderTree t = buildFolderTree({{"a", 0}, {"b", 0}, {"c", 0}});
    CHECK(t.nodes.size() == 3);
    CHECK(t.nodes[0].guid == "a" && t.nodes[1].guid == "b" && t.nodes[2].guid == "c");
    for (const auto& n : t.nodes) {
        CHECK(n.parentGuid.empty());
        CHECK(!n.isParent);
    }
}

// One folder: parent P (depth 1) contains child C, which closes the folder (-1).
// P is a parent and is itself top-level; C's parent is P.
static void testSingleFolder() {
    FolderTree t = buildFolderTree({{"P", 1}, {"C", -1}});
    const FolderNode* p = nodeOf(t, "P");
    const FolderNode* c = nodeOf(t, "C");
    CHECK(p && c);
    CHECK(p->isParent);
    CHECK(p->parentGuid.empty());     // the folder parent is itself top-level
    CHECK(!c->isParent);
    CHECK(c->parentGuid == "P");      // child sits under the folder
}

// Folder with two children; a following top-level leaf must land back at the root,
// proving the close (-1) popped the folder off the stack.
static void testFolderThenSibling() {
    FolderTree t = buildFolderTree({{"P", 1}, {"C1", 0}, {"C2", -1}, {"S", 0}});
    CHECK(nodeOf(t, "C1")->parentGuid == "P");
    CHECK(nodeOf(t, "C2")->parentGuid == "P");
    CHECK(nodeOf(t, "S")->parentGuid.empty());  // popped back to root
}

// Nested folders: P > Q > leaf. The inner leaf closes BOTH folders at once (-2).
// A sibling after it returns to the root.
static void testNestedDeepClose() {
    FolderTree t = buildFolderTree({{"P", 1}, {"Q", 1}, {"L", -2}, {"S", 0}});
    CHECK(nodeOf(t, "P")->isParent && nodeOf(t, "P")->parentGuid.empty());
    CHECK(nodeOf(t, "Q")->isParent && nodeOf(t, "Q")->parentGuid == "P");
    CHECK(nodeOf(t, "L")->parentGuid == "Q");   // deepest leaf under Q
    CHECK(!nodeOf(t, "L")->isParent);
    CHECK(nodeOf(t, "S")->parentGuid.empty());  // both levels popped
}

// Nested with a stepwise close: inner folder closes (-1), then a sibling at the
// OUTER level, then that folder closes (-1). Proves partial pops track correctly.
static void testNestedStepwiseClose() {
    FolderTree t = buildFolderTree(
        {{"P", 1}, {"Q", 1}, {"L", -1}, {"M", -1}});
    CHECK(nodeOf(t, "Q")->parentGuid == "P");
    CHECK(nodeOf(t, "L")->parentGuid == "Q");   // L inside inner folder Q
    CHECK(nodeOf(t, "M")->parentGuid == "P");   // after Q closed, M is under P
}

// Malformed stream: a close deeper than the open stack must clamp to empty, never
// underflow. A following leaf is still top-level and the walk stays total.
static void testMalformedCloseClamps() {
    FolderTree t = buildFolderTree({{"P", 1}, {"C", -5}, {"S", 0}});
    CHECK(nodeOf(t, "C")->parentGuid == "P");
    CHECK(nodeOf(t, "S")->parentGuid.empty());  // clamped, not underflowed
    CHECK(t.nodes.size() == 3);
}

// Empty input yields an empty tree (no fault).
static void testEmpty() {
    FolderTree t = buildFolderTree({});
    CHECK(t.nodes.empty());
}

// An empty folder (parent immediately closes itself, depth accounting P=1 then a
// child that both belongs to and closes it). Here P opens and the sole child
// closes; verifies a folder with exactly one leaf child.
static void testSingleChildFolder() {
    FolderTree t = buildFolderTree({{"P", 1}, {"only", -1}});
    CHECK(nodeOf(t, "only")->parentGuid == "P");
    CHECK(nodeOf(t, "P")->isParent);
}

int main() {
    testFlatLeaves();
    testSingleFolder();
    testFolderThenSibling();
    testNestedDeepClose();
    testNestedStepwiseClose();
    testMalformedCloseClamps();
    testEmpty();
    testSingleChildFolder();

    if (g_fail == 0) {
        std::printf("view_tree: all tests passed\n");
        return 0;
    }
    std::printf("view_tree: %d checks FAILED\n", g_fail);
    return 1;
}
