let isPinching = false;
let pinchZoomOccurred = false;
let initialPinchDistance = null;
let initialScale = 1;
let currentScale = 1;
let currentTranslateX = 0;
let currentTranslateY = 0;
let isPanning = false;
let panStartX = 0;
let panStartY = 0;
let panOriginX = 0;
let panOriginY = 0;
let pinchStartMidX = 0;
let pinchStartMidY = 0;
let pinchStartTranslateX = 0;
let pinchStartTranslateY = 0;
let exifAbortController = null;
let isGalleryLoading = false;
let autoPlayTimer = null;
let isAutoPlay = false;
let navHideTimer = null;
const AUTO_PLAY_INTERVAL_MS = 5000;
let pendingAutoAdvance = false;

// Assume that currentGalleryImages is already declared in app.js.
// Only declare currentGalleryIndex if it isn't defined already.
if (typeof currentGalleryIndex === "undefined") {
  var currentGalleryIndex = -1;
}

let currentExifRequestId = 0;

function encodeGalleryPath(relUrl) {
  if (!relUrl) {
    return '';
  }
  return relUrl
    .split('/')
    .map(segment => encodeURIComponent(segment))
    .join('/');
}

function escapeHtml(value) {
  return String(value === undefined || value === null ? '' : value).replace(/[&<>"']/g, (char) => ({
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#39;'
  }[char]));
}

function renderExifData(exif) {
  const exifPanel = document.getElementById('gallery-exif');
  if (!exifPanel) {
    return;
  }

  const entries = exif && Object.keys(exif).length > 0 ? exif : null;
  if (!entries) {
    exifPanel.innerHTML = '<div class="exif-row exif-placeholder">No EXIF data</div>';
    return;
  }

  const importantKeys = [
    'Model',
    'Make',
    'LensModel',
    'DateTimeOriginal',
    'ExposureTime',
    'FNumber',
    'ISOSpeedRatings',
    'FocalLength',
    'FocalLengthIn35mmFilm',
    'ExposureBiasValue',
    'WhiteBalance',
    'Software'
  ];
  const rows = [];

  importantKeys.forEach(key => {
    const value = exif[key];
    if (value !== undefined && value !== null && value !== '') {
      rows.push([key, value]);
    }
  });

  if (rows.length === 0) {
    exifPanel.innerHTML = '<div class="exif-row exif-placeholder">No EXIF data</div>';
    return;
  }

  exifPanel.innerHTML = rows.map(([key, value]) => {
    const safeKey = escapeHtml(key);
    const safeValue = escapeHtml(Array.isArray(value) ? value.join(', ') : value);
    return `<div class="exif-row"><span class="exif-key">${safeKey}</span><span class="exif-value">${safeValue}</span></div>`;
  }).join('');
}

function showExifPlaceholder(message) {
  const exifPanel = document.getElementById('gallery-exif');
  if (!exifPanel) {
    return;
  }
  exifPanel.innerHTML = `<div class="exif-row exif-placeholder">${message}</div>`;
}

function loadExifData(relUrl) {
  const exifPanel = document.getElementById('gallery-exif');
  const infoButton = document.getElementById('gallery-info');
  if (!exifPanel || !infoButton) {
    return;
  }

  const requestId = ++currentExifRequestId;
  if (exifAbortController) {
    try {
      exifAbortController.abort();
    } catch (e) {
      /* ignore */
    }
  }
  exifAbortController = typeof AbortController !== 'undefined' ? new AbortController() : null;

  infoButton.classList.add('loading');
  showExifPlaceholder('Loading EXIF...');

  fetch(`/media_exif/${encodeGalleryPath(relUrl)}`, exifAbortController ? { signal: exifAbortController.signal } : {})
    .then(res => {
      if (!res.ok) {
        throw new Error('EXIF fetch failed');
      }
      return res.json();
    })
    .then(data => {
      if (requestId !== currentExifRequestId) {
        return;
      }
      infoButton.classList.remove('loading');
      renderExifData((data && data.exif) || {});
    })
    .catch((err) => {
      if (err && err.name === 'AbortError') {
        return;
      }
      if (requestId !== currentExifRequestId) {
        return;
      }
      infoButton.classList.remove('loading');
      showExifPlaceholder('EXIF not available');
    });
}

