﻿<?php
/**
 * Gallery Generator — static pagination
 *
 * Generates index.html, index_2.html, index_3.html … for the root directory
 * and for every subdirectory inside "categories/".
 *
 * Expected structure:
 *   categories/
 *     animals/
 *       photo1.jpg
 *       photo2.png
 *     nature/
 *       img1.jpg
 *
 * Usage: php generate_gallery.php
 */

// ─── Configuration ────────────────────────────────────────────────────────────

define('CATEGORIES_DIR', __DIR__ . '/categories');
define('THUMB_DIR',      '__thumbs');   // created inside each subdirectory
define('THUMB_WIDTH',    400);
define('THUMB_HEIGHT',   400);
define('PER_PAGE',       18);           // items per page

$imageExtensions = ['jpg','jpeg','png','gif','webp','avif','bmp','tiff','tif'];

// ─── Helpers ──────────────────────────────────────────────────────────────────

function isImage(string $filename, array $exts): bool {
    return in_array(strtolower(pathinfo($filename, PATHINFO_EXTENSION)), $exts, true);
}

/** Generates a 400×400 center-cropped thumbnail via GD. Returns relative path or null. */
function makeThumbnail(string $srcPath, string $thumbDir, string $filename): ?string {
    if (!function_exists('imagecreatefromjpeg')) return null;

    $ext       = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
    $thumbName = pathinfo($filename, PATHINFO_FILENAME) . '_thumb.jpg';
    $thumbPath = $thumbDir . '/' . $thumbName;

    if (file_exists($thumbPath)) return THUMB_DIR . '/' . $thumbName;

    try {
        switch ($ext) {
            case 'jpg':
            case 'jpeg': $src = @imagecreatefromjpeg($srcPath); break;
            case 'png':  $src = @imagecreatefrompng($srcPath);  break;
            case 'gif':  $src = @imagecreatefromgif($srcPath);  break;
            case 'webp': $src = function_exists('imagecreatefromwebp') ? @imagecreatefromwebp($srcPath) : false; break;
            case 'bmp':  $src = function_exists('imagecreatefrombmp') ? @imagecreatefrombmp($srcPath)  : false; break;
            default:     $src = false;
        }
        if (!$src) return null;

        $sw = imagesx($src); $sh = imagesy($src);
        $tw = THUMB_WIDTH;   $th = THUMB_HEIGHT;

        $srcR = $sw / $sh;
        $dstR = $tw / $th;

        if ($srcR > $dstR) {
            $cropH = $sh; $cropW = (int)($sh * $dstR);
            $cropX = (int)(($sw - $cropW) / 2); $cropY = 0;
        } else {
            $cropW = $sw; $cropH = (int)($sw / $dstR);
            $cropX = 0;  $cropY = (int)(($sh - $cropH) / 2);
        }

        $thumb = imagecreatetruecolor($tw, $th);
        imagecopyresampled($thumb, $src, 0, 0, $cropX, $cropY, $tw, $th, $cropW, $cropH);

        if (!is_dir($thumbDir)) mkdir($thumbDir, 0755, true);
        imagejpeg($thumb, $thumbPath, 85);
        imagedestroy($src); imagedestroy($thumb);

        return THUMB_DIR . '/' . $thumbName;
    } catch (Exception $e) { return null; }
}

/** Inline SVG placeholder for non-image files. */
function placeholderSvg(): string {
    return "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400' viewBox='0 0 400 400'%3E%3Crect width='400' height='400' fill='%23e8ecf1'/%3E%3Cg fill='%2394a3b8'%3E%3Crect x='155' y='130' width='90' height='110' rx='6'/%3E%3Crect x='170' y='155' width='60' height='8' rx='3'/%3E%3Crect x='170' y='172' width='45' height='8' rx='3'/%3E%3Crect x='170' y='189' width='52' height='8' rx='3'/%3E%3Crect x='170' y='206' width='38' height='8' rx='3'/%3E%3C/g%3E%3C/svg%3E";
}

/** Returns the filename for a given page number (1 → index.html, 2 → index_2.html). */
function pageFile(int $page): string {
    return $page === 1 ? 'index.html' : "index_{$page}.html";
}

// ─── Shared styles ────────────────────────────────────────────────────────────

