<?php
session_start();

require_once __DIR__ . '/fl-config.php';
require_once __DIR__ . '/fl-path.php';

define('APP_NAME', 'selector-lite');
define('APP_VERSION', '2.1');
define('APP_UPDATE_DATE', '2026/05/24');

$root = realpath(FL_DATA_ROOT);
if ($root === false) {
    header('Content-Type: text/plain; charset=UTF-8');
    echo 'Cannot resolve data root.';
    exit;
}
$mountName = FL_MOUNT_NAME;
$embed = isset($_GET['embed']) && $_GET['embed'] === '1';

function mountToReal($path)
{
    global $root, $mountName;

    if ($path === $mountName || $path === '' || $path === '/') {
        return $root;
    }

    if (strpos($path, $mountName . '/') === 0) {
        $sub = substr($path, strlen($mountName) + 1);
        return fl_resolve_path($sub, $root);
    }

    return fl_resolve_path($path, $root);
}

// -----------------------------
// scan dir
// -----------------------------
function scanDirLite($path) {

    $items = @scandir($path);

    if (!$items) return [];

    $result = [];

    foreach ($items as $item) {

        if ($item === "." || $item === "..") continue;

        $full = $path . "/" . $item;

        $result[] = [
            "name"   => $item,
            "path"   => $full,
            "is_dir" => is_dir($full),
            "size"   => is_dir($full) ? -1 : filesize($full),
            "date"   => filemtime($full)
        ];
    }

    return $result;
}

// -----------------------------
// render tree
// -----------------------------
function renderTree(
    $path,
    $level = 0,
    $virtualBase = ""
) {

    global $root, $embed;

    $items = scanDirLite($path);

    usort($items, function($a, $b){

        if ($a["is_dir"] !== $b["is_dir"]) {
            return $b["is_dir"] <=> $a["is_dir"];
        }

        return strcasecmp($a["name"], $b["name"]);
    });

    foreach ($items as $item) {

        $name = htmlspecialchars($item["name"]);

$relative =
    $virtualBase
    ? $virtualBase . "/" . $item["name"]
    : $item["name"];

        $selected =
            isset($_SESSION["selected"][$relative]);

        $checked = $selected ? "checked" : "";

        $indent = $level * 22;

        $size =
            $item["is_dir"]
            ? "-"
            : number_format($item["size"]) . " bytes";

        $date =
            date("Y-m-d H:i", $item["date"]);

        // -------------------------
        // tree node start
        // -------------------------
        echo "
        <div class='tree-node'
             data-node='{$relative}'>
        ";

        // -------------------------
        // row
        // -------------------------
        echo "
        <div class='tree-row'
     data-path='{$relative}'
     data-level='{$level}'>

    <div class='left'
     style='padding-left:{$indent}px'>

            <input type='checkbox'
                   class='sel'
                   data-path='{$relative}'
                   {$checked}>
        ";

        // DIR
        if ($item["is_dir"]) {

            echo "
                <span class='toggle'
                      data-path='{$relative}'
                      data-open='0'>▶</span>

                <span class='icon folder-icon'>
                    📁
                </span>

                <span class='dirname'>
                    {$name}
                </span>
            ";

        } else {

            echo "
                <span class='toggle-space'></span>

                <span class='icon'>
                    📄
                </span>

                <a href='file-lister.php?"
                . ($embed ? "embed=1&" : "")
                . "path="
                . urlencode($relative)
                . "' class='filename'"
                . ($embed ? " target='lister'" : "")
                . ">
                    {$name}
                </a>
            ";
        }

echo "
        </div>

        <span class='meta'>
            [{$size}] [{$date}]
        </span>

    </div>
        ";

        // -------------------------
        // children container
        // -------------------------
        if ($item["is_dir"]) {

            echo "
            <div class='children'
                 data-parent='{$relative}'>
            </div>
            ";
        }

        // -------------------------
        // tree node end
        // -------------------------
        echo "</div>";
    }
}

// -----------------------------
// ajax expand
// -----------------------------
if (isset($_GET["ajax"])) {

    $relative = $_GET["path"] ?? "";

    $dir = mountToReal($relative);

    if (!$dir || !is_dir($dir)) {
        exit;
    }

    $level =
    intval($_GET["level"] ?? 1);

    renderTree(
    $dir,
    $level,
    $relative
);

    exit;
}

