<?php
/**
 * Reusable avatar zoom/crop picker.
 * Include once per page (after the avatar markup). It renders a hidden file
 * input (#avatarInput), a crop modal, a toast, and the wiring.
 *
 * To use on a page:
 *   - Add a button:  onclick="document.getElementById('avatarInput').click()"
 *   - Add the avatar circle to update:  <div id="avatarPreview">…</div>
 *   On a successful upload it also live-updates the top-bar avatar
 *   (.topbar-avatar) so the new photo shows on the home/dashboard instantly.
 */
?>
<style>
/* The cropper overlay is moved to <body> by JS (see below) and flex-centred,
   so it is ALWAYS dead-centre of the screen — never a bottom sheet — no matter
   what container, transform or stale stylesheet sits on the page. */
.crop-overlay { position: fixed; inset: 0; z-index: 3000; background: rgba(15,23,42,.72); display: none; align-items: center; justify-content: center; padding: 16px; overflow-y: auto; -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px); }
.crop-overlay.open { display: flex; animation: cropFade .2s ease; }
/* While a photo modal is open, hide the bottom tab bar so it can't overlap */
body.dabil-modal-open .app-bottomnav { display: none !important; }
@keyframes cropFade { from { opacity: 0; } to { opacity: 1; } }
.crop-sheet { width: 100%; max-width: 420px; margin: auto; background: #fff; border-radius: 22px;
  padding: 20px 18px calc(18px + env(safe-area-inset-bottom)); max-height: min(92dvh, 660px);
  overflow-y: auto; -webkit-overflow-scrolling: touch; animation: cropPop .24s ease; }