function sharedStyles(): string {
    return <<<'CSS'
<style>
  @import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@300;400;500;600&family=DM+Serif+Display:ital@0;1&display=swap');

  :root {
    --bg:      #fafbfc;
    --fg:      #0f172a;
    --muted:   #64748b;
    --border:  #e8ecf1;
    --primary: #3b82f6;
    --surface: #ffffff;
  }

  *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

  html, body {
    background: var(--bg);
    color: var(--fg);
    font-family: 'DM Sans', -apple-system, BlinkMacSystemFont, sans-serif;
    -webkit-font-smoothing: antialiased;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
  }

  main { flex: 1; }

  /* ── Grid ── */
  .grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 3px;
    width: 100%;
  }
  @media (min-width:580px)  { .grid { grid-template-columns: repeat(3,1fr); } }
  @media (min-width:768px)  { .grid { grid-template-columns: repeat(4,1fr); } }
  @media (min-width:1024px) { .grid { grid-template-columns: repeat(6,1fr); } }

  /* ── Tile ── */
  .tile {
    position: relative;
    display: block;
    aspect-ratio: 1 / 1;
    overflow: hidden;
    background: var(--border);
    outline: none;
    text-decoration: none;
    cursor: pointer;
    animation: fadeUp .45s ease both;
  }
  .tile:focus-visible { box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--primary); }

  .tile img {
    width: 100%; height: 100%;
    object-fit: cover; display: block;
    transition: transform .55s cubic-bezier(.25,.46,.45,.94);
  }
  .tile:hover img { transform: scale(1.06); }

  .tile::after {
    content: ''; position: absolute; inset: 0;
    background: rgba(15,23,42,0);
    transition: background .3s ease;
    pointer-events: none;
  }
  .tile:hover::after { background: rgba(15,23,42,.08); }

  /* Filename label on hover */
  .tile .label {
    position: absolute; bottom: 0; left: 0; right: 0;
    padding: 24px 10px 10px;
    background: linear-gradient(to top, rgba(15,23,42,.7), transparent);
    color: #fff; font-size: 11px; font-weight: 500; letter-spacing: .03em;
    opacity: 0; transform: translateY(4px);
    transition: opacity .3s ease, transform .3s ease;
    pointer-events: none;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .tile:hover .label { opacity: 1; transform: translateY(0); }

  /* File-type badge */
  .file-badge {
    position: absolute; top: 8px; right: 8px;
    background: rgba(15,23,42,.65); backdrop-filter: blur(4px);
    color: #fff; font-size: 9px; font-weight: 700;
    letter-spacing: .1em; text-transform: uppercase;
    padding: 2px 6px; border-radius: 3px;
    opacity: 0; transition: opacity .25s; pointer-events: none;
  }
  .tile:hover .file-badge { opacity: 1; }

  /* ── Breadcrumb nav bar ── */
  .back-bar {
    display: flex; align-items: center; gap: 8px;
    padding: 14px 20px;
    border-bottom: 1px solid var(--border);
    background: var(--surface);
    position: sticky; top: 0; z-index: 10;
  }
  .back-bar a {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: 13px; font-weight: 500; color: var(--muted);
    text-decoration: none; transition: color .2s;
  }
  .back-bar a:hover { color: var(--fg); }
  .back-bar .sep     { font-size: 13px; color: var(--border); }
  .back-bar .current { font-size: 13px; font-weight: 500; color: var(--fg); }

  /* ── Hero ── */
  .hero {
    max-width: 48rem; margin: 0 auto;
    padding: 64px 24px; text-align: center;
  }
  @media (min-width:640px) { .hero { padding: 96px 24px; } }

  .eyebrow {
    font-size: 11px; font-weight: 600;
    letter-spacing: .2em; text-transform: uppercase; color: var(--primary);
  }
  h1 {
    margin-top: 14px;
    font-family: 'DM Serif Display', Georgia, serif;
    font-size: clamp(36px, 6vw, 60px);
    font-weight: 400; letter-spacing: -.02em; line-height: 1.05;
  }
  .lede { margin-top: 14px; font-size: 17px; color: var(--muted); font-weight: 300; }
  .rule { margin: 28px auto 0; width: 48px; height: 1px; background: var(--border); }

  /* ── Pagination ── */
  .pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 6px;
    padding: 40px 24px 56px;
  }

  .pagination a,
  .pagination span {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 36px; height: 36px;
    padding: 0 10px;
    border-radius: 4px;
    font-size: 13px;
    font-weight: 500;
    text-decoration: none;
    transition: background .18s, color .18s, border-color .18s;
    border: 1px solid var(--border);
    color: var(--muted);
    background: var(--surface);
    gap: 5px;
    white-space: nowrap;
  }

  .pagination a:hover {
    background: var(--fg);
    color: var(--bg);
    border-color: var(--fg);
  }

  .pagination .pg-current {
    background: var(--fg);
    color: var(--bg);
    border-color: var(--fg);
    cursor: default;
  }

  .pagination .pg-ellipsis {
    border-color: transparent;
    background: transparent;
    cursor: default;
    color: var(--border);
    min-width: 20px;
  }

  .pagination .pg-disabled {
    opacity: .32;
    pointer-events: none;
    cursor: default;
  }

  /* ── Footer ── */
  footer {
    text-align: center; padding: 24px;
    font-size: 12px; color: var(--border);
    border-top: 1px solid var(--border);
  }

  @keyframes fadeUp {
    from { opacity: 0; transform: translateY(12px); }
    to   { opacity: 1; transform: translateY(0); }
  }