function toggleExifPanel() {
  const exifPanel = document.getElementById('gallery-exif');
  const infoButton = document.getElementById('gallery-info');
  if (!exifPanel || !infoButton) {
    return;
  }
  const isOpen = exifPanel.classList.toggle('open');
  infoButton.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
}

function resetExifPanel(clearOpen = true) {
  const exifPanel = document.getElementById('gallery-exif');
  const infoButton = document.getElementById('gallery-info');
  currentExifRequestId++;
  if (exifAbortController) {
    try {
      exifAbortController.abort();
    } catch (e) {
      /* ignore */
    }
    exifAbortController = null;
  }
  if (exifPanel) {
    exifPanel.innerHTML = '';
    if (clearOpen) {
      exifPanel.classList.remove('open');
    }
  }
  if (infoButton && clearOpen) {
    infoButton.setAttribute('aria-expanded', 'false');
    infoButton.classList.remove('loading');
  }
}

function updateFilenameDisplay(relUrl) {
  const filenameEl = document.getElementById('gallery-filename');
  if (!filenameEl) {
    return;
  }
  const filenameTextEl = filenameEl.querySelector('.filename-text');

  if (!relUrl) {
    if (filenameTextEl) {
      filenameTextEl.textContent = '';
    } else {
      filenameEl.textContent = '';
    }
    filenameEl.dataset.filename = '';
    return;
  }

  const parts = relUrl.split('/');
  const filename = parts[parts.length - 1] || relUrl;
  if (filenameTextEl) {
    filenameTextEl.textContent = filename;
  } else {
    filenameEl.textContent = filename;
  }
  filenameEl.dataset.filename = filename;
}

function updateFilenameProgress() {
  const filenameEl = document.getElementById('gallery-filename');
  if (!filenameEl) {
    return;
  }

  const total = currentGalleryImages.length;
  const hasValidIndex = currentGalleryIndex >= 0 && currentGalleryIndex < total;
  const progress = total > 0 && hasValidIndex
    ? ((currentGalleryIndex + 1) / total) * 100
    : 0;
  filenameEl.style.setProperty('--filename-progress', `${progress}%`);
}

function updateGalleryPosition() {
  const positionEl = document.getElementById('gallery-position');
  if (!positionEl) {
    return;
  }

  const total = currentGalleryImages.length;
  if (total <= 0 || currentGalleryIndex < 0 || currentGalleryIndex >= total) {
    positionEl.textContent = '';
    return;
  }
  positionEl.textContent = `${currentGalleryIndex + 1} / ${total}`;
}

function updateDownloadLink(relUrl) {
  const downloadBtn = document.getElementById('gallery-download');
  if (!downloadBtn) {
    return;
  }
  if (!relUrl) {
    downloadBtn.removeAttribute('data-relurl');
    return;
  }
  // Store relUrl for the click handler (which fetches a short-lived token)
  downloadBtn.dataset.relurl = relUrl;
}

function updateNextButtonState() {
  const nextButton = document.querySelector('.gallery-next');
  if (!nextButton) {
    return;
  }

  const atLastImage = currentGalleryIndex >= currentGalleryImages.length - 1;
  nextButton.textContent = atLastImage ? '\u00D7' : '\u203A';
  nextButton.setAttribute('aria-label', atLastImage ? 'Close gallery' : 'Next image');
  nextButton.classList.toggle('is-close', atLastImage);
}

function showGalleryNav() {
  clearTimeout(navHideTimer);
  document.querySelectorAll('.gallery-nav').forEach(btn => btn.classList.remove('nav-hidden'));
  if (currentScale > 1.01) {
    return;
  }
  navHideTimer = setTimeout(() => {
    document.querySelectorAll('.gallery-nav').forEach(btn => btn.classList.add('nav-hidden'));
  }, 1000);
}

