<?php
// ── FLOWCHART LIST ──────────────────────────────────────────────────────────
// Scans this script's own directory for *.xml files whose content contains a
// "<flowchart" tag (i.e. files that are actually flowchart documents, as
// opposed to any other stray .xml file that might live alongside them), and
// renders a menu of links to flowchart.php?name=<basename>, each opening in
// the "MAIN" target frame/window.
//
// A file only needs to *contain* "<flowchart" (case-insensitive) rather than
// have it as the very first tag, since a leading XML declaration (<?xml ...)
// and possibly whitespace/comments normally precede the root element.

$dir = __DIR__;

// ── EXISTENCE CHECK (AJAX) ───────────────────────────────────────────────────
// Used by the "+ New flowchart" flow below: before navigating to
// flowchart.php, the client asks this endpoint whether a given base name is
// already taken, so a person can be warned and choose to open the existing
// flowchart instead of unknowingly overwriting/reusing it. Name resolution
// here mirrors flowchart.php's own exactly (basename, strip ".xml", strip
// anything outside [A-Za-z0-9_-]) so what's checked here is exactly what
// flowchart.php would actually read from / write to.
if (isset($_GET['action']) && $_GET['action'] === 'check') {
    header('Content-Type: application/json');
    $raw = $_GET['name'] ?? '';
    $raw = basename($raw);
    $raw = preg_replace('/\.xml$/i', '', $raw);
    $raw = preg_replace('/[^A-Za-z0-9_\-]/', '', $raw);
    echo json_encode([
        'ok'     => true,
        'name'   => $raw,
        'exists' => ($raw !== '' && is_file($dir . '/' . $raw . '.xml')),
    ]);
    exit;
}

/**
 * Returns true if $path looks like a flowchart document: valid enough to
 * read, and containing a case-insensitive "<flowchart" tag somewhere in its
 * content.
 */
function isFlowchartXml(string $path): bool {
    // Cheap guard: only read a chunk, not the whole file — the root tag is
    // always near the top, so there's no need to load huge files in full
    // just to answer "is this a flowchart?".
    $chunk = @file_get_contents($path, false, null, 0, 4096);
    if ($chunk === false) return false;
    return (bool) preg_match('/<flowchart\b/i', $chunk);
}

/**
 * Best-effort extraction of the root <flowchart title="..."> attribute, plus
 * a couple of other light details, purely for display in the menu. Falls
 * back gracefully (null / 0) if anything can't be parsed — a menu entry
 * should never disappear just because a file is slightly malformed.
 */
function readFlowchartSummary(string $path): array {
    $title = null;
    $nodeCount = null;
    $edgeCount = null;

    $prevLibxml = libxml_use_internal_errors(true);
    $doc = new DOMDocument();
    $ok = $doc->load($path);
    libxml_clear_errors();
    libxml_use_internal_errors($prevLibxml);

    if ($ok && $doc->documentElement && strtolower($doc->documentElement->nodeName) === 'flowchart') {
        $root = $doc->documentElement;
        if ($root->hasAttribute('title')) {
            $t = trim($root->getAttribute('title'));
            $title = ($t !== '') ? $t : null;
        }
        $nodeCount = $doc->getElementsByTagName('node')->length;
        $edgeCount = $doc->getElementsByTagName('edge')->length;
    }

    return [$title, $nodeCount, $edgeCount];
}

$entries = [];
foreach ((glob($dir . '/*.xml') ?: []) as $path) {
    if (!is_file($path) || !isFlowchartXml($path)) continue;

    $baseName = basename($path, '.xml');
    [$title, $nodeCount, $edgeCount] = readFlowchartSummary($path);

    $entries[] = [
        'baseName'  => $baseName,
        'title'     => $title,
        'nodeCount' => $nodeCount,
        'edgeCount' => $edgeCount,
        'mtime'     => @filemtime($path) ?: 0,
        'size'      => @filesize($path) ?: 0,
    ];
}

// FlowchartHelp always leads the list (if present), since it's the doc
// people most often want a quick link to regardless of when it last
// changed. Everything else follows, most-recently-modified first.
usort($entries, function ($a, $b) {
    $aIsHelp = strcasecmp($a['baseName'], 'FlowchartHelp') === 0;
    $bIsHelp = strcasecmp($b['baseName'], 'FlowchartHelp') === 0;
    if ($aIsHelp !== $bIsHelp) return $aIsHelp ? -1 : 1;
    return $b['mtime'] <=> $a['mtime'];
});