</style>
CSS;
}

// ─── Render pagination block ──────────────────────────────────────────────────

function renderPagination(int $current, int $total): string {
    if ($total <= 1) return '';

    $chevL = '<svg width="13" height="13" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M9 11L5 7l4-4"/></svg>';
    $chevR = '<svg width="13" height="13" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M5 3l4 4-4 4"/></svg>';

    $out = '<nav class="pagination" aria-label="Pagination">' . "\n";

    // ← Previous
    if ($current > 1) {
        $href = pageFile($current - 1);
        $out .= "  <a href=\"{$href}\" aria-label=\"Previous page\">{$chevL} Previous</a>\n";
    } else {
        $out .= "  <span class=\"pg-disabled\" aria-hidden=\"true\">{$chevL} Previous</span>\n";
    }

    // Page numbers with sliding window
    $window = 2;
    $shown  = [];
    for ($p = 1; $p <= $total; $p++) {
        if ($p === 1 || $p === $total || abs($p - $current) <= $window) {
            $shown[] = $p;
        }
    }
    $shown = array_unique($shown);
    sort($shown);

    $prev = null;
    foreach ($shown as $p) {
        if ($prev !== null && $p - $prev > 1) {
            $out .= "  <span class=\"pg-ellipsis\" aria-hidden=\"true\">&hellip;</span>\n";
        }
        if ($p === $current) {
            $out .= "  <span class=\"pg-current\" aria-current=\"page\">{$p}</span>\n";
        } else {
            $href = pageFile($p);
            $out .= "  <a href=\"{$href}\" aria-label=\"Go to page {$p}\">{$p}</a>\n";
        }
        $prev = $p;
    }

    // Next →
    if ($current < $total) {
        $href = pageFile($current + 1);
        $out .= "  <a href=\"{$href}\" aria-label=\"Next page\">Next {$chevR}</a>\n";
    } else {
        $out .= "  <span class=\"pg-disabled\" aria-hidden=\"true\">Next {$chevR}</span>\n";
    }

    $out .= '</nav>' . "\n";
    return $out;
}

// ─── Generate pages for a category subdirectory ───────────────────────────────