function clamp(value, min, max) {
  return Math.min(Math.max(value, min), max);
}

function getPanBounds(image, scale) {
  const stage = document.getElementById('gallery-stage');
  if (!stage || !image) {
    return { maxX: 0, maxY: 0 };
  }
  const baseWidth = image.clientWidth || 0;
  const baseHeight = image.clientHeight || 0;
  const scaledWidth = baseWidth * scale;
  const scaledHeight = baseHeight * scale;

  return {
    maxX: Math.max(0, (scaledWidth - stage.clientWidth) / 2),
    maxY: Math.max(0, (scaledHeight - stage.clientHeight) / 2)
  };
}

function applyImageTransform(image) {
  if (!image) {
    return;
  }
  const { maxX, maxY } = getPanBounds(image, currentScale);
  currentTranslateX = clamp(currentTranslateX, -maxX, maxX);
  currentTranslateY = clamp(currentTranslateY, -maxY, maxY);
  image.style.transform = `translate(-50%, -50%) translate(${currentTranslateX}px, ${currentTranslateY}px) scale(${currentScale})`;
}

function resetImageTransformState(image = null) {
  isPinching = false;
  isPanning = false;
  pinchZoomOccurred = false;
  initialPinchDistance = null;
  initialScale = 1;
  currentScale = 1;
  currentTranslateX = 0;
  currentTranslateY = 0;
  panStartX = 0;
  panStartY = 0;
  panOriginX = 0;
  panOriginY = 0;
  pinchStartMidX = 0;
  pinchStartMidY = 0;
  pinchStartTranslateX = 0;
  pinchStartTranslateY = 0;
  if (image) {
    applyImageTransform(image);
  }
}

function openGalleryModal(relUrl) {
  const modal = document.getElementById('gallery-modal');
  if (!modal) {
    return;
  }
  document.body.style.overflow = 'hidden'; // Disable background scrolling.
  currentGalleryIndex = currentGalleryImages.indexOf(relUrl);
  if (currentGalleryIndex < 0) {
    currentGalleryIndex = 0;
  }
  modal.setAttribute('aria-hidden', 'false');
  showGalleryImage(relUrl);
  modal.style.display = 'flex';
  showGalleryNav();
}

function showGalleryImage(relUrl) {
  if (isGalleryLoading) {
    return;
  }
  isGalleryLoading = true;
  clearAutoPlayTimer();
  updateNextButtonState();
  updateGalleryPosition();
  updateFilenameProgress();
  loadExifData(relUrl);
  const fullUrl = '/media/' + relUrl;
  const stage = document.getElementById('gallery-stage');
  const loader = document.getElementById('loader-container');
  if (!stage || !loader) {
    isGalleryLoading = false;
    return;
  }
  updateDownloadLink(relUrl);
  // Set a timeout for showing the loader after 500ms.
  let loaderTimeout = setTimeout(() => {
    loader.style.display = 'flex';
  }, 500);

  // Preload the new image.
  const tempImg = new Image();
  tempImg.src = fullUrl;

  tempImg.onload = function () {
      clearTimeout(loaderTimeout);
      loader.style.display = 'none';
      updateFilenameDisplay(relUrl);
  
      // Select all current images in the gallery stage
      const existingImages = stage.querySelectorAll('img');
  
      // Create a new image element for the transition
      const newImage = document.createElement('img');
      newImage.src = fullUrl;
      newImage.style.position = 'absolute';
      newImage.style.top = '50%';   // Center vertically
      newImage.style.left = '50%';  // Center horizontally
      newImage.style.transform = 'translate(-50%, -50%)'; // Ensure centering
      newImage.style.transition = 'opacity 0.5s ease-in-out';
      newImage.style.opacity = 0;  // Start fully transparent
      newImage.id = 'gallery-modal-content';
      newImage.alt = '';

      // Add pinch-to-zoom event listeners
      newImage.addEventListener('touchstart', handlePinchStart, { passive: false });
      newImage.addEventListener('touchmove', handlePinchMove, { passive: false });
      newImage.addEventListener('touchend', handlePinchEnd, { passive: false });
      newImage.addEventListener('touchcancel', handlePinchEnd, { passive: false });


      // Reset pinch variables for the new image
      resetImageTransformState(newImage);
  
      // Append new image on top of existing ones
      stage.appendChild(newImage);
  
      // Fade out all old images and fade in the new one
      setTimeout(() => {
          existingImages.forEach(img => {
              img.style.opacity = 0; // Apply fade-out
          });
          newImage.style.opacity = 1; // Fade in the new image
      }, 10);
  
      // Wait for fade-out to finish, then remove all old images
      setTimeout(() => {
          existingImages.forEach(img => {
              if (img.parentNode) {
                  img.parentNode.removeChild(img); // Remove old images
              }
          });
          isGalleryLoading = false;
          if (pendingAutoAdvance && isAutoPlay) {
              pendingAutoAdvance = false;
              attemptAutoAdvance();
              return;
          }
          if (isAutoPlay) {
              scheduleAutoPlayCycle();
          }
      }, 510); // 500ms for fade-out + small buffer
  
      // Preload the next image
      preloadNextImage();
  };
  

  tempImg.onerror = function() {
    clearTimeout(loaderTimeout);
    loader.style.display = 'none';
    isGalleryLoading = false;
    pendingAutoAdvance = false;
    if (isAutoPlay) {
      scheduleAutoPlayCycle();
    }
    console.error('Error loading image:', fullUrl);
  };
}