function humanFileSize(int $bytes): string {
    if ($bytes < 1024) return $bytes . ' B';
    if ($bytes < 1024 * 1024) return round($bytes / 1024, 1) . ' KB';
    return round($bytes / (1024 * 1024), 1) . ' MB';
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Flowcharts</title>
<style>
  /* Deliberately a distinct dark BLUE palette (vs. dataDiagramList.php's
     near-neutral dark), so which "...List" page you're on — and therefore
     which diagram type you're about to open — is obvious at a glance even
     without reading the header text. */
  :root {
    --bg-app: #0A1330;
    --bg-panel: #121F4A;
    --bg-input: #1A295C;
    --border: #253566;
    --border-strong: #3D54A0;
    --text-primary: #E8ECFB;
    --text-muted: #9AA8D9;
    --text-dim: #6B7AB0;
    --accent: #AAB8FF;
    --accent-strong: #5468E8;
    --accent-hover-glow: rgba(84,104,232,.22);
  }
  * { box-sizing: border-box; margin: 0; padding: 0; }
  body {
    font-family: 'Segoe UI', system-ui, sans-serif;
    background: var(--bg-app);
    color: var(--text-primary);
    padding: 32px 24px 64px;
  }
  .wrap { max-width: 780px; margin: 0 auto; }
  header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 24px;
    flex-wrap: wrap;
  }
  h1 { font-size: 22px; font-weight: 600; }
  .count { color: var(--text-dim); font-size: 13px; }
  .header-actions { display: flex; align-items: center; gap: 10px; }
  .new-btn {
    display: inline-block;
    padding: 8px 16px;
    border-radius: 8px;
    background: var(--accent-strong);
    color: #fff;
    text-decoration: none;
    font-size: 14px;
    font-weight: 500;
    white-space: nowrap;
    border: none;
    font-family: inherit;
    cursor: pointer;
  }
  .new-btn:hover { filter: brightness(1.1); }
  .exit-btn {
    display: inline-block;
    padding: 8px 16px;
    border-radius: 8px;
    background: var(--bg-panel);
    border: 1px solid var(--border-strong);
    color: var(--text-primary);
    font-size: 14px;
    font-weight: 500;
    font-family: inherit;
    white-space: nowrap;
    cursor: pointer;
  }
  .exit-btn:hover { background: var(--bg-input); }
  .list { display: flex; flex-direction: column; gap: 10px; }
  .card {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    background: var(--bg-panel);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 14px 18px;
    text-decoration: none;
    color: #FFFFFF;
    transition: border-color .12s, background .12s;
  }
  .card:hover { border-color: var(--border-strong); background: var(--bg-input); }
  .card-main { min-width: 0; }
  .card-title { font-size: 15px; font-weight: 600; color: #FFFFFF; }
  .card-sub {
    margin-top: 3px;
    font-size: 12.5px;
    color: #E4E8FF;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .card-meta {
    flex-shrink: 0;
    text-align: right;
    font-size: 12px;
    color: #C6CDF2;
    line-height: 1.5;
  }
  .empty {
    background: var(--bg-panel);
    border: 1px dashed var(--border-strong);
    border-radius: 10px;
    padding: 40px 20px;
    text-align: center;
    color: var(--text-muted);
  }
  .empty a { color: var(--accent); }
  .empty button.link-like {
    background: none; border: none; padding: 0; margin: 0;
    color: var(--accent); font: inherit; cursor: pointer; text-decoration: underline;
  }
</style>
</head>
<body>
<div class="wrap">
  <header>
    <div>
      <h1>Flowcharts</h1>
      <div class="count">
        <?php echo count($entries); ?> found in <?php echo htmlspecialchars($dir); ?>
      </div>
    </div>
    <div class="header-actions">
      <button class="new-btn" type="button" onclick="startNewFlowchart()">+ New flowchart</button>
      <button class="exit-btn" type="button" onclick="exitApp()">🚪 Exit</button>
    </div>
  </header>

  <?php if (!$entries): ?>
    <div class="empty">
      No flowchart XML files found here yet.<br>
      <button type="button" class="link-like" onclick="startNewFlowchart()">Create your first one</button>.
    </div>
  <?php else: ?>
    <div class="list">
      <?php foreach ($entries as $e): ?>
        <a class="card"
           href="flowchart.php?name=<?php echo urlencode($e['baseName']); ?>"
           target="MAIN">
          <div class="card-main">
            <div class="card-title"><?php echo htmlspecialchars($e['title'] ?? $e['baseName']); ?></div>
            <div class="card-sub">
              <?php echo htmlspecialchars($e['baseName']); ?>.xml
              <?php if ($e['nodeCount'] !== null): ?>
                &nbsp;·&nbsp;<?php echo (int)$e['nodeCount']; ?> node<?php echo $e['nodeCount'] == 1 ? '' : 's'; ?>
                &nbsp;·&nbsp;<?php echo (int)$e['edgeCount']; ?> edge<?php echo $e['edgeCount'] == 1 ? '' : 's'; ?>
              <?php endif; ?>
            </div>
          </div>
          <div class="card-meta">
            <?php echo $e['mtime'] ? date('M j, Y g:ia', $e['mtime']) : '—'; ?><br>
            <?php echo humanFileSize($e['size']); ?>
          </div>
        </a>
      <?php endforeach; ?>
    </div>
  <?php endif; ?>
</div>
<script>
// Strips a ".xml" extension, any path components, and anything outside
// letters/digits/underscore/hyphen — a client-side mirror of the exact
// sanitization this page's own "check" endpoint (and flowchart.php's name
// resolution) apply server-side, so a bad name is caught immediately
// instead of round-tripping first.
function sanitizeBaseName(raw) {
  return String(raw || '')
    .trim()
    .replace(/^.*[\\\/]/, '')
    .replace(/\.xml$/i, '')
    .replace(/[^A-Za-z0-9_\-]/g, '');
}

// Prompts for a name, validates it, and checks whether a flowchart with
// that name already exists before navigating to flowchart.php — so
// starting "new" never silently lands on (and risks overwriting) an
// existing flowchart without the person knowing. Loops until the person
// either picks a free name, chooses to open an existing one instead, or
// cancels.
async function startNewFlowchart() {
  while (true) {
    const proposed = prompt('Name for the new flowchart (no extension):', '');
    if (proposed === null) return; // cancelled

    const cleaned = sanitizeBaseName(proposed);
    if (!cleaned) {
      alert('Please enter a valid name — letters, digits, "_" and "-" only.');
      continue;
    }

    let finalName = cleaned;
    let exists = false;
    try {
      const resp = await fetch('flowchartList.php?action=check&name=' + encodeURIComponent(cleaned), { cache: 'no-store' });
      const data = await resp.json();
      if (data && data.ok) {
        // The server's sanitized name is canonical — reflect it back so
        // what gets confirmed/opened matches what flowchart.php would
        // actually resolve to.
        finalName = data.name || cleaned;
        exists = !!data.exists;
      }
    } catch (e) {
      console.error('Existence check failed:', e);
      // Can't verify right now — fall through rather than blocking
      // flowchart creation entirely; flowchart.php's own save-time
      // handling (for a file that doesn't exist on disk yet) is still a
      // backstop.
    }

    if (!finalName) {
      alert('Please enter a valid name — letters, digits, "_" and "-" only.');
      continue;
    }

    if (exists) {
      const openExisting = confirm(
        `A flowchart named "${finalName}" already exists.\n\nOpen the existing one instead of creating a new one?`
      );
      if (openExisting) {
        window.open('flowchart.php?name=' + encodeURIComponent(finalName), 'MAIN');
        return;
      }
      continue; // ask again for a different name
    }

    window.open('flowchart.php?name=' + encodeURIComponent(finalName), 'MAIN');
    return;
  }
}

// Simple back-navigation — this list page has no unsaved state of its own
// to confirm before leaving, unlike flowchart.php/dataDiagram.php's Exit
// button, so this just goes straight back one step in history.
function exitApp() {
  history.go(-1);
  // Note: window.close() only works on tabs/windows that were opened via
  // script — most browsers silently ignore it on a normally-navigated tab.
  setTimeout(() => window.close(), 1000);
}
</script>
</body>
</html>