<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <title>Take Attendance</title>
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <style>
    :root{
      --bg:#F7F8FA; --surface:#ffffff; --text:#0f172a; --muted:#64748b; --primary:#2563eb;
      --ok:#10b981; --danger:#ef4444; --border:#e5e7eb; --chip:#f1f5f9;
      --sidebar:#111827; --sidefg:#e5e7eb; --sidehover:#1f2937;
    }
    *{box-sizing:border-box}
    body{font-family:system-ui, -apple-system, Segoe UI, Roboto, Arial; color:var(--text); background:var(--bg); margin:0}
    a{color:var(--primary); text-decoration:none}
    a:hover{text-decoration:underline}

    /* ===== Shell & Sidebar ===== */
    .shell{display:flex; min-height:100vh}
    .sidebar{width:260px; background:var(--sidebar); color:var(--sidefg); padding:18px 16px; position:sticky; top:0; height:100dvh; display:flex; flex-direction:column; gap:16px; border-right:1px solid rgba(255,255,255,.06)}
    .brand{display:flex; align-items:center; gap:10px; font-weight:800; letter-spacing:.2px}
    .brand .dot{width:10px; height:10px; border-radius:50%; background:var(--ok)}
    .side-nav{display:flex; flex-direction:column; gap:6px; margin-top:6px}
    .side-link{display:flex; align-items:center; gap:10px; padding:10px 12px; border-radius:10px; color:var(--sidefg); border:1px solid transparent}
    .side-link:hover{background:rgba(255,255,255,.06)}
    .side-link.active{background:var(--sidehover); border-color:#374151}
    .side-spacer{flex:1}
    .side-foot{font-size:12px; opacity:.75}
    .hamburger{display:none; position:fixed; top:12px; left:12px; z-index:50; background:var(--sidebar); color:var(--sidefg); border:1px solid #2b3440; border-radius:10px; padding:8px 10px; cursor:pointer}

    /* ===== Content ===== */
    .content{flex:1}
    .container{max-width:1200px; margin:24px auto; padding:0 16px}
    .topbar{display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:16px}
    .page-title{font-size:26px; font-weight:800}
    .chips{display:flex; gap:8px; flex-wrap:wrap}
    .chip{background:var(--chip); padding:6px 10px; border-radius:999px; font-size:12px}

    .layout{display:grid; grid-template-columns: 1.05fr .95fr; gap:24px}
    @media (max-width: 960px){ .layout{grid-template-columns:1fr} }

    .panel{background:var(--surface); border:1px solid var(--border); border-radius:12px; padding:14px}
    .panel h3{margin:0 0 10px 0; font-size:16px}

    .row{display:flex; gap:12px; align-items:center; flex-wrap:wrap}
    .button{padding:10px 14px; border:none; border-radius:10px; cursor:pointer; font-weight:600; font-size:14px; background:var(--primary); color:#fff; box-shadow:0 4px 12px rgba(37,99,235,.15)}
    .button.secondary{background:#475569}
    .button:disabled{opacity:.6; cursor:not-allowed}

    .status{font-size:13px; color:var(--muted)}

    /* Video */
    .video-wrap{ position: relative; width: 100%; max-width: 560px; aspect-ratio: 4/3; }
    video{ position:absolute; inset:0; width:100%; height:100%; background:#000; object-fit:cover }
    canvas#overlay{ position:absolute; inset:0; width:100%; height:100%; pointer-events:none }

    /* Log */
    .log{ height:280px; overflow:auto; background:#0b1220; color:#e5e7eb; border:1px solid #111827; padding:10px; border-radius:12px; font:12px/1.35 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace }
    .log div{ padding:4px 0; border-bottom:1px dashed rgba(255,255,255,.06) }
    .log div:last-child{ border-bottom:none }

    /* Inputs */
    input[type="text"], input[placeholder]{ padding:10px 12px; border:1px solid var(--border); border-radius:10px; outline:none; font-size:14px }
    input[type="text"]:focus{ border-color:#c7d2fe; box-shadow:0 0 0 3px rgba(37,99,235,.15) }

    /* Mobile sidebar */
    @media (max-width: 900px){ .sidebar{position:fixed; left:-280px; transition:left .18s ease} .sidebar.open{left:0} .hamburger{display:block} }
  </style>
</head>
<body>
  <button class="hamburger" id="hamburger">☰ Menu</button>
  <div class="shell">
   <!-- sidebar.html -->
<aside class="sidebar" id="sidebar">
  <div class="brand">
    <span class="dot"></span> <span>FaceAttend</span>
  </div>

  <nav class="side-nav">
   <a class="side-link" href="/admin">Admin Panel</a>
    <a class="side-link" href="/register">Register (Users)</a>
    <a class="side-link" href="/attendance">Take Attendance</a>
     <a class="side-link" href="/logout">Logout</a>
  </nav>

  <div class="side-spacer"></div>
  <div class="side-foot">© <span id="yy"></span> • v1.1</div>
</aside>

<script>
  document.getElementById('yy').textContent = new Date().getFullYear();
</script>


    <!-- Content -->
    <main class="content">
      <div class="container">
        <div class="topbar">
          <div class="page-title">Take Attendance</div>
          <div class="chips" role="navigation" aria-label="Quick links">
            <a class="chip" href="/register">Register</a>
            <a class="chip" href="/attendance">Attendance</a>
            <a class="chip" href="/admin">Admin</a>
          </div>
        </div>

        <div class="layout">
          <!-- LEFT: camera -->
          <div>
            <div class="panel">
              <h3>Camera</h3>
              <div class="video-wrap">
                <video id="video" autoplay playsinline></video>
                <canvas id="overlay" width="420" height="315"></canvas>
              </div>
              <div class="row" style="margin-top:10px">
                <button id="btnStart" class="button">Start</button>
                <button id="btnStop"  class="button secondary" disabled>Stop</button>
                <span id="status" class="status"></span>
              </div>
            </div>
          </div>

          <!-- RIGHT: log & manual -->
          <div>
            <div class="panel">
              <h3>Log</h3>
              <div id="log" class="log"></div>
            </div>
            <div class="panel" style="margin-top:14px">
              <h3>Manual Fallback</h3>
              <div class="row">
                <input id="manualCode" type="text" placeholder="Student Code e.g., S-0101" />
                <button id="btnManual" class="button">Mark Manual</button>
              </div>
            </div>
          </div>
        </div>
      </div>
    </main>
  </div>

  <!-- hidden canvas used only to grab a frame -->
  <canvas id="grab" width="420" height="315" style="display:none"></canvas>

  <script>
    // Sidebar interactions
    const hamburger = document.getElementById('hamburger');
    const sidebar = document.getElementById('sidebar');
    hamburger.addEventListener('click', ()=> sidebar.classList.toggle('open'));
    document.getElementById('yy').textContent = new Date().getFullYear();

    // ===== Original functionality preserved =====
    const video = document.getElementById('video');
    const overlay = document.getElementById('overlay');
    const octx = overlay.getContext('2d');
    const grab = document.getElementById('grab');
    const gctx = grab.getContext('2d');

    const log = document.getElementById('log');
    const btnStart = document.getElementById('btnStart');
    const btnStop = document.getElementById('btnStop');
    const manualCode = document.getElementById('manualCode');
    const btnManual = document.getElementById('btnManual');
    const statusEl = document.getElementById('status');

    let stream = null;       // track MediaStream so we can stop it
    let running = false;
    let inFlight = false;
    let lastSent = 0;
    const SEND_EVERY_MS = 250;

    // Try multi-face endpoint first; if it fails once, fallback to single-face
    let useMulti = true;

    function appendLog(text) {
      const p = document.createElement('div');
      p.textContent = new Date().toLocaleTimeString() + ' - ' + text;
      log.prepend(p);
    }

    function clearOverlay() { octx.clearRect(0, 0, overlay.width, overlay.height); }

    function drawBox(box, label, color) {
      if (!box) return;
      const {x, y, w, h} = box;
      octx.lineWidth = 3;
      octx.strokeStyle = color;
      octx.strokeRect(x, y, w, h);

      const pad = 6;
      octx.font = "14px sans-serif";
      const text = label || "";
      const tw = octx.measureText(text).width;
      const tx = Math.max(0, x);
      const ty = Math.max(0, y - 20);
      octx.fillStyle = color;
      octx.fillRect(tx, ty, tw + pad*2, 20);
      octx.fillStyle = "#fff";
      octx.fillText(text, tx + pad, ty + 14);
    }

    async function openCamera() {
      if (stream) return; // already open
      try {
        stream = await navigator.mediaDevices.getUserMedia({
          video: { width: { ideal: 640 }, height: { ideal: 480 }, facingMode: 'user' },
          audio: false
        });
        video.srcObject = stream;
        await video.play();
        statusEl.textContent = 'Camera ready.';
      } catch (e) {
        appendLog('Cannot access webcam: ' + e.message);
        statusEl.textContent = 'Webcam error';
        stream = null;
      }
    }

    function closeCamera() {
      if (stream) {
        stream.getTracks().forEach(t => t.stop());
        stream = null;
      }
      video.srcObject = null;
    }

    function frameDataURL() {
      gctx.drawImage(video, 0, 0, grab.width, grab.height);
      return grab.toDataURL('image/jpeg', 0.8); // good quality for LBPH
    }

    async function callRecognizeMulti(dataURL) {
      const res = await fetch('/api/recognize_frame_multi', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ image_base64: dataURL })
      });
      if (!res.ok) throw new Error('multi endpoint HTTP ' + res.status);
      return res.json();
    }

    async function callRecognizeSingle(dataURL) {
      const res = await fetch('/api/recognize_frame', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ image_base64: dataURL })
      });
      if (!res.ok) throw new Error('single endpoint HTTP ' + res.status);
      const j = await res.json();
      // Convert to multi-like structure for unified drawing
      if (j.ok && j.box) {
        return {
          ok: true,
          detections: [{
            box: j.box,
            matched: j.matched,
            full_name: j.full_name,
            student_code: j.student_code,
            confidence: j.confidence,
            threshold: j.threshold
          }]
        };
      }
      if (j.ok && j.reason === 'No face') {
        return { ok: true, detections: [] };
      }
      return j; // pass through errors
    }

    async function sendFrame() {
      if (!stream || !video.srcObject) return; // no camera
      inFlight = true;
      try {
        const dataURL = frameDataURL();
        let payload;

        if (useMulti) {
          try {
            payload = await callRecognizeMulti(dataURL);
          } catch (e) {
            // first failure: permanently fall back this session
            useMulti = false;
            appendLog('Multi-face endpoint unavailable. Falling back to single-face.');
            payload = await callRecognizeSingle(dataURL);
          }
        } else {
          payload = await callRecognizeSingle(dataURL);
        }

        clearOverlay();

        if (!payload.ok) {
          statusEl.textContent = payload.error || 'Server error';
          return;
        }

        const dets = payload.detections || [];
        if (dets.length === 0) {
          statusEl.textContent = 'No face';
        } else {
          let anyMatched = false;
          for (const d of dets) {
            const label = d.matched
              ? `${d.full_name} (${d.student_code})  c:${d.confidence.toFixed(1)}≤${d.threshold}`
              : `Unknown  c:${(d.confidence ?? 0).toFixed(1)}≤${d.threshold}`;
            drawBox(d.box, label, d.matched ? "#00c853" : "#e53935");
            if (d.matched) anyMatched = true;
          }
          statusEl.textContent = anyMatched ? 'Marked ✓' : '';
        }

      } catch (e) {
        appendLog('Network error: ' + e.message);
        statusEl.textContent = 'Network error';
      } finally {
        inFlight = false;
      }
    }

    function loop(ts) {
      if (!running) return;
      if (!inFlight && ts - lastSent >= SEND_EVERY_MS) {
        lastSent = ts;
        sendFrame();
      }
      requestAnimationFrame(loop);
    }

    btnStart.addEventListener('click', async () => {
      if (running) return;
      await openCamera();
      if (!stream) return; // failed to open
      running = true;
      btnStart.disabled = true;
      btnStop.disabled = false;
      statusEl.textContent = 'Detecting…';
      requestAnimationFrame(loop);
    });

    btnStop.addEventListener('click', () => {
      running = false;
      btnStart.disabled = false;
      btnStop.disabled = true;
      clearOverlay();
      closeCamera();             // camera OFF
      statusEl.textContent = 'Stopped.';
      appendLog('Recognition stopped.');
    });

    btnManual.addEventListener('click', async () => {
      const code = manualCode.value.trim();
      if (!code) return;
      const res = await fetch('/api/manual_attendance', {
        method: 'POST', headers: {'Content-Type':'application/json'},
        body: JSON.stringify({ student_code: code })
      }).then(r=>r.json());
      if (res.ok) appendLog('Manual: ' + code + ' recorded.');
      else appendLog('Manual failed: ' + (res.error || 'unknown'));
    });
  </script>
</body>
</html>