function preloadNextImage() {
  if (currentGalleryIndex < currentGalleryImages.length - 1) {
    const nextImage = new Image();
    nextImage.src = '/media/' + currentGalleryImages[currentGalleryIndex + 1];
  }
}

function closeGalleryModal() {
  clearTimeout(navHideTimer);
  document.querySelectorAll('.gallery-nav').forEach(btn => btn.classList.remove('nav-hidden'));
  const modal = document.getElementById('gallery-modal');
  const stage = document.getElementById('gallery-stage');
  if (modal) {
    modal.style.display = 'none';
    modal.setAttribute('aria-hidden', 'true');
  }
  isGalleryLoading = false;
  setAutoPlay(false);
  updateDownloadLink(null);
  // remove all images
  if (stage) {
    const existingImages = stage.querySelectorAll('img');
    existingImages.forEach(img => {
      if (img.parentNode) {
        img.parentNode.removeChild(img);
      }
    });
  }
  const filenameEl = document.getElementById('gallery-filename');
  if (filenameEl) {
    const filenameTextEl = filenameEl.querySelector('.filename-text');
    if (filenameTextEl) {
      filenameTextEl.textContent = '';
    } else {
      filenameEl.textContent = '';
    }
    filenameEl.dataset.filename = '';
    filenameEl.style.setProperty('--filename-progress', '0%');
  }
  const positionEl = document.getElementById('gallery-position');
  if (positionEl) {
    positionEl.textContent = '';
  }
  resetExifPanel(true);
  resetImageTransformState();
  // Restore scrolling.
  document.body.style.overflow = '';
}

function navigateByScreenHalf(clientX) {
  if (isGalleryLoading) {
    return;
  }

  const midpoint = window.innerWidth / 2;
  if (clientX < midpoint) {
    if (currentGalleryIndex > 0) {
      currentGalleryIndex--;
      showGalleryImage(currentGalleryImages[currentGalleryIndex]);
    }
    return;
  }

  if (currentGalleryIndex < currentGalleryImages.length - 1) {
    currentGalleryIndex++;
    showGalleryImage(currentGalleryImages[currentGalleryIndex]);
  } else {
    closeGalleryModal();
  }
}

function handleGalleryScreenClick(e) {
  // If a pinch zoom occurred, don't change the image.
  if (pinchZoomOccurred) {
    pinchZoomOccurred = false;
    return;
  }
  if (currentScale > 1.01 || isPinching || isPanning) {
    return;
  }

  const target = e.target;
  if (!target) {
    return;
  }

  // Do not interpret clicks on controls/panels as navigation.
  if (target.closest('.gallery-control') || target.closest('.gallery-nav') || target.closest('#gallery-exif')) {
    return;
  }

  navigateByScreenHalf(e.clientX);
}




