/**
 * @fileoverview Perfil de detecção: Shell Sort.
 *
 * Invariantes obrigatórios:
 *   1. Presença de pelo menos um par consecutivo (read, write) no trace onde
 *      |write.i - read.i| > 1. Isso ocorre porque, nas passadas com gap > 1,
 *      o argumento da escrita vm.write(j, vm.read(j-gap)) gera read(j-gap)
 *      imediatamente seguido de write(j), com distância = gap.
 *
 * Por que isso distingue Shell Sort:
 *   - Insertion Sort shift: todos os pares (read, write) têm delta = 1 (gap=1).
 *   - Bubble/Selection: usam compare+swap, não read-write adjacentes.
 *   - Shell Sort: passes iniciais com gap > 1 geram pares com delta > 1.
 *
 * Variações aceitas:
 *   - Qualquer sequência de gaps: n/2, Hibbard, Knuth, etc.
 *   - Shift-based (write) ou swap-based (swap com gap > 1 também aceito).
 *
 * O que NÃO é aceito:
 *   - Ausência de deslocamentos com gap > 1 (todos delta = 1 → provavelmente Insertion Sort).
 */

/**
 * @param {import("../core/ExecutionTrace.js").TraceEvent[]} trace
 * @param {number} n - Tamanho da lista.
 * @returns {{ match: boolean, failReason?: string }}
 */
export function shellProfile(trace, n) {
  const writes = trace.filter(e => e.type === "write");
  const reads  = trace.filter(e => e.type === "read");

  if (writes.length === 0 && reads.length === 0) {
    return {
      match: false,
      failReason:
        "Nenhuma leitura ou escrita detectada. " +
        "O Shell Sort deve comparar e deslocar elementos com gap = tamanho/2 nas primeiras passadas.",
    };
  }

  // Detectar pares consecutivos (read(p), write(q)) com |q - p| > 1
  // Isso ocorre no Shell Sort quando vm.write(j, vm.read(j-gap)) gera read(j-gap) → write(j).
  let nonAdjacentShifts = 0;
  for (let k = 0; k < trace.length - 1; k++) {
    const curr = trace[k];
    const next = trace[k + 1];
    if (curr.type === "read" && next.type === "write") {
        // Shell Sort: vm.write(j, vm.read(j-gap)) → write.i = j, read.i = j-gap
        // Portanto SEMPRE write.i > read.i (shift para a direita com gap > 0).
        // No Counting Sort existe um par espúrio (read(n-1), write(0)) onde
        // write.i < read.i — esse é descartado pela condição next.i > curr.i.
        if (next.i - curr.i > 1) nonAdjacentShifts++;
    }
  }

  // Também aceitar swap não adjacente (variante swap-based Shell Sort)
  const nonAdjacentSwaps = trace.filter(
    e => e.type === "swap" && Math.abs(e.i - e.j) > 1
  );

  if (nonAdjacentShifts === 0 && nonAdjacentSwaps.length === 0) {
    return {
      match: false,
      failReason:
        "Todos os deslocamentos e trocas foram entre posições adjacentes (gap = 1). " +
        "O Shell Sort começa com gaps grandes (gap = tamanho/2) e os reduz progressivamente. " +
        "Certifique-se de usar a variável de gap nas comparações: leia arr[j-gap] e escreva em arr[j].",
    };
  }

  return { match: true };
}
