<?php
// ------- CONFIG -------
$HIDE = [
  '.', '..', '.well-known', '.git', '.htaccess', '.htpasswd',
  '_autoindex', 'cgi-bin', 'index.php'
];
$TITLE = $_SERVER['HTTP_HOST'] . ' — Index';

// Helper: safe path
$baseDir = realpath(__DIR__);

// List directory items
function list_items($dir, $hide) {
  $out = [];
  $dh = @opendir($dir);
  if (!$dh) return $out;
  while (($entry = readdir($dh)) !== false) {
    if (in_array($entry, $hide)) continue;
    $full = $dir . DIRECTORY_SEPARATOR . $entry;
    if (!is_readable($full)) continue;

    $isDir = is_dir($full);
    $size = $isDir ? 0 : filesize($full);
    $mtime = filemtime($full);

    // quick one-level subfolder size summary
    if ($isDir) {
      $sum = 0; $n=0;
      if ($sdh = @opendir($full)) {
        while (($e = readdir($sdh)) !== false) {
          if ($e==='.'||$e==='..') continue;
          $p = $full.DIRECTORY_SEPARATOR.$e;
          if (is_file($p)) { $sum += filesize($p); $n++; }
        }
        closedir($sdh);
      }
      $size = $sum; // use sum for display (approx)
    }

    $out[] = [
      'name' => $entry,
      'href' => rawurlencode($entry) . ($isDir ? '/' : ''),
      'isDir' => $isDir,
      'size' => $size,
      'mtime' => $mtime,
      'ext' => $isDir ? '' : strtolower(pathinfo($entry, PATHINFO_EXTENSION)),
    ];
  }
  closedir($dh);
  // Sort default by name asc, dirs first
  usort($out, function($a,$b){
    if ($a['isDir'] !== $b['isDir']) return $a['isDir'] ? -1 : 1;
    return strnatcasecmp($a['name'], $b['name']);
  });
  return $out;
}

$items = list_items($baseDir, $HIDE);