function initGallerySwipe() {
  let touchStartX = 0;
  let touchEndX = 0;
  const galleryModal = document.getElementById('gallery-modal');
  if (!galleryModal) {
    return;
  }

  galleryModal.addEventListener('touchstart', function(e) {
    // If more than one finger is on screen, ignore swipe tracking.
    if (e.touches.length > 1) {
      return;
    }
    if (currentScale > 1.01 || isPinching || isPanning) {
      return;
    }
    touchStartX = e.changedTouches[0].screenX;
  }, false);

  galleryModal.addEventListener('touchend', function(e) {
    // If the event was part of a multi-touch (pinch) gesture or a pinch was detected, skip swipe.
    if (e.changedTouches.length > 1 || isPinching || isPanning || pinchZoomOccurred || currentScale > 1.01) {
      return;
    }
    touchEndX = e.changedTouches[0].screenX;
    handleSwipe();
  }, false);

  function handleSwipe() {
    const deltaX = touchEndX - touchStartX;
    if (Math.abs(deltaX) > 50) { // Swipe threshold.
      if (deltaX > 0) { // Swipe right: previous image.
        if (!isGalleryLoading && currentGalleryIndex > 0) {
          currentGalleryIndex--;
          showGalleryImage(currentGalleryImages[currentGalleryIndex]);
        }
      } else { // Swipe left: next image.
        if (!isGalleryLoading && currentGalleryIndex < currentGalleryImages.length - 1) {
          currentGalleryIndex++;
          showGalleryImage(currentGalleryImages[currentGalleryIndex]);
        }
      }
    }
  }
}

function clearAutoPlayTimer() {
  if (autoPlayTimer) {
    clearTimeout(autoPlayTimer);
    autoPlayTimer = null;
  }
}

function setAutoPlay(active) {
  const playButton = document.getElementById('gallery-play');
  isAutoPlay = active;
  if (playButton) {
    playButton.textContent = active ? '\u2225' : '\u25B6';
    playButton.setAttribute('aria-pressed', active ? 'true' : 'false');
    playButton.setAttribute('aria-label', active ? 'Pause slideshow' : 'Start slideshow');
    playButton.classList.toggle('playing', active);
  }
  pendingAutoAdvance = false;
  clearAutoPlayTimer();
  if (active) {
    scheduleAutoPlayCycle();
  } else {
    setPlayRingProgress(0);
  }
}

function setPlayRingProgress(deg) {
  const playButton = document.getElementById('gallery-play');
  if (playButton) {
    playButton.style.setProperty('--play-progress', `${deg}deg`);
  }
}

function restartPlayAnimation() {
  const playButton = document.getElementById('gallery-play');
  if (!playButton) {
    return;
  }
  playButton.classList.remove('playing-anim');
  // force reflow to restart animation
  void playButton.offsetWidth;
  setPlayRingProgress(0);
  playButton.classList.add('playing-anim');
}

function completePlayRing() {
  const playButton = document.getElementById('gallery-play');
  if (!playButton) {
    return;
  }
  playButton.classList.remove('playing-anim');
  setPlayRingProgress(360);
}

function scheduleAutoPlayCycle() {
  if (!isAutoPlay) {
    return;
  }
  clearAutoPlayTimer();
  restartPlayAnimation();
  autoPlayTimer = setTimeout(() => {
    triggerAutoAdvance();
  }, AUTO_PLAY_INTERVAL_MS);
}

function triggerAutoAdvance() {
  completePlayRing();
  if (isGalleryLoading) {
    pendingAutoAdvance = true;
    return;
  }
  attemptAutoAdvance();
}