function generateCategoryPages(
    string $catDir,
    string $catName,
    array  $files,
    array  $imageExts
): void {

    $thumbDir    = $catDir . '/' . THUMB_DIR;
    $totalFiles  = count($files);
    $totalPages  = max(1, (int)ceil($totalFiles / PER_PAGE));
    $displayName = ucwords(str_replace(['-','_'], ' ', $catName));
    $styles      = sharedStyles();

    // Remove stale pagination files from a previous run
    foreach (glob($catDir . '/index_*.html') as $old) unlink($old);

    for ($page = 1; $page <= $totalPages; $page++) {

        $slice     = array_slice($files, ($page - 1) * PER_PAGE, PER_PAGE);
        $start     = ($page - 1) * PER_PAGE + 1;
        $end       = min($page * PER_PAGE, $totalFiles);
        $tilesHtml = '';

        foreach ($slice as $i => $file) {
            $isImg   = isImage($file, $imageExts);
            $srcPath = $catDir . '/' . $file;
            $ext     = strtoupper(pathinfo($file, PATHINFO_EXTENSION)) ?: 'FILE';
            $label   = htmlspecialchars($file);
            $delay   = $i * 50;

            if ($isImg) {
                $thumb    = makeThumbnail($srcPath, $thumbDir, $file);
                $thumbSrc = $thumb ? htmlspecialchars($thumb) : $label;
                $href     = htmlspecialchars($file);

                $tilesHtml .= <<<HTML
      <a class="tile" href="{$href}" target="_blank" rel="noopener noreferrer"
         aria-label="Open image: {$label}" style="animation-delay:{$delay}ms">
        <img loading="lazy" alt="{$label}" src="{$thumbSrc}" />
        <span class="label">{$label}</span>
        <span class="file-badge">{$ext}</span>
      </a>

HTML;
            } else {
                $ph   = placeholderSvg();
                $href = htmlspecialchars($file);

                $tilesHtml .= <<<HTML
      <a class="tile" href="{$href}" target="_blank" rel="noopener noreferrer"
         aria-label="Open file: {$label}" style="animation-delay:{$delay}ms">
        <img loading="lazy" alt="File: {$label}" src="{$ph}" />
        <span class="label">{$label}</span>
        <span class="file-badge">{$ext}</span>
      </a>

HTML;
            }
        }

        $pageLabel  = $totalPages > 1 ? " — Page {$page} of {$totalPages}" : '';
        $countLabel = $totalFiles === 1 ? '1 file' : "{$totalFiles} files";
        $rangeLabel = $totalPages > 1
            ? "Showing {$start}–{$end} of {$totalFiles} files"
            : $countLabel;
        $pagination = renderPagination($page, $totalPages);
        $filename   = pageFile($page);

        $html = <<<HTML
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>{$displayName}{$pageLabel} — Gallery</title>
<meta name="description" content="Category {$displayName}: {$countLabel}." />
{$styles}
</head>
<body>

<nav class="back-bar" aria-label="Breadcrumb">
  <a href="../../index.html">
    <svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M9 11L5 7l4-4"/></svg>
    Gallery
  </a>
  <span class="sep">/</span>
  <span class="current">{$displayName}</span>
</nav>

<main>
  <section aria-label="Files in {$displayName}">
    <div class="grid">
{$tilesHtml}
    </div>
  </section>

  {$pagination}

  <section class="hero">
    <p class="eyebrow">Category</p>
    <h1>{$displayName}</h1>
    <p class="lede">{$rangeLabel}</p>
    <div class="rule"></div>
  </section>
</main>

<footer>&copy; Gallery &mdash; {$displayName}</footer>

</body>
</html>
HTML;

        file_put_contents($catDir . '/' . $filename, $html);
    }

    $pl = $totalPages === 1 ? '1 page' : "{$totalPages} pages";
    echo "  ✔ categories/{$catName}/ — {$totalFiles} items, {$pl}\n";
}

// ─── Generate root index pages ────────────────────────────────────────────────