// If JSON requested (?json=1) provide API output (optional)
if (isset($_GET['json'])) {
  header('Content-Type: application/json; charset=utf-8');
  echo json_encode($items);
  exit;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
<title><?=htmlspecialchars($TITLE)?></title>
<meta name="theme-color" content="#0b1020" media="(prefers-color-scheme: dark)">
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
<style>
  :root{
    --bg:#0b1020; --bg2:#0f1631; --text:#e9edf6; --muted:#a9b3c9; --card:#121830; --line:#223;
    --accent:#7dd3fc; --accent2:#86efac; --radius:14px; --shadow:0 10px 30px rgba(0,0,0,.25);
  }
  @media (prefers-color-scheme: light){
    :root{ --bg:#f7f8fb; --bg2:#ffffff; --text:#0f172a; --muted:#475569; --card:#ffffff; --line:#e2e8f0; --shadow:0 8px 20px rgba(2,6,23,.08);}
  }
  *{box-sizing:border-box}
  html,body{height:100%}
  body{
    margin:0; font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Inter,Arial,sans-serif;
    color:var(--text); background:linear-gradient(180deg,var(--bg),var(--bg) 60%,var(--bg2));
  }
  .wrap{max-width:1100px;margin:0 auto;padding:24px 16px 40px}
  header{display:flex;align-items:center;gap:12px;margin:4px 0 18px}
  .dot{width:10px;height:10px;border-radius:999px;background:radial-gradient(circle at 40% 40%,var(--accent),#3b82f6)}
  h1{margin:0;font-size:clamp(22px,3.2vw,30px);font-weight:800;letter-spacing:.2px}
  .sub{color:var(--muted);font-size:14px;margin:4px 0 0}
  .bar{display:flex;gap:10px;flex-wrap:wrap;margin:18px 0 14px}
  .input,.btn,select{background:var(--bg2);border:1px solid var(--line);color:var(--text);padding:10px 12px;border-radius:12px;outline:none}
  .input::placeholder{color:var(--muted)}
  .btn{cursor:pointer;font-weight:700}
  .btn.primary{background:linear-gradient(90deg,var(--accent),#60a5fa); color:#021019; border:0}
  .toggle{display:flex;gap:8px;align-items:center}
  .pill{font-size:12px;color:var(--muted)}
  .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:14px;margin-top:8px}
  .card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);display:flex;flex-direction:column;padding:14px;gap:10px;min-height:120px}
  .row{display:flex;gap:12px;align-items:flex-start}
  .ico{width:38px;height:38px;border-radius:12px;display:grid;place-items:center;background:linear-gradient(180deg,rgba(125,211,252,.15),rgba(134,239,172,.15));border:1px solid var(--line)}
  .ttl{font-weight:700;font-size:16px;margin:0 0 4px}
  .desc{color:var(--muted);font-size:13px;margin:0}
  .meta{display:flex;gap:12px;flex-wrap:wrap;color:var(--muted);font-size:12px}
  .actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:6px}
  .a{display:inline-flex;align-items:center;gap:6px;padding:8px 10px;border-radius:10px;text-decoration:none;border:1px solid var(--line)}
  .a:hover{border-color:#3b82f6}
  .a.primary{background:linear-gradient(90deg,var(--accent),#60a5fa); color:#03131c; border:0}
  .list{margin-top:18px;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;background:var(--card);box-shadow:var(--shadow)}
  table{width:100%;border-collapse:collapse;font-size:14px}
  thead th{position:sticky;top:0;background:var(--bg2);text-align:left;padding:12px;border-bottom:1px solid var(--line);cursor:pointer}
  tbody td{padding:12px;border-bottom:1px solid var(--line)}
  tbody tr:hover{background:rgba(99,102,241,.08)}
  code{background:rgba(148,163,184,.15);padding:.1rem .35rem;border-radius:6px}
  footer{margin-top:28px;display:flex;justify-content:space-between;align-items:center;color:var(--muted);font-size:13px}
  .hide{display:none}
</style>
</head>
<body>
<div class="wrap">
  <header>
    <span class="dot" aria-hidden="true"></span>
    <div>
      <h1><?=htmlspecialchars($TITLE)?></h1>
      <p class="sub">Auto-indexed. Add files/folders—no code changes needed.</p>
    </div>
  </header>

  <div class="bar" role="search">
    <input id="q" class="input" type="search" placeholder="Type to filter…  (press / to focus)" aria-label="Filter entries">
    <div class="toggle">
      <button class="btn" id="viewGrid" aria-pressed="true" title="Grid view">Grid</button>
      <button class="btn" id="viewList" aria-pressed="false" title="List view">List</button>
    </div>
    <span style="flex:1"></span>
    <span id="count" class="pill">0 items</span>
  </div>

  <!-- GRID -->
  <section id="cards" class="grid" aria-label="Quick links">
    <?php foreach ($items as $e): ?>
      <article class="card" data-href="<?=htmlspecialchars($e['href'])?>">
        <div class="row">
          <div class="ico" aria-hidden="true">
            <?php if ($e['isDir']): ?>
              <svg width="22" height="22" viewBox="0 0 24 24" fill="none"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v1H3V7Z" stroke="currentColor" stroke-width="1.4"/><rect x="3" y="8" width="18" height="11" rx="2" stroke="currentColor" stroke-width="1.4"/></svg>
            <?php else: ?>
              <svg width="22" height="22" viewBox="0 0 24 24" fill="none"><rect x="4" y="3" width="16" height="18" rx="2" stroke="currentColor" stroke-width="1.4"/><path d="M8 7h8M8 11h8M8 15h5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
            <?php endif; ?>
          </div>
          <div>
            <h2 class="ttl"><?=htmlspecialchars($e['name'])?></h2>
            <p class="desc">
              <?=$e['isDir'] ? 'Folder' : (strtoupper($e['ext']) ?: 'File')?> •
              <span><?= $e['isDir'] ? 'approx ' : '' ?><?= number_format($e['size']) ?> B</span>
            </p>
          </div>
        </div>
        <div class="meta">
          <span>Last modified: <span><?= date('Y-m-d H:i', $e['mtime']) ?></span></span>
          <span>Type: <span><?=$e['isDir']?'Directory':'File'?></span></span>
        </div>
        <div class="actions">
          <a class="a primary" href="<?=htmlspecialchars($e['href'])?>">Open</a>
          <?php if(!$e['isDir']): ?>
            <a class="a" href="<?=htmlspecialchars($e['href'])?>" download>Download</a>
          <?php endif; ?>
        </div>
      </article>
    <?php endforeach; ?>
  </section>

  <!-- LIST -->
  <section id="tableWrap" class="list hide" aria-label="All entries (table view)">
    <table id="tbl">
      <thead>
        <tr>
          <th data-sort="name">Name</th>
          <th data-sort="mtime">Last Modified</th>
          <th data-sort="size">Size</th>
          <th>Open</th>
        </tr>
      </thead>
      <tbody id="rows">
        <?php foreach ($items as $e): ?>
          <tr data-name="<?=htmlspecialchars($e['name'])?>" data-size="<?=$e['size']?>" data-mtime="<?=$e['mtime']?>" data-href="<?=htmlspecialchars($e['href'])?>">
            <td><strong><?=htmlspecialchars($e['name'])?></strong>
              <div class="desc"><?= $e['isDir'] ? 'Folder' : (strtoupper($e['ext']) ?: 'File') ?></div>
            </td>
            <td><?= date('Y-m-d H:i', $e['mtime']) ?></td>
            <td><?= number_format($e['size']) ?> B</td>
            <td>
              <a class="a" href="<?=htmlspecialchars($e['href'])?>">Open</a>
              <?php if(!$e['isDir']): ?>
              <a class="a" href="<?=htmlspecialchars($e['href'])?>" download>Download</a>
              <?php endif; ?>
            </td>
          </tr>
        <?php endforeach; ?>
      </tbody>
    </table>
  </section>

  <footer>
    <span id="status">Ready.</span>
    <span>© <?=date('Y')?> <?=htmlspecialchars($_SERVER['HTTP_HOST'])?></span>
  </footer>
</div>

<script>
const els = {
  q: document.getElementById('q'),
  cards: document.getElementById('cards'),
  tableWrap: document.getElementById('tableWrap'),
  rows: document.getElementById('rows'),
  count: document.getElementById('count'),
  status: document.getElementById('status'),
  viewGrid: document.getElementById('viewGrid'),
  viewList: document.getElementById('viewList'),
};
let view='grid', sortKey='name', sortDir=1;

// Keyboard shortcuts
window.addEventListener('keydown',e=>{
  if(e.key==='/'){ e.preventDefault(); els.q.focus(); }
  if(e.key==='Escape'){ els.q.value=''; filter(); }
});

// Card click (navigate when clicking card body)
document.querySelectorAll('.card').forEach(card=>{
  card.addEventListener('click',e=>{
    if(e.target.closest('a')) return;
    location.href = card.getAttribute('data-href');
  });
});

// Toggle views
els.viewGrid.addEventListener('click',()=>{view='grid'; els.viewGrid.ariaPressed='true'; els.viewList.ariaPressed='false'; renderCount();});
els.viewList.addEventListener('click',()=>{view='list'; els.viewList.ariaPressed='true'; els.viewGrid.ariaPressed='false'; renderCount(); toggleList();});

function toggleList(){
  document.getElementById('cards').classList.toggle('hide', view==='list');
  els.tableWrap.classList.toggle('hide', view!=='list');
}

// Table sorting
document.querySelectorAll('th[data-sort]').forEach(th=>{
  th.addEventListener('click',()=>{
    const k = th.getAttribute('data-sort');
    if(sortKey===k) sortDir*=-1; else { sortKey=k; sortDir=1; }
    sortTable();
  });
});

function sortTable(){
  const rows = Array.from(els.rows.querySelectorAll('tr'));
  rows.sort((a,b)=>{
    const A = a.dataset[sortKey] || '';
    const B = b.dataset[sortKey] || '';
    // numeric for size/mtime
    if (sortKey==='size' || sortKey==='mtime') {
      return (Number(A)-Number(B)) * sortDir;
    }
    return A.localeCompare(B, undefined, {numeric:true}) * sortDir;
  });
  rows.forEach(r=>els.rows.appendChild(r));
}

function filter(){
  const q = els.q.value.trim().toLowerCase();
  // cards
  document.querySelectorAll('.card .ttl').forEach(ttl=>{
    const card = ttl.closest('.card');
    const name = ttl.textContent.toLowerCase();
    const desc = (card.querySelector('.desc')?.textContent||'').toLowerCase();
    const show = !q || name.includes(q) || desc.includes(q);
    card.style.display = show ? '' : 'none';
  });
  // table
  let visible = 0;
  els.rows.querySelectorAll('tr').forEach(tr=>{
    const name = tr.getAttribute('data-name').toLowerCase();
    const show = !q || name.includes(q);
    tr.style.display = show ? '' : 'none';
    if (show) visible++;
  });
  els.count.textContent = `${visible || document.querySelectorAll('.card:not([style*="display: none"])').length} item(s)`;
}

function renderCount(){
  if (view==='grid') {
    const visibleCards = document.querySelectorAll('.card:not([style*="display: none"])').length;
    els.count.textContent = `${visibleCards} item(s)`;
  } else {
    const visibleRows = Array.from(els.rows.querySelectorAll('tr')).filter(r=>r.style.display!=='none').length;
    els.count.textContent = `${visibleRows} item(s)`;
  }
}

els.q.addEventListener('input', ()=>{ filter(); renderCount(); });

// Initialize
filter(); sortTable(); toggleList();
els.status.textContent = `View: ${view} • Sorted by ${sortKey} ${sortDir>0?'↑':'↓'}`;
</script>
</body>
</html>