?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>selector-lite v2.0</title>

<style>

body{
    background:#111;
    color:#eee;
    font-family:sans-serif;
    margin:40px;
}

a{
    color:#4ea3ff;
    text-decoration:none;
}

a:hover{
    text-decoration:underline;
}

.box{
    background:#1a1a1a;
    border:1px solid #333;
    border-radius:8px;
    padding:20px;
}

.tree-row:hover{
    background:#1b1b1b;
}
.tree-row{
    display:flex;
    align-items:center;
    width:100%;
    line-height:30px;
}
.tree-node{
    display:block;
    width:100%;
}
.left{
    display:flex;
    align-items:center;
    gap:8px;

    min-width:0;

    flex:1;
}

.toggle{
    width:16px;
    cursor:pointer;
    user-select:none;
    color:#aaa;
    flex-shrink:0;
}

.toggle:hover{
    color:#fff;
}

.toggle-space{
    display:inline-block;
    width:16px;
    flex-shrink:0;
}


.dirname{
    cursor:pointer;
    color:#4ea3ff;
}

.dirname:hover{
    text-decoration:underline;
}

.filename{
    color:#ddd;
    overflow:hidden;
    text-overflow:ellipsis;
}

.meta{

    margin-left:auto;

    min-width:300px;

    text-align:right;

    color:#777;

    font-size:12px;

    white-space:nowrap;

    flex-shrink:0;
}

.icon{
    width:20px;
    flex-shrink:0;
}

.children{
    display:block;
    width:100%;
}

.selected .dirname,
.selected .filename{
    color:#4ea3ff;
    font-weight:bold;
}


.selected .dirname,
.selected .filename{
    color:#4ea3ff;
    font-weight:bold;
}

.toolbar{
    margin-bottom:15px;
}

button{
    padding:6px 12px;
    cursor:pointer;
}

.sel-count{
    margin-left:15px;
    color:#aaa;
    font-size:12px;
}
.breadcrumb{
    margin-bottom:15px;
    padding:10px;
    background:#222;
    border-radius:6px;
    font-size:14px;
}

.breadcrumb span{
    cursor:pointer;
    color:#4ea3ff;
}

.breadcrumb span:hover{
    text-decoration:underline;
}
.root-row{
    border-bottom:1px solid #333;
    margin-bottom:8px;
    padding-bottom:6px;
}
.root-row{
    display:none;
}

body.embed{
    margin:0;
    height:100%;
    overflow:auto;
    box-sizing:border-box;
}

body.embed h1,
body.embed > p{
    display:none;
}

body.embed .box{
    border:none;
    border-radius:0;
    padding:10px 12px;
    min-height:100%;
    box-sizing:border-box;
}

body.embed .meta{
    min-width:0;
    font-size:10px;
}

body.embed .toolbar{
    margin-bottom:10px;
}
</style>
</head>

<body class="<?= $embed ? 'embed' : '' ?>">

<?php if (!$embed): ?>
<h1><?= APP_NAME ?> v<?= APP_VERSION ?></h1>

<p>
Lazy Load Tree Explorer
</p>
<?php endif; ?>

<div class="toolbar">

    <button id="clearAll" type="button">
        Clear selection
    </button>

    <span id="selCount" class="sel-count">
        0 selected
    </span>

</div>
<div id="breadcrumb" class="breadcrumb">
    /
</div>
<div class="box">

    <!-- ROOT NODE -->
    <div class="tree-node root-node"
         data-node="root">

<div class="children root-children"
     data-parent="root">

    <div class="tree-node mount-node"
         data-node="<?= htmlspecialchars($mountName) ?>">

        <div class="tree-row"
             data-path="<?= htmlspecialchars($mountName) ?>"
             data-level="1">

            <div class="left">

                <span class="toggle"
                      data-path="<?= htmlspecialchars($mountName) ?>"
                      data-open="1">▼</span>

                <span class="icon folder-icon">
                    📁
                </span>

                <span class="dirname">
                    <?= htmlspecialchars($mountName) ?>
                </span>

            </div>

            <span class="meta">
                MOUNT
            </span>

        </div>

        <div class="children"
             data-parent="<?= htmlspecialchars($mountName) ?>">

            <?php renderTree(
    $root,
    2,
    $mountName
); ?>

        </div>

    </div>