function attemptAutoAdvance() {
  pendingAutoAdvance = false;
  if (!isAutoPlay) {
    return;
  }
  if (currentGalleryIndex >= currentGalleryImages.length - 1) {
    setAutoPlay(false);
    return;
  }
  const nextUrl = currentGalleryImages[currentGalleryIndex + 1];
  const img = new Image();
  img.onload = function() {
    if (!isAutoPlay) {
      return;
    }
    currentGalleryIndex++;
    showGalleryImage(nextUrl);
  };
  img.onerror = function() {
    if (isAutoPlay) {
      scheduleAutoPlayCycle();
    }
  };
  img.src = '/media/' + nextUrl;
}

function handlePinchStart(e) {
  if (e.touches.length === 2) {
    e.preventDefault();
    isPinching = true;
    isPanning = false;
    pinchZoomOccurred = false; // Reset at the start of a pinch gesture.
    const dx = e.touches[0].pageX - e.touches[1].pageX;
    const dy = e.touches[0].pageY - e.touches[1].pageY;
    initialPinchDistance = Math.sqrt(dx * dx + dy * dy);
    initialScale = currentScale;
    pinchStartMidX = (e.touches[0].pageX + e.touches[1].pageX) / 2;
    pinchStartMidY = (e.touches[0].pageY + e.touches[1].pageY) / 2;
    pinchStartTranslateX = currentTranslateX;
    pinchStartTranslateY = currentTranslateY;
    showGalleryNav();
    return;
  }

  if (e.touches.length === 1 && currentScale > 1.01) {
    e.preventDefault();
    isPanning = true;
    panStartX = e.touches[0].pageX;
    panStartY = e.touches[0].pageY;
    panOriginX = currentTranslateX;
    panOriginY = currentTranslateY;
    showGalleryNav();
  }
}

function handlePinchMove(e) {
  if (e.touches.length === 2 && initialPinchDistance) {
    e.preventDefault();
    const dx = e.touches[0].pageX - e.touches[1].pageX;
    const dy = e.touches[0].pageY - e.touches[1].pageY;
    const currentDistance = Math.sqrt(dx * dx + dy * dy);
    let scale = currentDistance / initialPinchDistance;
    scale = Math.max(1, Math.min(scale * initialScale, 4));
    // If the scale has changed enough, mark that a pinch zoom occurred.
    if (Math.abs(scale - currentScale) > 0.01) {
      pinchZoomOccurred = true;
    }
    currentScale = scale;
    const midX = (e.touches[0].pageX + e.touches[1].pageX) / 2;
    const midY = (e.touches[0].pageY + e.touches[1].pageY) / 2;
    currentTranslateX = pinchStartTranslateX + (midX - pinchStartMidX);
    currentTranslateY = pinchStartTranslateY + (midY - pinchStartMidY);
    applyImageTransform(e.currentTarget);
    showGalleryNav();
    return;
  }

  if (e.touches.length === 1 && isPanning && currentScale > 1.01) {
    e.preventDefault();
    const deltaX = e.touches[0].pageX - panStartX;
    const deltaY = e.touches[0].pageY - panStartY;
    currentTranslateX = panOriginX + deltaX;
    currentTranslateY = panOriginY + deltaY;
    pinchZoomOccurred = true;
    applyImageTransform(e.currentTarget);
    showGalleryNav();
  }
}

function handlePinchEnd(e) {
  if (e.touches.length === 1 && currentScale > 1.01) {
    // Transition from pinch to one-finger pan.
    isPinching = false;
    isPanning = true;
    initialPinchDistance = null;
    panStartX = e.touches[0].pageX;
    panStartY = e.touches[0].pageY;
    panOriginX = currentTranslateX;
    panOriginY = currentTranslateY;
    showGalleryNav();
    return;
  }

  if (e.touches.length < 1) {
    isPinching = false;
    isPanning = false;
    initialPinchDistance = null;
    initialScale = currentScale;
    const image = e.currentTarget;
    if (currentScale <= 1.01) {
      currentScale = 1;
      currentTranslateX = 0;
      currentTranslateY = 0;
    }
    applyImageTransform(image);
    showGalleryNav();
  }
}