@keyframes cropPop { from { transform: scale(.95); opacity: .5; } to { transform: scale(1); opacity: 1; } }
/* Current photo is tappable to view full size */
#avatarPreview { cursor: pointer; }
/* Full-image viewer (tap avatar to expand before changing) */
.av-view { position: fixed; inset: 0; z-index: 3100; display: none; align-items: center; justify-content: center; padding: 24px; background: rgba(8,12,24,.93); -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px); }
.av-view.open { display: flex; animation: cropFade .2s ease; }
.av-view img { max-width: 100%; max-height: 80vh; border-radius: 18px; object-fit: contain; box-shadow: 0 24px 70px rgba(0,0,0,.55); animation: cropPop .26s ease; }
.av-close { position: absolute; top: calc(env(safe-area-inset-top,0px) + 14px); right: 16px; width: 42px; height: 42px; border-radius: 50%; background: rgba(255,255,255,.16); color: #fff; border: none; display: flex; align-items: center; justify-content: center; cursor: pointer; }
.av-close svg { width: 22px; height: 22px; stroke: #fff; fill: none; stroke-width: 2.4; }
.crop-sheet h3 { font-size: 1.05rem; font-weight: 800; text-align: center; margin-bottom: 4px; color: var(--text); }
.crop-sheet .crop-sub { font-size: .8rem; color: var(--text-muted); text-align: center; margin-bottom: 14px; }
.crop-stage { position: relative; width: min(62vw, 230px); aspect-ratio: 1 / 1; margin: 0 auto 16px; border-radius: 50%; overflow: hidden; background: #0F172A; touch-action: none; cursor: grab; box-shadow: 0 0 0 3px #fff, 0 0 0 5px var(--primary-bg); }
.crop-stage:active { cursor: grabbing; }
.crop-stage img { position: absolute; top: 0; left: 0; will-change: transform; user-select: none; -webkit-user-drag: none; pointer-events: none; max-width: none; }
.crop-zoom-row { display: flex; align-items: center; gap: 12px; margin: 0 6px 18px; color: var(--text-muted); }
.crop-zoom-row svg { width: 18px; height: 18px; stroke: currentColor; fill: none; flex-shrink: 0; }
.crop-zoom-row input[type=range] { flex: 1; -webkit-appearance: none; appearance: none; height: 5px; border-radius: 5px; background: var(--border); outline: none; }
.crop-zoom-row input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 22px; height: 22px; border-radius: 50%; background: var(--primary); cursor: pointer; box-shadow: 0 2px 6px rgba(34,90,227,.4); }
.crop-zoom-row input[type=range]::-moz-range-thumb { width: 22px; height: 22px; border: none; border-radius: 50%; background: var(--primary); cursor: pointer; }
.crop-actions { display: flex; gap: 12px; }
.crop-btn { flex: 1; padding: 14px; border-radius: 13px; font-family: var(--font); font-weight: 700; font-size: .92rem; cursor: pointer; border: none; touch-action: manipulation; min-height: 50px; }
.crop-btn-cancel { background: var(--bg); color: var(--text); border: 1.5px solid var(--border); }
.crop-btn-cancel:active { background: var(--border); }
.crop-btn-save { background: var(--primary); color: #fff; box-shadow: 0 6px 18px rgba(34,90,227,.3); }
.crop-btn-save:active { background: var(--primary-dark); }
.crop-btn:disabled { opacity: .6; cursor: default; }
</style>

<input type="file" id="avatarInput" accept="image/*" style="display:none">

<div class="crop-overlay" id="cropOverlay" aria-hidden="true">
  <div class="crop-sheet" role="dialog" aria-modal="true" aria-label="Adjust photo">
    <h3>Adjust your photo</h3>
    <p class="crop-sub">Drag to reposition · pinch or slide to zoom</p>
    <div class="crop-stage" id="cropStage">
      <img id="cropImg" alt="">
    </div>
    <div class="crop-zoom-row">
      <svg viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"/><line x1="11" y1="8" x2="11" y2="14"/><line x1="8" y1="11" x2="14" y2="11"/></svg>
      <input type="range" id="cropZoom" min="1" max="4" step="0.01" value="1">
      <svg viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"/><line x1="8" y1="11" x2="14" y2="11"/></svg>
    </div>
    <div class="crop-actions">
      <button type="button" class="crop-btn crop-btn-cancel" id="cropCancel">Cancel</button>
      <button type="button" class="crop-btn crop-btn-save" id="cropSave">Set Photo</button>
    </div>
  </div>
</div>

<div class="av-view" id="avView" aria-hidden="true">
  <button type="button" class="av-close" id="avClose" aria-label="Close">
    <svg viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
  </button>
  <img id="avViewImg" src="" alt="Profile photo">
</div>

<div class="os-toast" id="profileToast" style="position:fixed;bottom:calc(env(safe-area-inset-bottom) + 24px);left:50%;transform:translateX(-50%);background:#0F172A;color:#fff;padding:11px 20px;border-radius:30px;font-size:.82rem;font-weight:600;box-shadow:0 6px 20px rgba(0,0,0,.25);z-index:1250;opacity:0;pointer-events:none;transition:opacity .25s;max-width:86%;text-align:center"></div>

<script>
(function () {
  function toast(msg) {
    const t = document.getElementById('profileToast');
    if (!t) return;
    t.textContent = msg; t.style.opacity = '1';
    clearTimeout(window._pToastTimer);
    window._pToastTimer = setTimeout(() => { t.style.opacity = '0'; }, 3000);
  }

  const input   = document.getElementById('avatarInput');
  const overlay = document.getElementById('cropOverlay');
  const stage   = document.getElementById('cropStage');
  const img     = document.getElementById('cropImg');
  const zoomEl  = document.getElementById('cropZoom');
  const btnCancel = document.getElementById('cropCancel');
  const btnSave   = document.getElementById('cropSave');
  if (!input || !overlay) return;

  // Move the fixed overlays to <body> so no page container/transform can trap
  // them — this guarantees the modal sits dead-centre, never a bottom sheet.
  [overlay, document.getElementById('avView'), document.getElementById('profileToast')].forEach(function (el) {
    if (el && el.parentNode !== document.body) document.body.appendChild(el);
  });

  // ── Full-image viewer (tap the avatar to expand before changing) ──────
  var avView = document.getElementById('avView');
  var avViewImg = document.getElementById('avViewImg');
  var avClose = document.getElementById('avClose');
  var avPreview = document.getElementById('avatarPreview');
  function openViewer(src) { if (!src || !avView) return; avViewImg.src = src; avView.classList.add('open'); avView.setAttribute('aria-hidden', 'false'); document.body.classList.add('dabil-modal-open'); }
  function closeViewer() { if (!avView) return; avView.classList.remove('open'); avView.setAttribute('aria-hidden', 'true'); document.body.classList.remove('dabil-modal-open'); avViewImg.src = ''; }
  if (avPreview) avPreview.addEventListener('click', function () { var im = avPreview.querySelector('img'); if (im) openViewer(im.getAttribute('src')); });
  if (avClose) avClose.addEventListener('click', closeViewer);
  if (avView) avView.addEventListener('click', function (e) { if (e.target === avView) closeViewer(); });

  const OUT = 600, MAX_ZOOM = 4;
  let natW = 0, natH = 0, baseScale = 1, zoom = 1, panX = 0, panY = 0, stageSize = 0;

  function render() {
    const scale = baseScale * zoom, dw = natW * scale, dh = natH * scale;
    const maxX = Math.max(0, (dw - stageSize) / 2), maxY = Math.max(0, (dh - stageSize) / 2);
    panX = Math.min(maxX, Math.max(-maxX, panX));
    panY = Math.min(maxY, Math.max(-maxY, panY));
    img.style.width = dw + 'px'; img.style.height = dh + 'px';
    img.style.transform = 'translate(' + ((stageSize - dw) / 2 + panX) + 'px,' + ((stageSize - dh) / 2 + panY) + 'px)';
  }

  function openCropper(file) {
    const reader = new FileReader();
    reader.onload = function (ev) {
      const tmp = new Image();
      tmp.onload = function () {
        natW = tmp.naturalWidth; natH = tmp.naturalHeight; img.src = tmp.src;
        overlay.classList.add('open'); overlay.setAttribute('aria-hidden', 'false');
        document.body.classList.add('dabil-modal-open');
        document.body.style.overflow = 'hidden';
        requestAnimationFrame(() => {
          stageSize = stage.getBoundingClientRect().width;
          baseScale = Math.max(stageSize / natW, stageSize / natH);
          zoom = 1; panX = 0; panY = 0; zoomEl.value = 1; render();
        });
      };
      tmp.onerror = function () { toast("Couldn't read that image."); };
      tmp.src = ev.target.result;
    };
    reader.readAsDataURL(file);
  }

  function closeCropper() {
    overlay.classList.remove('open'); overlay.setAttribute('aria-hidden', 'true');
    document.body.classList.remove('dabil-modal-open');
    document.body.style.overflow = ''; img.src = ''; input.value = '';
  }

  input.addEventListener('change', function (e) {
    const file = e.target.files[0];
    if (!file) return;
    if (!file.type.startsWith('image/')) { toast('Please choose an image file.'); input.value = ''; return; }
    if (file.size > 12 * 1024 * 1024) { toast('Image is too large (max 12MB).'); input.value = ''; return; }
    openCropper(file);
  });

  zoomEl.addEventListener('input', function () { zoom = parseFloat(this.value); render(); });

  const pointers = new Map();
  let pinchDist = 0, pinchZoom = 1;
  const dist = (a, b) => Math.hypot(a.x - b.x, a.y - b.y);

  stage.addEventListener('pointerdown', function (e) {
    stage.setPointerCapture(e.pointerId);
    pointers.set(e.pointerId, { x: e.clientX, y: e.clientY });
    if (pointers.size === 2) { const v = [...pointers.values()]; pinchDist = dist(v[0], v[1]); pinchZoom = zoom; }
  });
  stage.addEventListener('pointermove', function (e) {
    if (!pointers.has(e.pointerId)) return;
    const prev = pointers.get(e.pointerId), cur = { x: e.clientX, y: e.clientY };
    pointers.set(e.pointerId, cur);
    if (pointers.size === 1) { panX += cur.x - prev.x; panY += cur.y - prev.y; render(); }
    else if (pointers.size === 2) {
      const v = [...pointers.values()], d = dist(v[0], v[1]);
      if (pinchDist > 0) { zoom = Math.min(MAX_ZOOM, Math.max(1, pinchZoom * (d / pinchDist))); zoomEl.value = zoom; render(); }
    }
  });
  function endPointer(e) { if (pointers.has(e.pointerId)) pointers.delete(e.pointerId); if (pointers.size < 2) pinchDist = 0; }
  stage.addEventListener('pointerup', endPointer);
  stage.addEventListener('pointercancel', endPointer);
  stage.addEventListener('wheel', function (e) { e.preventDefault(); zoom = Math.min(MAX_ZOOM, Math.max(1, zoom - e.deltaY * 0.0015)); zoomEl.value = zoom; render(); }, { passive: false });

  btnCancel.addEventListener('click', closeCropper);
  overlay.addEventListener('click', function (e) { if (e.target === overlay) closeCropper(); });

  btnSave.addEventListener('click', function () {
    const scale = baseScale * zoom;
    const srcSize = stageSize / scale;
    const left = (stageSize - natW * scale) / 2 + panX;
    const top  = (stageSize - natH * scale) / 2 + panY;
    const sx = (-left) / scale, sy = (-top) / scale;

    const canvas = document.createElement('canvas');
    canvas.width = OUT; canvas.height = OUT;
    const ctx = canvas.getContext('2d');
    ctx.imageSmoothingQuality = 'high';
    ctx.drawImage(img, sx, sy, srcSize, srcSize, 0, 0, OUT, OUT);

    btnSave.disabled = true; btnCancel.disabled = true; btnSave.textContent = 'Saving…';

    canvas.toBlob(function (blob) {
      if (!blob) { toast("Couldn't process the image."); btnSave.disabled = false; btnCancel.disabled = false; btnSave.textContent = 'Set Photo'; return; }
      const fd = new FormData();
      fd.append('avatar', blob, 'avatar.jpg');
      const preview = document.getElementById('avatarPreview');
      const prevHtml = preview ? preview.innerHTML : '';
      if (preview) preview.innerHTML = '<span class="pa-initials" style="font-size:.8rem">…</span>';

      fetch('upload_avatar.php', { method: 'POST', body: fd })
        .then(r => r.json())
        .then(d => {
          if (d.ok && d.url) {
            const bust = d.url + '?t=' + Date.now();
            if (preview) preview.innerHTML = '<img src="' + bust + '" alt="">';
            // Reflect immediately on the top-bar avatar (shown on the home/dashboard)
            document.querySelectorAll('.topbar-avatar').forEach(el => { el.innerHTML = '<img src="' + bust + '" alt="">'; });
            toast('Profile photo updated!');
            closeCropper();
          } else {
            if (preview) preview.innerHTML = prevHtml;
            toast(d.error || "Couldn't update your photo.");
          }
        })
        .catch(() => { if (preview) preview.innerHTML = prevHtml; toast('Upload failed. Check your connection.'); })
        .finally(() => { btnSave.disabled = false; btnCancel.disabled = false; btnSave.textContent = 'Set Photo'; });
    }, 'image/jpeg', 0.9);
  });
})();
</script>