</div>

    </div>

</div>

<script>
const CONFIG = {
    mountName:
        <?= json_encode($mountName) ?>,
    embed: <?= $embed ? 'true' : 'false' ?>
};
let currentPath = "";

function notifySelectionChanged() {
    if (window.parent !== window) {
        window.parent.postMessage({ type: 'fl-selection-changed' }, '*');
    }
}

function postSelection(path, select) {
    return fetch("file-lister.php", {
        method: "POST",
        headers: {
            "Content-Type": "application/x-www-form-urlencoded"
        },
        body:
            "path=" +
            encodeURIComponent(path) +
            "&select=" +
            select
    }).then(function() {
        notifySelectionChanged();
    });
}
function updateBreadcrumb(path){

    currentPath = path;

    const box =
        document.getElementById("breadcrumb");

    if (!path){

        box.innerHTML = "/";
        return;
    }

    const parts = path.split("/");

let html =
    `<span data-path="${CONFIG.mountName}">/</span>`;

let accum = CONFIG.mountName;

    parts.forEach(part => {

        accum += (accum ? "/" : "") + part;

        html += `
            →
            <span data-path="${accum}">
                ${part}
            </span>
        `;
    });

    box.innerHTML = html;
}
// -------------------------
// selection count
// -------------------------
function updateSelCount(){

    const count =
        document.querySelectorAll(".sel:checked").length;

    document.getElementById("selCount")
        .textContent = count + " selected";
}

// -------------------------
// checkbox
// -------------------------
document.addEventListener("change", (e) => {

    if (!e.target.classList.contains("sel")) return;

    const cb = e.target;

    const row = cb.closest(".tree-row");
	
    if (e.target.dataset.path === "root") {
    return;
    }
	
    if (cb.checked) {
        row.classList.add("selected");
    } else {
        row.classList.remove("selected");
    }

    updateSelCount();

    postSelection(
        cb.dataset.path,
        cb.checked ? 1 : 0
    );
});


document.addEventListener("click", async (e) => {

    // checkbox除外
    if (e.target.classList.contains("sel")) {
        return;
    }

    const row =
        e.target.closest(".tree-row");

    if (!row) return;

    const toggle =
        row.querySelector(".toggle");

    if (!toggle) return;

    const path =
        toggle.dataset.path;

    const node =
        toggle.closest(".tree-node");

    const child =
        node.querySelector(".children");

    const open =
        toggle.dataset.open === "1";

// ---------------------
// ROOT special
// ---------------------
if (path === "root") {

    updateBreadcrumb("");

    if (open) {

        child.style.display = "none";
        toggle.textContent = "▶";
        toggle.dataset.open = "0";
		updateBreadcrumb("");

    } else {

        child.style.display = "block";
        toggle.textContent = "▼";
        toggle.dataset.open = "1";
    }

    return;
}

    // ---------------------
    // CLOSE
    // ---------------------
    if (open) {

        const selectedInside =
            child.querySelector(".sel:checked");

        if (selectedInside) {

            alert(
                "Selected item exists inside this directory."
            );

            return;
        }

        child.innerHTML = "";
        toggle.textContent = "▶";
        toggle.dataset.open = "0";
        updateBreadcrumb("");
        return;
    }

    // ---------------------
    // OPEN
    // ---------------------
    updateBreadcrumb(path);

const currentLevel =
    parseInt(
        row.dataset.level || 0
    );

const res = await fetch(
    "?ajax=1"
    + "&path=" + encodeURIComponent(path)
    + "&level=" + (currentLevel + 1)
);

    const html = await res.text();

    child.innerHTML = html;

    toggle.textContent = "▼";

    toggle.dataset.open = "1";
});
// -------------------------
// clear all
// -------------------------
document.getElementById("clearAll")
.addEventListener("click", () => {

    document.querySelectorAll(".sel")
    .forEach(cb => {

        cb.checked = false;

        const row =
            cb.closest(".tree-row");

        row.classList.remove("selected");

        postSelection(cb.dataset.path, 0);
    });

    updateSelCount();
});

// init
updateSelCount();

</script>

</body>
</html>