function handleGalleryKeydown(e) {
  const modal = document.getElementById('gallery-modal');
  if (!modal || modal.style.display !== 'flex') {
    return;
  }

  if (e.key === 'Escape') {
    closeGalleryModal();
    return;
  }

  if (e.key === 'ArrowLeft') {
    e.preventDefault();
    if (!isGalleryLoading && currentGalleryIndex > 0) {
      currentGalleryIndex--;
      showGalleryImage(currentGalleryImages[currentGalleryIndex]);
    }
    return;
  }

  if (e.key === 'ArrowRight') {
    e.preventDefault();
    if (!isGalleryLoading && currentGalleryIndex < currentGalleryImages.length - 1) {
      currentGalleryIndex++;
      showGalleryImage(currentGalleryImages[currentGalleryIndex]);
    } else if (!isGalleryLoading) {
      closeGalleryModal();
    }
    return;
  }

  if (e.key === ' ') {
    e.preventDefault();
    setAutoPlay(!isAutoPlay);
  }
}



function initGalleryControls() {
  const nextButton = document.querySelector('.gallery-next');
  const prevButton = document.querySelector('.gallery-prev');
  const closeButton = document.getElementById('gallery-close');
  const infoButton = document.getElementById('gallery-info');
  const playButton = document.getElementById('gallery-play');

  if (nextButton) {
      nextButton.addEventListener('click', function () {
          if (isGalleryLoading) {
              return;
          }
          if (currentGalleryIndex < currentGalleryImages.length - 1) {
              currentGalleryIndex++;
              showGalleryImage(currentGalleryImages[currentGalleryIndex]);
          } else {
              closeGalleryModal();
          }
      });
  }

  if (prevButton) {
      prevButton.addEventListener('click', function () {
          if (isGalleryLoading) {
              return;
          }
          if (currentGalleryIndex > 0) {
              currentGalleryIndex--;
              showGalleryImage(currentGalleryImages[currentGalleryIndex]);
          }
      });
  }

  if (closeButton) {
      closeButton.addEventListener('click', closeGalleryModal);
  }

  const downloadButton = document.getElementById('gallery-download');
  if (downloadButton) {
      downloadButton.addEventListener('click', async function (e) {
          e.preventDefault();
          e.stopPropagation();

          const relUrl = downloadButton.dataset.relurl;
          if (!relUrl) return;

          const encodedSubpath = relUrl
            .replace(/^\/+/, '')
            .split('/')
            .map(segment => encodeURIComponent(decodeURIComponent(segment)))
            .join('/');

          // Fetch a short-lived download token (works in Telegram's internal browser)
          let tokenizedUrl;
          try {
              const resp = await fetch(`/create_dltoken/${encodedSubpath}`);
              if (!resp.ok) return;
              tokenizedUrl = (await resp.text()).trim();
          } catch {
              return;
          }

          if (!tokenizedUrl) return;

          // Append download=true so the server returns the full-size original as attachment
          const sep = tokenizedUrl.includes('?') ? '&' : '?';
          const downloadUrl = new URL(tokenizedUrl + sep + 'download=true', window.location.href);

          const a = document.createElement('a');
          a.href = downloadUrl.toString();
          document.body.appendChild(a);
          a.click();
          document.body.removeChild(a);
      });
  }

  if (infoButton) {
      infoButton.addEventListener('click', function (e) {
          e.stopPropagation();
          toggleExifPanel();
      });
  }

  if (playButton) {
      playButton.addEventListener('click', function (e) {
          e.stopPropagation();
          setAutoPlay(!isAutoPlay);
      });
  }
}


function initGallery() {
  const galleryModal = document.getElementById('gallery-modal');
  if (!galleryModal || galleryModal.dataset.galleryBound) return;

  initGallerySwipe();
  initGalleryControls();

  galleryModal.addEventListener('click', handleGalleryScreenClick);
  document.addEventListener('keydown', handleGalleryKeydown);
  galleryModal.dataset.galleryBound = '1';
}

window.initGallery = initGallery;