function generateRootPages(array $categories): void {

    $catList    = array_values($categories);
    $total      = count($catList);
    $totalPages = max(1, (int)ceil($total / PER_PAGE));
    $styles     = sharedStyles();

    // Extra styles only needed on the root page
    $extraCss = <<<'CSS'
<style>
  .cat-tile .cat-info {
    position: absolute; bottom: 0; left: 0; right: 0;
    padding: 32px 12px 12px;
    background: linear-gradient(to top, rgba(15,23,42,.82), transparent);
    display: flex; flex-direction: column; gap: 2px;
    transform: translateY(4px); transition: transform .3s ease;
  }
  .cat-tile:hover .cat-info { transform: translateY(0); }
  .cat-name {
    color: #fff; font-size: 13px; font-weight: 600; letter-spacing: .01em;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .cat-count { color: rgba(255,255,255,.6); font-size: 11px; font-weight: 400; }
</style>
CSS;

    // Remove stale pagination files from a previous run
    foreach (glob(__DIR__ . '/index_*.html') as $old) unlink($old);

    for ($page = 1; $page <= $totalPages; $page++) {

        $slice     = array_slice($catList, ($page - 1) * PER_PAGE, PER_PAGE);
        $start     = ($page - 1) * PER_PAGE + 1;
        $end       = min($page * PER_PAGE, $total);
        $tilesHtml = '';

        foreach ($slice as $i => $info) {
            $catName     = $info['name'];
            $displayName = ucwords(str_replace(['-','_'], ' ', $catName));
            $href        = htmlspecialchars('categories/' . $catName . '/index.html');
            $countLabel  = $info['count'] === 1 ? '1 file' : "{$info['count']} files";
            $delay       = $i * 60;

            if ($info['cover']) {
                $thumbPath = 'categories/' . $catName . '/' . THUMB_DIR . '/' .
                             pathinfo($info['cover'], PATHINFO_FILENAME) . '_thumb.jpg';
                $thumbFull = __DIR__ . '/' . $thumbPath;
                $thumbSrc  = file_exists($thumbFull)
                    ? htmlspecialchars($thumbPath)
                    : htmlspecialchars('categories/' . $catName . '/' . $info['cover']);
                $alt = htmlspecialchars("Cover: {$displayName}");

                $tilesHtml .= <<<HTML
      <a class="tile cat-tile" href="{$href}"
         aria-label="Open category: {$displayName}" style="animation-delay:{$delay}ms">
        <img loading="lazy" alt="{$alt}" src="{$thumbSrc}" />
        <span class="cat-info">
          <span class="cat-name">{$displayName}</span>
          <span class="cat-count">{$countLabel}</span>
        </span>
      </a>

HTML;
            } else {
                $ph = placeholderSvg();
                $tilesHtml .= <<<HTML
      <a class="tile cat-tile" href="{$href}"
         aria-label="Open category: {$displayName}" style="animation-delay:{$delay}ms">
        <img loading="lazy" alt="{$displayName}" src="{$ph}" />
        <span class="cat-info">
          <span class="cat-name">{$displayName}</span>
          <span class="cat-count">{$countLabel}</span>
        </span>
      </a>

HTML;
            }
        }

        $pageLabel  = $totalPages > 1 ? " — Page {$page} of {$totalPages}" : '';
        $catLabel   = $total === 1 ? '1 category' : "{$total} categories";
        $rangeLabel = $totalPages > 1
            ? "Showing {$start}–{$end} of {$total} categories"
            : "A curated collection across {$catLabel}";
        $pagination = renderPagination($page, $totalPages);
        $filename   = pageFile($page);

        $html = <<<HTML
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Gallery{$pageLabel} — Curated Visual Collection</title>
<meta name="description" content="Gallery with {$catLabel}. Click a category to explore." />
{$styles}
{$extraCss}
</head>
<body>

<main>
  <section aria-label="Gallery categories">
    <div class="grid">
{$tilesHtml}
    </div>
  </section>

  {$pagination}

  <section class="hero">
    <p class="eyebrow">Collection</p>
    <h1>Gallery</h1>
    <p class="lede">{$rangeLabel}</p>
    <div class="rule"></div>
  </section>
</main>

<footer>&copy; Visual Gallery</footer>

</body>
</html>
HTML;

        file_put_contents(__DIR__ . '/' . $filename, $html);
    }

    $pl = $totalPages === 1 ? '1 page' : "{$totalPages} pages";
    echo "  ✔ root index — {$total} categories, {$pl}\n";
}

// ─── Main ─────────────────────────────────────────────────────────────────────

echo "\n🖼  Gallery Generator\n";
echo str_repeat('─', 44) . "\n";

if (!is_dir(CATEGORIES_DIR)) {
    echo "⚠  Directory 'categories/' not found in " . __DIR__ . "\n";
    echo "   Creating sample structure...\n\n";
    foreach (['nature', 'architecture', 'portraits'] as $cat) {
        $dir = CATEGORIES_DIR . '/' . $cat;
        mkdir($dir, 0755, true);
        file_put_contents($dir . '/README.txt', "Place images here ({$cat}).");
    }
    echo "   Folders created. Add images and run the script again.\n\n";
    exit(0);
}

// ── Scan categories ──
$categories = [];

foreach (glob(CATEGORIES_DIR . '/*', GLOB_ONLYDIR) as $catPath) {
    $catName = basename($catPath);
    if ($catName === THUMB_DIR) continue;

    $files = [];
    foreach (scandir($catPath) as $f) {
        if ($f[0] === '.')    continue;
        if ($f === 'index.html') continue;
        if (preg_match('/^index_\d+\.html$/', $f)) continue;
        if (is_dir($catPath . '/' . $f)) continue;
        $files[] = $f;
    }
    sort($files);

    // Pick a random image as the category cover
    $images = array_values(array_filter($files, function($f) use ($imageExtensions) { return isImage($f, $imageExtensions); }));
    $cover  = $images ? $images[array_rand($images)] : null;

    $categories[$catName] = [
        'name'  => $catName,
        'path'  => $catPath,
        'files' => $files,
        'count' => count($files),
        'cover' => $cover,
    ];
}

if (empty($categories)) {
    echo "⚠  No subdirectories found inside 'categories/'. Aborting.\n\n";
    exit(1);
}

echo "Found " . count($categories) . " categories  (PER_PAGE=" . PER_PAGE . ").\n\n";

// Generate pages for each category
foreach ($categories as $catName => $info) {
    echo "Processing: {$catName}/\n";
    generateCategoryPages($info['path'], $catName, $info['files'], $imageExtensions);
}

echo "\nGenerating root index...\n";
generateRootPages($categories);

// ── Summary ──
echo "\n✅  Done!\n\n";
echo "Generated files:\n";
echo "  index.html              ← root (page 1)\n";
echo "  index_2.html, …         ← root (next pages, if any)\n";
foreach (array_keys($categories) as $c) {
    $n = (int)ceil($categories[$c]['count'] / PER_PAGE);
    echo "  categories/{$c}/index.html\n";
    if ($n > 1) echo "  categories/{$c}/index_2.html … index_{$n}.html\n";
}
echo "\n";