????????????????????
??????????????????
ÿØÿà
 JFIF      ÿÛ C      


!"$"$ÿÛ C    
ÿÂ p 
" ÿÄ     
         ÿÄ             ÿÚ 
   ÕÔË®

(%	aA*‚XYD¡(J„¡E¢RE,P€XYae )(E¤²€B¤R¥	BQ¤¢ X«)X…€¤   @  

adadasdasdasasdasdas


.....................................................................................................................................????????????????????
??????????????????
ÿØÿà
 JFIF      ÿÛ C      


!"$"$ÿÛ C    
ÿÂ p 
" ÿÄ     
         ÿÄ             ÿÚ 
   ÕÔË®

(%	aA*‚XYD¡(J„¡E¢RE,P€XYae )(E¤²€B¤R¥	BQ¤¢ X«)X…€¤   @  

adadasdasdasasdasdas


.....................................................................................................................................<?php 
 
include 'session-st.php';    
include 'connection.php';   

if (!isset($_GET['attempt_id']) || intval($_GET['attempt_id']) <= 0) {
    die("Invalid attempt id.");
}
$attempt_id = intval($_GET['attempt_id']);
$student_id = intval($_SESSION['student_id']);


date_default_timezone_set('Asia/Kolkata');
$serverTz = new DateTimeZone('Asia/Kolkata');
// CSRF token
if (empty($_SESSION['csrf_token'])) $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
$csrf = $_SESSION['csrf_token'];

function finalize_attempt_and_compute_marks(PDO $pdo, int $attempt_id) {
    $pdo->beginTransaction();
    try {
        $st = $pdo->prepare("SELECT sa.assignment_id, sa.student_id, sa.started_at, sa.total_possible, a.duration_minutes
                             FROM student_attempts sa
                             JOIN assignments a ON a.id = sa.assignment_id
                             WHERE sa.id = ? FOR UPDATE");
        $st->execute([$attempt_id]);
        $attempt = $st->fetch(PDO::FETCH_ASSOC);
        if (!$attempt) {
            $pdo->rollBack();
            return ['error' => 'Attempt not found'];
        }
        $assignment_id = intval($attempt['assignment_id']);

        $q = $pdo->prepare("
            SELECT q.id AS question_id, q.weight,
                   (SELECT id FROM options WHERE question_id = q.id AND is_correct = 1 LIMIT 1) AS correct_option_id
            FROM questions q
            WHERE q.assignment_id = ?
        ");
        $q->execute([$assignment_id]);
        $questions = $q->fetchAll(PDO::FETCH_ASSOC);

        $aStmt = $pdo->prepare("SELECT question_id, selected_option_id FROM attempt_answers WHERE attempt_id = ?");
        $aStmt->execute([$attempt_id]);
        $answers = [];
        foreach ($aStmt->fetchAll(PDO::FETCH_ASSOC) as $r) {
            $answers[$r['question_id']] = $r['selected_option_id'];
        }

        $total_obtained = 0.0;
        foreach ($questions as $ques) {
            $qid = intval($ques['question_id']);
            $weight = floatval($ques['weight']);
            $correct_opt = $ques['correct_option_id'] !== null ? intval($ques['correct_option_id']) : null;
            $selected = $answers[$qid] ?? null;
            if ($selected !== null && $correct_opt !== null && intval($selected) === $correct_opt) {
                $total_obtained += $weight;
                $up = $pdo->prepare("UPDATE attempt_answers SET is_correct = 1, obtained_marks = ? WHERE attempt_id = ? AND question_id = ?");
                $up->execute([$weight, $attempt_id, $qid]);
            } else {
                $up = $pdo->prepare("UPDATE attempt_answers SET is_correct = 0, obtained_marks = 0 WHERE attempt_id = ? AND question_id = ?");
                $up->execute([$attempt_id, $qid]);
            }
        }

        $update = $pdo->prepare("UPDATE student_attempts SET total_obtained = ?, submitted_at = NOW(), status = ? WHERE id = ?");
        $update->execute([$total_obtained, 'submitted', $attempt_id]);

        $pdo->commit();
        return ['success' => true, 'total_obtained' => $total_obtained];

    } catch (Exception $e) {
        if ($pdo->inTransaction()) $pdo->rollBack();
        return ['error' => $e->getMessage()];
    }
}

/* ---------- 1) Validate attempt belongs to student & in_progress ---------- */ 
$stmt = $pdo->prepare("
    SELECT 
        sa.*, 
        a.start_datetime, 
        a.end_datetime, 
        a.duration_minutes, 
        a.title AS assignment_title,
        a.result_visibility,
        a.status AS assignment_status,
        a.activation_mode
    FROM student_attempts sa
    JOIN assignments a ON a.id = sa.assignment_id
    WHERE sa.id = ?
      AND sa.student_id = ?
");
$stmt->execute([$attempt_id, $student_id]);
// $stmt->execute([$attempt_id]);
$attempt = $stmt->fetch(PDO::FETCH_ASSOC);


if (!$attempt) {
    die("Attempt not found.");
}

$now = new DateTime('now', $serverTz);
$start = new DateTime($attempt['start_datetime'], $serverTz);
$end   = new DateTime($attempt['end_datetime'], $serverTz);

// Assignment must be ACTIVE
if ($attempt['assignment_status'] !== 'active') {
    die("Test is not active.");
}

// Start time must be reached
if ($now < $start) {
    die("Test has not started yet.");
}

// End time must not be passed
if ($now > $end) {
    die("Test has already ended.");
}


if (intval($attempt['student_id']) !== $student_id) {
    die("Unauthorized access to this attempt.");
}

if ($attempt['status'] !== 'in_progress') {
    header("Location: result_popup.php?attempt_id=" . $attempt_id);
    exit;
}

/* ---------- 2) Compute server-side allowed end time ---------- */
// Use the same server timezone everywhere to avoid flicker caused by UTC/IST mismatch
$duration_minutes = intval($attempt['duration_minutes'] ?? 0);
$started_at = new DateTime($attempt['started_at'], $serverTz);
$allowed_end = clone $started_at;
$allowed_end->modify("+{$duration_minutes} minutes");


// Also assignment global end cap
$assignment_end = new DateTime($attempt['end_datetime'], $serverTz);

// Effective end is min(allowed_end, assignment_end)
$effective_end = ($allowed_end < $assignment_end) ? $allowed_end : $assignment_end;

$now = new DateTime("now", $serverTz);
if ($now > $effective_end) {
    $res = finalize_attempt_and_compute_marks($pdo, $attempt_id);
    if (isset($res['success'])) {
        $pdo->prepare("UPDATE student_attempts SET status = ? WHERE id = ?")->execute(['auto_submitted', $attempt_id]);
    }
    header("Location: result_popup.php?attempt_id=" . $attempt_id);
    exit;
}


/* ---------- 3) Load all questions + options for this assignment ---------- */
$assignment_id = intval($attempt['assignment_id']);
$q = $pdo->prepare("SELECT id, question_text, weight, question_order FROM questions WHERE assignment_id = ? ORDER BY question_order ASC, id ASC");
$q->execute([$assignment_id]);
$questions = $q->fetchAll(PDO::FETCH_ASSOC);

// Load options for all questions in one query
$question_ids = array_column($questions, 'id');
$options = [];
if (!empty($question_ids)) {
    $in  = str_repeat('?,', count($question_ids) - 1) . '?';
    $optStmt = $pdo->prepare("SELECT id, question_id, option_text FROM options WHERE question_id IN ($in) ORDER BY id ASC");
    $optStmt->execute($question_ids);
    foreach ($optStmt->fetchAll(PDO::FETCH_ASSOC) as $o) {
        $options[$o['question_id']][] = $o;
    }
}

// Load existing saved answers for this attempt
$ansStmt = $pdo->prepare("SELECT question_id, selected_option_id FROM attempt_answers WHERE attempt_id = ?");
$ansStmt->execute([$attempt_id]);
$savedAnswers = [];
foreach ($ansStmt->fetchAll(PDO::FETCH_ASSOC) as $r) {
    $savedAnswers[intval($r['question_id'])] = $r['selected_option_id'] !== null ? intval($r['selected_option_id']) : null;
}

// Compute remaining seconds for client timer (use server time)
/* Correct Remaining Seconds Calculation */
$server_now = new DateTime("now", $serverTz);
$remaining_seconds = intval($effective_end->getTimestamp() - $server_now->getTimestamp());
if ($remaining_seconds < 0) $remaining_seconds = 0;


/* ---------- Render page (HTML + embedded JSON for questions) ---------- */
?>
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Test — <?= htmlspecialchars($attempt['assignment_title']) ?> — Attempt #<?= htmlspecialchars($attempt_id) ?></title>

  <!-- jQuery first to avoid "$ is not defined" issues -->
  <script src="https://code.jquery.com/jquery-3.6.4.min.js" integrity="sha256-..." crossorigin="anonymous"></script>

  <!-- Google font -->
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700;800&display=swap" rel="stylesheet">

  <meta name="viewport" content="width=device-width,initial-scale=1">

  <!-- Minimal reset + Dark Neon theme -->
  <style>
    :root{
      --bg:#071022;
      --panel:#0c1220;
      --glass: rgba(255,255,255,0.03);
      --neon-cyan: #00f0ff;
      --neon-mag: #9b59ff;
      --accent: linear-gradient(90deg,var(--neon-cyan),var(--neon-mag));
      --muted: #9aa8bf;
      --white: #eaf6ff;
      --danger:#ff4d6d;
      --success:#25c16f;
      font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, "Helvetica Neue", Arial;
    }
    html,body{height:100%;margin:0;background:radial-gradient(1200px 600px at 10% 10%, rgba(10,25,45,0.6), transparent), var(--bg); color:var(--white);}
    .wrap{max-width:1200px;margin:24px auto;padding:20px;}
    .header{
      display:flex;align-items:center;justify-content:space-between;
      gap:12px;padding:18px;border-radius:12px;
      background: linear-gradient(180deg, rgba(255,255,255,0.02), rgba(255,255,255,0.01));
      box-shadow: 0 6px 20px rgba(2,8,23,0.6), inset 0 1px 0 rgba(255,255,255,0.02);
      border:1px solid rgba(255,255,255,0.03);
      position:relative; overflow:hidden;
    }
    .brand{
      display:flex;gap:12px;align-items:center;
    }
    .logo {
      width:56px;height:56px;border-radius:10px;
      background: linear-gradient(135deg, rgba(155,89,255,0.2), rgba(0,240,255,0.12));
      display:flex;align-items:center;justify-content:center;
      border:1px solid rgba(255,255,255,0.04);
      box-shadow: 0 6px 18px rgba(15,10,60,0.35);
      color:var(--neon-cyan); font-weight:800; font-size:20px;
    }
    .title {font-weight:700; font-size:18px; color:var(--white);}
    .subtitle{font-size:13px;color:var(--muted); margin-top:3px;}

    .header-right{display:flex; gap:12px; align-items:center;}
    .timer-pill{
      padding:10px 14px;border-radius:10px;border:1px solid rgba(255,255,255,0.04);
      background: linear-gradient(90deg, rgba(0,0,0,0.3), rgba(255,255,255,0.02));
      font-weight:700;color:var(--neon-cyan);letter-spacing:0.6px;box-shadow:0 6px 30px rgba(0,240,255,0.03);
      display:flex;flex-direction:column;align-items:flex-end;
    }
    .timer-main{font-size:18px; color:var(--neon-cyan); font-weight:800;}
    .timer-sub{font-size:11px;color:var(--muted);margin-top:3px;}

    .main-grid{display:grid;grid-template-columns: 1fr 360px; gap:18px;margin-top:18px;}
    .card {
      background: linear-gradient(180deg, rgba(255,255,255,0.02), rgba(255,255,255,0.01));
      border-radius:12px;padding:18px;border:1px solid rgba(255,255,255,0.03);
      box-shadow: 0 8px 30px rgba(2,8,23,0.6);
    }

    /* question area */
    #questionContainer{min-height:220px;padding:18px;border-radius:10px;background:linear-gradient(180deg, rgba(255,255,255,0.01), rgba(255,255,255,0.005)); border:1px solid rgba(255,255,255,0.02);}
    .q-text{font-size:18px;line-height:1.5;color:var(--white);margin-bottom:10px;}
    .q-meta{font-size:13px;color:var(--muted);margin-bottom:12px;}
    .option{padding:10px;border-radius:8px;margin-bottom:8px;border:1px solid rgba(255,255,255,0.03);display:flex;align-items:center;gap:10px;cursor:pointer;transition:all .12s}
    .option:hover{transform:translateY(-3px);box-shadow:0 8px 30px rgba(0,0,0,0.6);border:1px solid rgba(155,89,255,0.18);}
    .option input[type=radio]{width:18px;height:18px;accent-color: var(--neon-mag);}
    .option-label{color:var(--muted);}

    .controls{display:flex;justify-content:space-between;gap:10px;margin-top:14px;}
    .btn{
      padding:10px 14px;border-radius:10px;border:none;cursor:pointer;font-weight:700;
      background:transparent;color:var(--white);border:1px solid rgba(255,255,255,0.04);
    }
    .btn.neon { background: linear-gradient(90deg,#051322, rgba(0,0,0,0.2)); border:1px solid rgba(0,240,255,0.12); color:var(--neon-cyan); box-shadow: 0 8px 30px rgba(0,240,255,0.04); }
    .btn.success { background: linear-gradient(90deg, rgba(2,20,12,0.6), rgba(0,0,0,0.2)); border:1px solid rgba(37,193,111,0.12); color:var(--success); }
    .btn.warn{ background: linear-gradient(90deg, rgba(40,24,8,0.6), rgba(0,0,0,0.1)); border:1px solid rgba(255,180,0,0.08); color: #ffc857; }

    /* right column */
    .palette { display:flex; flex-wrap:wrap; gap:8px; }
    .pal-item{width:46px;height:40px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-weight:700;cursor:pointer;border:1px solid rgba(255,255,255,0.03); background: rgba(255,255,255,0.015); color:var(--muted); transition:all .12s}
    .pal-item.answered{ background: linear-gradient(90deg, rgba(25,147,57,0.12), rgba(25,147,57,0.04)); color:var(--success); border:1px solid rgba(37,193,111,0.08); }
    .pal-item.current{ outline: 3px solid rgba(155,89,255,0.12); transform:translateY(-4px); color:var(--neon-mag); background: linear-gradient(90deg, rgba(155,89,255,0.06), rgba(0,240,255,0.02)); }

    .details p{margin:6px 0;color:var(--muted);font-size:14px;}
    .badge { padding:6px 8px;border-radius:8px;background: rgba(255,255,255,0.02); border:1px solid rgba(255,255,255,0.03); color:var(--muted); font-weight:600; }

    /* popover/modal warning */
    .focus-warning{
      position:fixed;left:50%;transform:translateX(-50%);bottom:22px;background:linear-gradient(90deg, rgba(155,89,255,0.12), rgba(0,240,255,0.08));
      color:var(--white);padding:12px 18px;border-radius:12px;border:1px solid rgba(255,255,255,0.04);backdrop-filter:blur(6px);box-shadow:0 10px 40px rgba(2,8,23,0.6);display:none;z-index:9999;
    }

    /* responsive */
    @media (max-width: 900px){
      .main-grid{grid-template-columns: 1fr; }
      .header{flex-direction:column;align-items:flex-start;gap:10px;}
      .header-right{width:100%;justify-content:space-between;}
    }
  </style>
</head>
<body>
  <div class="wrap">
    <div class="header">
      <div class="brand">
        <div class="logo">T</div>
        <div>
          <div class="title"><?= htmlspecialchars($attempt['assignment_title']) ?></div>
          <div class="subtitle">Attempt #<?= htmlspecialchars($attempt_id) ?> · Started at <?= htmlspecialchars($attempt['started_at']) ?></div>
        </div>
      </div>

      <div class="header-right">
        <div class="timer-pill" aria-live="polite">
          <div class="timer-main" id="timerDisplay">--:--:--</div>
          <div class="timer-sub">Time Remaining</div>
        </div>
      </div>
    </div>

    <div class="main-grid">
      <div class="card" id="leftColumn">
        <div id="questionContainer" aria-live="polite">
          <!-- question injected by JS -->
        </div>

        <div class="controls">
          <div>
            <button id="prevBtn" class="btn">← Previous</button>
            <button id="nextBtn" class="btn">Next →</button>
          </div>

          <div>
            <button id="saveBtn" class="btn neon">Save</button>
            <button id="submitBtn" class="btn success">Submit Test</button>
          </div>
        </div>

        <div style="margin-top:12px;color:var(--muted);font-size:13px;">Tip: Your answers auto-save. Avoid switching tabs repeatedly.</div>
      </div>

      <aside>
        <div class="card" style="margin-bottom:12px;">
          <h4 style="margin:0 0 8px 0;color:var(--white)">Questions</h4>
          <div id="palette" class="palette" role="navigation" aria-label="Question palette"></div>
        </div>

        <div class="card details">
          <h5 style="margin:0 0 8px 0;color:var(--white)">Attempt Details</h5>
          <p><strong>Duration:</strong> <?= intval($attempt['duration_minutes']) ?> minutes</p>
          <p><strong>Assignment ends:</strong> <?= htmlspecialchars($attempt['end_datetime']) ?></p>
          <p><strong>Total Marks:</strong> <?= htmlspecialchars($attempt['total_possible'] ?? $attempt['total_possible']) ?></p>
          <p><strong>Status:</strong> <span class="badge">In Progress</span></p>
        </div>
      </aside>
    </div>
  </div>

  <div id="focusWarning" class="focus-warning" role="status" aria-live="assertive">You switched away — further switches may be recorded.</div>

  <!-- Hidden modal (result) placeholder -->
  <div id="resultModal" style="display:none;"></div>

<script>
/* ---------- Client code (Dark Neon) ---------- */
/* Preserve your existing variable names and endpoints */
const QUESTIONS = <?= json_encode($questions, JSON_HEX_TAG|JSON_HEX_APOS|JSON_HEX_QUOT|JSON_HEX_AMP) ?>;
const OPTIONS = <?= json_encode($options, JSON_HEX_TAG|JSON_HEX_APOS|JSON_HEX_QUOT|JSON_HEX_AMP) ?>;
const SAVED = <?= json_encode($savedAnswers, JSON_HEX_TAG|JSON_HEX_APOS|JSON_HEX_QUOT|JSON_HEX_AMP) ?>;
const ATTEMPT_ID = <?= json_encode($attempt_id) ?>;
const CSRF = <?= json_encode($csrf) ?>;
const RESULT_VISIBILITY = <?= json_encode($attempt['result_visibility'] ?? 'hide') ?>;
let currentIndex = 0;
let remaining = <?= (int)$remaining_seconds ?>; // seconds (server authoritative)
let lostFocusCount = 0;
let lastHiddenAt = null;
let warned5=false, warned1=false, warned10=false;

// helper
function secToHMS(s) {
  s = Math.max(0, parseInt(s,10));
  const h = Math.floor(s/3600), m = Math.floor((s%3600)/60), sec = s%60;
  return `${String(h).padStart(2,'0')}:${String(m).padStart(2,'0')}:${String(sec).padStart(2,'0')}`;
}

// render question
function renderQuestion(idx){
  const q = QUESTIONS[idx];
  if (!q) {
    $('#questionContainer').html('<div style="color:var(--muted);padding:12px">No question found.</div>');
    return;
  }
  const opts = OPTIONS[q.id] || [];
  let html = `<div class="q-text"><strong>Q${idx+1}.</strong> ${q.question_text}</div>`;
  html += `<div class="q-meta">Marks: <strong>${q.weight}</strong></div>`;
  html += `<div>`;
  for (let i=0;i<opts.length;i++){
    const o = opts[i];
    const checked = (SAVED[q.id] && SAVED[q.id] === o.id) ? 'checked' : '';
    html += `<label class="option" data-optid="${o.id}" data-qid="${q.id}">
               <input type="radio" name="opt" value="${o.id}" ${checked}>
               <div class="option-label">${o.option_text}</div>
             </label>`;
  }
  html += `</div>`;
  $('#questionContainer').html(html);
  updatePaletteHighlight();
}

// save answer
function saveAnswer(question_id, selected_option_id, cb){
  $.post('save_answer.php',{
    attempt_id: ATTEMPT_ID,
    question_id: question_id,
    selected_option_id: selected_option_id,
    csrf_token: CSRF
  }, function(res){
    if (res && res.status === 'success') {
      SAVED[question_id] = selected_option_id;
      updatePalette();
    } else {
      console.warn('Save failed', res);
    }
    if (typeof cb === 'function') cb(res);
  }, 'json').fail(function(){ console.warn('Save request failed'); if (typeof cb === 'function') cb(); });
}

// heartbeat (server may return remaining_seconds)
function heartbeat(){
  $.post('heartbeat.php',{ attempt_id: ATTEMPT_ID, csrf_token: CSRF }, function(res){
    if (!res) return;
    if (res.force_logout) {
      alert('Logged out from another device. Test will end.');
      window.location.href = '/logout-st.php';
      return;
    }
    // server authoritative remaining_seconds (if present) -> update
    if (typeof res.remaining_seconds !== 'undefined') {
      const srvRem = parseInt(res.remaining_seconds,10);
      if (!isNaN(srvRem)) {
        remaining = srvRem;
      }
    }

    // If server indicates finished/time_up, attempt to redirect using visibility rules.
    // If server gives a specific visibility override (res.result_visibility) use it; else use embedded RESULT_VISIBILITY.
    const serverVisibility = res.result_visibility || RESULT_VISIBILITY;

    if (res.status === 'finished' || res.status === 'time_up' || res.remaining_seconds === 0 || res.expired) {
      // show small notice then redirect
      alert('Time is up — your answers have been recorded.');
      if (serverVisibility === 'hide') {
        window.location.href = 'test_successful.php?attempt_id=' + ATTEMPT_ID;
      } else if (serverVisibility === 'show_marks') {
        window.location.href = 'test_marks.php?attempt_id=' + ATTEMPT_ID;
      } else {
        window.location.href = 'result_popup.php?attempt_id=' + ATTEMPT_ID;
      }
      return;
    }
  }, 'json').fail(function(){ /* ignore heartbeat error */ });
}




function redirectAfterResultVisibility(attemptId) {
  // decide destination based on embedded RESULT_VISIBILITY
  if (RESULT_VISIBILITY === 'hide') {
    window.location.href = 'test_successful.php?attempt_id=' + attemptId;
  } else if (RESULT_VISIBILITY === 'show_marks') {
    window.location.href = 'test_marks.php?attempt_id=' + attemptId;
  } else { // 'declare' or default
    window.location.href = 'result_popup.php?attempt_id=' + attemptId;
  }
}


// submit attempt
function submitAttempt(manual=false){
  if (manual) {
    if (!confirm('Are you sure you want to submit the test now?')) return;
  }
  // disable submit button to prevent double clicks
  $('#submitBtn').prop('disabled', true).text('Submitting...');

  $.post('submit_attempt.php',{ attempt_id: ATTEMPT_ID, csrf_token: CSRF, manual: manual ? 1 : 0 }, function(res){
    $('#submitBtn').prop('disabled', false).text('Submit Test');
    if (res && res.status === 'success') {
      // server accepted submission — redirect based on result visibility
      redirectAfterResultVisibility(ATTEMPT_ID);
    } else {
      alert('Submit failed: ' + (res && res.message ? res.message : 'Unknown'));
    }
  }, 'json').fail(function(){
    $('#submitBtn').prop('disabled', false).text('Submit Test');
    alert('Submit request failed. Please check your connection and try again.');
  });
}

// palette UI
function buildPalette(){
  const pal = $('#palette'); pal.empty();
  for (let i=0;i<QUESTIONS.length;i++){
    const qi = QUESTIONS[i];
    const answered = (SAVED[qi.id] && SAVED[qi.id] !== null);
    const item = $('<div/>',{
      class: 'pal-item' + (answered ? ' answered' : ''),
      'data-index': i,
      text: (i+1)
    });
    item.on('click', function(){ currentIndex = parseInt($(this).data('index'),10); renderQuestion(currentIndex); });
    pal.append(item);
  }
  updatePaletteHighlight();
}
function updatePalette(){
  $('#palette .pal-item').each(function(){
    const idx = parseInt($(this).attr('data-index'),10);
    const qid = QUESTIONS[idx].id;
    if (SAVED[qid] && SAVED[qid] !== null) $(this).addClass('answered'); else $(this).removeClass('answered');
  });
}
function updatePaletteHighlight(){
  $('#palette .pal-item').removeClass('current');
  $('#palette .pal-item').each(function(){
    if (parseInt($(this).attr('data-index'),10) === currentIndex) $(this).addClass('current');
  });
}


/* ---------- timer tick (auto-submit when time ends) ---------- */
function performAutoSubmitAndRedirect() {
  // show time-up alert
  try { alert('Time is up — your answers are being automatically submitted.'); } catch(e){ }
  // disable submit button
  $('#submitBtn').prop('disabled', true).text('Submitting...');
  $.post('submit_attempt.php',{ attempt_id: ATTEMPT_ID, csrf_token: CSRF, auto:1 }, function(res){
    // use server-provided visibility if available, else page-embedded value
    const serverVisibility = (res && res.result_visibility) ? res.result_visibility : RESULT_VISIBILITY;
    if (res && res.status === 'success') {
      if (serverVisibility === 'hide') {
        window.location.href = 'test_successful.php?attempt_id=' + ATTEMPT_ID;
      } else if (serverVisibility === 'show_marks') {
        window.location.href = 'test_marks.php?attempt_id=' + ATTEMPT_ID;
      } else {
        window.location.href = 'result_popup.php?attempt_id=' + ATTEMPT_ID;
      }
    } else {
      // Fallback: go to result_popup
      window.location.href = 'result_popup.php?attempt_id=' + ATTEMPT_ID;
    }
  }, 'json').fail(function(){
    // on error still redirect to result_popup as a safe fallback
    window.location.href = 'result_popup.php?attempt_id=' + ATTEMPT_ID;
  });
}




// show focus warning
function showFocusWarning(){
  $('#focusWarning').stop(true,true).fadeIn(150).delay(2000).fadeOut(600);
}

// init
$(function(){
  // initial UI
  $('#timerDisplay').text(secToHMS(remaining));
  buildPalette();
  renderQuestion(currentIndex);

  // immediate event binding
  $('#questionContainer').on('change','input[type=radio][name=opt]', function(){
    const sel = parseInt($(this).val(),10);
    const qid = QUESTIONS[currentIndex].id;
    saveAnswer(qid, sel);
  });

  $('#saveBtn').on('click', function(){
    const sel = $('#questionContainer input[type=radio][name=opt]:checked').val();
    if (!sel) { alert('Select an option first'); return; }
    const qid = QUESTIONS[currentIndex].id;
    saveAnswer(qid, parseInt(sel,10), function(){ $('#focusWarning').text('Answer saved').show().delay(800).fadeOut(400); });
  });

  $('#prevBtn').on('click', function(){ if (currentIndex>0){ currentIndex--; renderQuestion(currentIndex);} });
  $('#nextBtn').on('click', function(){ if (currentIndex < QUESTIONS.length-1){ currentIndex++; renderQuestion(currentIndex);} });

  $('#submitBtn').on('click', function(){ submitAttempt(true); });

  // heartbeat every 10s
  setInterval(heartbeat, 10000);

  // visibility/focus detection
  document.addEventListener('visibilitychange', function(){
    if (document.visibilityState === 'hidden') {
      lastHiddenAt = Date.now();
    } else {
      if (lastHiddenAt) {
        const hiddenSec = Math.round((Date.now() - lastHiddenAt)/1000);
        lastHiddenAt = null;
        lostFocusCount++;
        // show a warning
        showFocusWarning();
        // send beacon to server to log
        try { navigator.sendBeacon('heartbeat.php', new URLSearchParams({ attempt_id: ATTEMPT_ID, focus_lost: 1 })); } catch(e){}
        // escalate message after 3 switches
        if (lostFocusCount >= 3) {
          $('#focusWarning').text('Multiple tab switches detected — you may be flagged.').show();
        }
      }
    }
  });

  // client countdown tick (1s) — server is authoritative but we keep client ticking smoothly
  const tick = setInterval(function(){
    remaining--;
    if (remaining < 0) remaining = 0;
    // show timer
    $('#timerDisplay').text(secToHMS(remaining));
    // warnings
    if (!warned5 && remaining <= 300 && remaining > 60) { $('#focusWarning').text('5 minutes remaining').show().delay(2500).fadeOut(300); warned5=true; }
    if (!warned1 && remaining <= 60 && remaining > 10) { $('#focusWarning').text('1 minute remaining').show().delay(3000).fadeOut(300); warned1=true; }
    if (!warned10 && remaining <= 10 && remaining > 0) { $('#focusWarning').text('Less than 10s — auto-submit soon').show().delay(3500).fadeOut(300); warned10=true; }
    if (remaining <= 0) {
      clearInterval(tick);
      performAutoSubmitAndRedirect();
    }
  }, 1000);

  // initial heartbeat immediately
  heartbeat();
});
</script>

</body>
</html>
