/*
Outils => Fonctions du zoom SUPPRIME
Outils => Fonctions du plein écran
Outils => Fonctions des modales annexes
Outils => Fonctions du rideau
Outils => Fonctions de la lucarne
Outils => Fonctions de la grille dynamique
Outils => Fonctions des widgets
Outils => Fonctions génériques
*/

/*******************************************/
/* VARIABLES                               */
/*******************************************/

let synth = window.speechSynthesis;
const espacesInsecables = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"

/*****************************************/
/* Outils => Fonctions du plein écran    */
/*****************************************/

/* Passer en plein écran */
function pleinEcran() {
  //const modal = document.querySelector('#outils_modal .modal-dialog');
  const zonePE = document.querySelector('body');

  if (!document.fullscreenElement &&
      !document.webkitFullscreenElement &&
      !document.mozFullScreenElement &&
      !document.msFullscreenElement) {

    if (zonePE.requestFullscreen) {
      zonePE.requestFullscreen();
    } else if (zonePE.webkitRequestFullscreen) {
      zonePE.webkitRequestFullscreen();
    } else if (zonePE.mozRequestFullScreen) {
      zonePE.mozRequestFullScreen();
    } else if (zonePE.msRequestFullscreen) {
      zonePE.msRequestFullscreen();
    } else {
      alert("Plein écran non supporté par ce navigateur.");
    }

  } else {
    if (document.exitFullscreen) {
      document.exitFullscreen();
    } else if (document.webkitExitFullscreen) {
      document.webkitExitFullscreen();
    } else if (document.mozCancelFullScreen) {
      document.mozCancelFullScreen();
    } else if (document.msExitFullscreen) {
      document.msExitFullscreen();
    }
  }
}

/* Gérer le plein écran ou pas */
function mettreAJourBoutonPleinEcran() {
  const btn = $('#toggleFullscreen');
  const icon = btn.find('i');

  if (document.fullscreenElement ||
      document.webkitFullscreenElement ||
      document.mozFullScreenElement ||
      document.msFullscreenElement) {

    btn.attr('title', 'Quitter le plein écran');
    icon.removeClass('bi-arrows-fullscreen').addClass('bi-fullscreen-exit');
  } else {
    btn.attr('title', 'Afficher en plein écran');
    icon.removeClass('bi-fullscreen-exit').addClass('bi-arrows-fullscreen');
  }
  // Ajouter dans doc.ready : $(document).on('fullscreenchange webkitfullscreenchange mozfullscreenchange MSFullscreenChange', mettreAJourBoutonPleinEcran);
}

/********************************************/
/* Outils => Fonctions de la copie d'écran  */
/********************************************/

function copierEcran(id) {
  const element = document.getElementById(id);
  if (!element) {
    console.error(`Élément avec l'id "${id}" introuvable.`);
    return;
  }

  html2canvas(element).then(canvas => {
    // Création d’un lien de téléchargement
    const image = canvas.toDataURL("image/png");
    const link = document.createElement("a");
    link.href = image;
    //link.download = "capture.png";
    link.download = "pragmatni-"+afficherDateEnr()+".png";
    link.click();
  }).catch(error => {
    console.error("Erreur lors de la capture :", error);
  });
}

/********************************************/
/* Outils => Fonctions du rideau            */
/********************************************/

function ouvrirRideau() {
  const $rideau = $('#rideau');
  $rideau.show();

  // Cache la navbar
  $("#outils_modal-header").css("visibility","hidden");

  // Initialiser l'opacité
  const range = document.getElementById('opacityRange');
  $rideau.css('background-color', `rgba(0, 0, 0, ${range.value})`);

  // Redimensionnement
  let isResizing = false;
  let direction = '';
  let startX, startY, startWidth, startHeight;

  const updateSize = (e) => {
    if (!isResizing) return;

    const clientX = e.clientX ?? e.touches?.[0]?.clientX;
    const clientY = e.clientY ?? e.touches?.[0]?.clientY;
    const windowWidth = window.innerWidth;
    const windowHeight = window.innerHeight;

    if (direction === 'top') {
      let deltaY = clientY - startY;
      let newTop = startY + deltaY;
      let newHeight = startHeight - deltaY;

      // Ne pas dépasser le haut de l'écran
      if (newTop < 0) {
        newTop = 0;
        newHeight = startHeight + startY;
      }

      $('#rideau').css('top', `${newTop}px`);
      $('#rideau').css('height', `${newHeight}px`);
    }

    else if (direction === 'bottom') {
      let deltaY = clientY - startY;
      let newHeight = startHeight + deltaY;
      const maxHeight = windowHeight - parseFloat($('#rideau').css('top'));
      newHeight = Math.min(newHeight, maxHeight);
      $('#rideau').css('height', `${newHeight}px`);
    }

    else if (direction === 'left') {
      let deltaX = clientX - startX;
      let newLeft = startX + deltaX;
      let newWidth = startWidth - deltaX;

      if (newLeft < 0) {
        newLeft = 0;
        newWidth = startWidth + startX;
      }

      $('#rideau').css('left', `${newLeft}px`);
      $('#rideau').css('width', `${newWidth}px`);
    }

    else if (direction === 'right') {
      let deltaX = clientX - startX;
      let newWidth = startWidth + deltaX;
      const maxWidth = windowWidth - parseFloat($('#rideau').css('left'));
      newWidth = Math.min(newWidth, maxWidth);
      $('#rideau').css('width', `${newWidth}px`);
    }
  };

  const stopResize = () => {
    isResizing = false;
    direction = '';
    document.removeEventListener('mousemove', updateSize);
    document.removeEventListener('mouseup', stopResize);
    document.removeEventListener('touchmove', updateSize);
    document.removeEventListener('touchend', stopResize);
    document.removeEventListener('pointermove', updateSize);
    document.removeEventListener('pointerup', stopResize);
  };

  $('.rideau_handle').on('mousedown touchstart pointerdown', function (e) {
    e.preventDefault();
    isResizing = true;
    direction = $(this).attr('class').split('rideau_handle-')[1].split(' ')[0];

    const rect = $rideau[0].getBoundingClientRect();
    startX = e.clientX ?? e.touches?.[0]?.clientX;
    startY = e.clientY ?? e.touches?.[0]?.clientY;
    startWidth = rect.width;
    startHeight = rect.height;

    document.addEventListener('mousemove', updateSize);
    document.addEventListener('mouseup', stopResize);
    document.addEventListener('touchmove', updateSize, { passive: false });
    document.addEventListener('touchend', stopResize);
    document.addEventListener('pointermove', updateSize);
    document.addEventListener('pointerup', stopResize);
  });
}

function resetRideau() {
  const rideau = document.getElementById('rideau');
  rideau.style.top = '0';
  rideau.style.left = '0';
  rideau.style.width = '100%';
  rideau.style.height = '100%';
  document.getElementById('opacityRange').value = 1;
  rideau.style.backgroundColor = 'rgba(0, 0, 0, 1)';
}

function fermerRideau() {
  document.getElementById('rideau').style.display = 'none';
  $("#outils_modal-header").css("visibility","visible");
}

// Mise à jour de l'opacité avec le slider
document.addEventListener('DOMContentLoaded', () => {
  const range = document.getElementById('opacityRange');
  const rideau = document.getElementById('rideau');

  range.addEventListener('input', () => {
    rideau.style.backgroundColor = `rgba(0, 0, 0, ${range.value})`;
  });
});

/********************************************/
/* Outils => Fonctions de la lucarne        */
/********************************************/

let hole;
let lucarneIsRound = false;
const shapeBtn = document.getElementById('lucarne_shapeBtn');
const maskRect = document.getElementById('maskRect');
const maskEllipse = document.getElementById('maskEllipse');
const svgMask = document.getElementById('lucarne_mask');
const opacitySlider = document.getElementById('lucarne_opacity');
const maskOverlay = svgMask.querySelector('rect[mask]'); // le fond noir avec opacity

opacitySlider.addEventListener('input', () => {
  const val = parseInt(opacitySlider.value, 10) / 100;
  maskOverlay.setAttribute('opacity', val.toString());
});
svgMask.style.display = 'none';

shapeBtn.onclick = () => {
  lucarneIsRound = !lucarneIsRound;
  hole.classList.toggle('lucarne_round', lucarneIsRound);
  shapeBtn.textContent = (lucarneIsRound ? 'Rond -> Carré' : 'Carré -> Rond');
  lucarneUpdateMask();
};

function lucarneBuild() {
  // Hole
  hole = document.createElement('div');
  hole.id = 'lucarne_hole';
  hole.style.left = 'calc(50% - 75px)';
  hole.style.top = 'calc(50% - 75px)';
  hole.innerHTML = '<div class="lucarne_resize-handle"></div>';
  document.body.appendChild(hole);

  if (lucarneIsRound) hole.classList.add('lucarne_round');

  requestAnimationFrame(() => {
    lucarneUpdateMask();
  });

  svgMask.style.display = 'block';

  // Drag
  let drag = false, offset = {};
  hole.addEventListener('pointerdown', e => {
    if (e.target.classList.contains('lucarne_resize-handle')) return;
    drag = true;
    offset.x = e.clientX - hole.offsetLeft;
    offset.y = e.clientY - hole.offsetTop;
  });
  document.addEventListener('pointermove', e => {
    if (!drag) return;
    hole.style.left = (e.clientX - offset.x) + 'px';
    hole.style.top = (e.clientY - offset.y) + 'px';
    lucarneUpdateMask();
  });
  document.addEventListener('pointerup', () => drag = false);

  // Resize
  let resizing = false, start = {}, size = {};
  const handle = hole.querySelector('.lucarne_resize-handle');
  handle.addEventListener('pointerdown', e => {
    resizing = true;
    start.x = e.clientX;
    start.y = e.clientY;
    size.w = hole.offsetWidth;
    size.h = hole.offsetHeight;
    e.stopPropagation();
  });
  document.addEventListener('pointermove', e => {
    if (!resizing) return;
    const w = Math.max(50, size.w + (e.clientX - start.x));
    const h = Math.max(50, size.h + (e.clientY - start.y));
    hole.style.width = w + 'px';
    hole.style.height = h + 'px';
    lucarneUpdateMask();
  });
  document.addEventListener('pointerup', () => resizing = false);

  shapeBtn.style.display = 'inline-block';
  opacitySlider.style.display = 'inline-block';
}

function lucarneDestroy() {
  hole.remove();
  hole = null;
  svgMask.style.display = 'none';
  shapeBtn.style.display = 'none';
  opacitySlider.style.display = 'none';
}

function lucarneUpdateMask() {
  const holeRect = hole.getBoundingClientRect();
  const maskRectSVG = svgMask.getBoundingClientRect(); // svg position réelle

  const x = holeRect.left - maskRectSVG.left;
  const y = holeRect.top - maskRectSVG.top;
  const w = holeRect.width;
  const h = holeRect.height;

  if (lucarneIsRound) {
    maskEllipse.setAttribute('cx', x + w / 2);
    maskEllipse.setAttribute('cy', y + h / 2);
    maskEllipse.setAttribute('rx', w / 2);
    maskEllipse.setAttribute('ry', h / 2);
    maskEllipse.style.display = 'block';
    maskRect.style.display = 'none';
  } else {
    maskRect.setAttribute('x', x);
    maskRect.setAttribute('y', y);
    maskRect.setAttribute('width', w);
    maskRect.setAttribute('height', h);
    maskRect.style.display = 'block';
    maskEllipse.style.display = 'none';
  }
}

// Gestion de l'ouverture et de la fermeture de la lucarne
$('#outils_bt_lucarne').on('click', function () {
  lucarneBuild();
  $('#lucarne_bt_fermer').show();
  $('#lucarne_controls').show();
  $("#outils_modal-header").css("visibility","hidden");
});
$('#lucarne_bt_fermer').on('click', function () {
  lucarneDestroy();
  $('#lucarne_bt_fermer').hide();
  $('#lucarne_controls').hide();
  $("#outils_modal-header").css("visibility","visible");
});
    
/**********************************************/
/* Outils => Fonctions de la grille dynamique */
/**********************************************/

let grilleRows = 3, grilleCols = 3;
let grilleCellWidths = Array(grilleCols).fill(window.innerWidth / grilleCols);
let cellHeights = Array(grilleRows).fill((window.innerHeight - 48) / grilleRows);
let grilleActiveCell = null;
let grilleCellData = {};
let grilleResizing = false, resizeCol = -1, resizeRow = -1, startX, startY;
let grilleLastTap = 0;

$('#grille_contenu').on('pointermove', function (e) {
  const rect = this.getBoundingClientRect();
  const x = e.clientX - rect.left;
  const y = e.clientY - rect.top;

  if (nearColBorder(x) >= 0) {
    this.style.cursor = 'ew-resize';
  } else if (nearRowBorder(y) >= 0) {
    this.style.cursor = 'ns-resize';
  } else {
    this.style.cursor = 'default';
  }

  function nearColBorder(x) {
    let pos = 0;
    for (let i = 0; i < grilleCellWidths.length - 1; i++) {
      pos += grilleCellWidths[i];
      if (Math.abs(x - pos) < 6) return i;
    }
    return -1;
  }

  function nearRowBorder(y) {
    let pos = 0;
    for (let i = 0; i < cellHeights.length - 1; i++) {
      pos += cellHeights[i];
      if (Math.abs(y - pos) < 6) return i;
    }
    return -1;
  }
});

$('#grille_contenu').on('pointerdown', function (e) {
  const rect = this.getBoundingClientRect();
  const x = e.clientX - rect.left;
  const y = e.clientY - rect.top;

  resizeCol = getColAtBorder(x);
  resizeRow = getRowAtBorder(y);

  if (resizeCol >= 0 || resizeRow >= 0) {
    grilleResizing = true;
    startX = e.clientX;
    startY = e.clientY;
    e.preventDefault();
  }

  function getColAtBorder(x) {
    let pos = 0;
    for (let i = 0; i < grilleCellWidths.length - 1; i++) {
      pos += grilleCellWidths[i];
      if (Math.abs(x - pos) < 6) return i;
    }
    return -1;
  }

  function getRowAtBorder(y) {
    let pos = 0;
    for (let i = 0; i < cellHeights.length - 1; i++) {
      pos += cellHeights[i];
      if (Math.abs(y - pos) < 6) return i;
    }
    return -1;
  }
});

$(document).on('pointermove', function (e) {
  if (!grilleResizing) return;
  saveCellData();
  if (resizeCol >= 0) {
    const dx = e.clientX - startX;
    grilleCellWidths[resizeCol] += dx;
    grilleCellWidths[resizeCol + 1] -= dx;
    startX = e.clientX;
  }
  if (resizeRow >= 0) {
    const dy = e.clientY - startY;
    cellHeights[resizeRow] += dy;
    cellHeights[resizeRow + 1] -= dy;
    startY = e.clientY;
  }
  grilleRenderGrid();
});

$(document).on('pointerup', function () {
  grilleResizing = false;
  resizeCol = -1;
  resizeRow = -1;
});

$('#grille_contenu').on('click touchend', '.grille_cell', function (e) {
  const now = new Date().getTime();
  const timeSince = now - grilleLastTap;
  if (timeSince < 600 && timeSince > 0) {
    grilleActiveCell = this;
    const title = $(grilleActiveCell).text();
    $('#grille_titre_cellule').val(title);
    const modal = new bootstrap.Modal(document.getElementById('grille_modal_options'));
    const lamodale = document.getElementById("grille_modal_options");
    const linput = document.getElementById("grille_titre_cellule");
    lamodale.addEventListener('shown.bs.modal', () => {
      linput.focus()
    })
    modal.show();
    //$("#customBackdrop").addClass("show").show();
  }
  grilleLastTap = now;
});

$('#grille_colorPicker').on('click touchend', '.grille_color-picker', function () {
  if (grilleActiveCell) {
    const row = $(grilleActiveCell).data('row');
    const col = $(grilleActiveCell).data('col');
    const key = `${row}-${col}`;
    const bg = $(this).data('color');
    const title = $('#grille_titre_cellule').val();
    $(grilleActiveCell).css('background', bg);
    $(grilleActiveCell).text(title);
    grilleCellData[key] = { title, bg };
    bootstrap.Modal.getInstance(document.getElementById('grille_modal_options')).hide();
  }
});

$('#grille_bt_valider_titre').click(() => {
  if (grilleActiveCell) {
    const row = $(grilleActiveCell).data('row');
    const col = $(grilleActiveCell).data('col');
    const key = `${row}-${col}`;
    const bg = $(grilleActiveCell).css('background-color'); // conserver la couleur existante
    const title = $('#grille_titre_cellule').val(); // prendre le nouveau titre
    $(grilleActiveCell).text(title); // mettre à jour le texte
    grilleCellData[key] = { title, bg }; // sauvegarder les données
  }
});

$('#grille_bt_ajouter_colonne').click(() => {
  if (!grilleActiveCell) return;
  const colIndex = parseInt($(grilleActiveCell).data('col'));
  saveCellData();
  grilleCols++;
  grilleCellWidths.splice(colIndex + 1, 0, 0); // temporaire
  grilleNormalizeCellSizes();
  grilleRenderGrid();
});

$('#grille_bt_ajouter_ligne').click(() => {
  if (!grilleActiveCell) return;
  const rowIndex = parseInt($(grilleActiveCell).data('row'));
  saveCellData();
  grilleRows++;
  cellHeights.splice(rowIndex + 1, 0, 0); // temporaire
  grilleNormalizeCellSizes();
  grilleRenderGrid();
});

$('#grille_bt_supprimer_colonne').click(() => {
  if (grilleCols <= 1 || !grilleActiveCell) return;
  const colIndex = parseInt($(grilleActiveCell).data('col'));
  saveCellData();
  grilleCellWidths.splice(colIndex, 1);
  grilleCols--;
  grilleNormalizeCellSizes();
  grilleRenderGrid();
});

$('#grille_bt_supprimer_ligne').click(() => {
  if (grilleRows <= 1 || !grilleActiveCell) return;
  const rowIndex = parseInt($(grilleActiveCell).data('row'));
  saveCellData();
  cellHeights.splice(rowIndex, 1);
  grilleRows--;
  grilleNormalizeCellSizes();
  grilleRenderGrid();
});

$('#grille_bt_exporter_json').click(() => {
  const gridData = {
    grilleCols,
    grilleRows,
    grilleCellWidths,
    cellHeights,
    cells: []
  };
  $('#grille_contenu .grille_cell').each(function () {
    const row = $(this).data('row');
    const col = $(this).data('col');
    const key = `${row}-${col}`;
    gridData.cells.push({
      row,
      col,
      bg: $(this).css('background-color'),
      title: $(this).text()
    });
  });
  const json = JSON.stringify(gridData, null, 2);
  const blob = new Blob([json], { type: 'application/json' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = "grille-"+afficherDateEnr()+".json";
  a.click();
  URL.revokeObjectURL(url);
  bootstrap.Modal.getInstance(document.getElementById('grille_modal_options')).hide(); // fermer la modale
});

$('#grille_input_importer_json').change(function (e) {
  const file = e.target.files[0];
  if (!file) return;
  const reader = new FileReader();
  reader.onload = function (evt) {
    try {
      const data = JSON.parse(evt.target.result);
      if (!data.grilleCols || !data.grilleRows || !data.grilleCellWidths || !data.cellHeights || !data.cells) {
        alert("Fichier JSON invalide.");
        return;
      }
      grilleCols = data.grilleCols;
      grilleRows = data.grilleRows;
      grilleCellWidths = data.grilleCellWidths;
      cellHeights = data.cellHeights;
      grilleCellData = {};
      data.cells.forEach(cell => {
        const key = `${cell.row}-${cell.col}`;
        grilleCellData[key] = {
          title: cell.title,
          bg: cell.bg
        };
      });
      grilleNormalizeCellSizes();
      grilleRenderGrid();
      bootstrap.Modal.getInstance(document.getElementById('grille_modal_options')).hide(); // fermer la modale
    } catch (err) {
      alert("Erreur lors de l'importation du fichier JSON.");
      console.error(err);
    }
  };
  reader.readAsText(file);
});

$('#grille_bt_importer_json').click(() => {
  $('#grille_input_importer_json').click();
});

$('#resetSizes').click(() => {
  grilleNormalizeCellSizes();
  grilleRenderGrid();
});

$('#grille_bt_supprimer_titres').click(() => {
  $('.grille_cell').html("");
});

function saveCellData() {
  $('#grille_contenu .grille_cell').each(function () {
    const row = $(this).data('row');
    const col = $(this).data('col');
    const key = `${row}-${col}`;
    grilleCellData[key] = {
      title: $(this).text(),
      bg: $(this).css('background-color')
    };
  });
}

function grilleNormalizeCellSizes() {
  const availableHeight = window.innerHeight - 48;
  const availableWidth = window.innerWidth;
  const newHeight = availableHeight / grilleRows;
  const newWidth = availableWidth / grilleCols;
  cellHeights = Array(grilleRows).fill(newHeight);
  grilleCellWidths = Array(grilleCols).fill(newWidth);
}

function grilleRenderGrid() {
  const grid = $('#grille_contenu');
  grid.empty();

  const colSizes = grilleCellWidths.map(w => `${w}px`).join(' ');
  const rowSizes = cellHeights.map(h => `${h}px`).join(' ');

  grid.css('grid-template-columns', colSizes);
  grid.css('grid-template-rows', rowSizes);

  for (let r = 0; r < grilleRows; r++) {
    for (let c = 0; c < grilleCols; c++) {
      const key = `${r}-${c}`;
      const cell = $(`<div class="grille_cell" data-row="${r}" data-col="${c}"></div>`);
      if (grilleCellData[key]) {
        cell.text(grilleCellData[key].title || '');
        cell.css('background-color', grilleCellData[key].bg || '');
      }
      grid.append(cell);
    }
  }
}

/*******************************************/
/* Outils => Fonctions des widgets         */
/*******************************************/

let topZ = 1000;
const getNextZIndex = () => ++topZ;

/*
$('#modalFullscreen').on('shown.bs.modal', () => {
  $('#box-container').empty();
  creerEtiquette("note1", ["txt-move", "txt-resize"], 100, 100, "Ceci est une étiquette", null, true);
  creerEtiquette("note2", ["txt-move"], 150, 150, "Restera à l’arrière", null, false);
  creerImage("img1", ["txt-move", "txt-resize"], 200, 200, "https://via.placeholder.com/150", "Image 1", 150, 150, null, true);
  creerImage("img2", ["txt-move", "txt-resize"], 400, 200, "https://via.placeholder.com/150", "Image 2 false", 150, 150, null, false);
  creerBox('box4', 300, 200, ['box-move'], 'Nouvelle Box', 100, 100, null, null, true);
  creerBox('box5', 300, 200, ['box-move'], 'Box bloquée', 100, 100, null, null, false);
  creerImage("img3", ["txt-move", "txt-resize"], 0, 0, "https://via.placeholder.com/150", "Image 3", 50, 50, null, true, $('#box5 .box-content'));
});
*/

function creerEtiquette(id, classes = [], x = 50, y = 50, html = '', zIndex = null, bringToFrontOnInteract = true, container = $('#box-container')) {
  $('#' + id).remove();

  const $el = $('<div>', {
    id,
    class: `txt-widget ${classes.join(' ')}`,
    html,
    css: {
      top: y,
      left: x,
      zIndex: zIndex ?? getNextZIndex()
    }
  });

  $el.attr('data-bring-front', bringToFrontOnInteract ? '1' : '0');

  const $editBtn = $('<span>', {
    class: 'edit-btn',
    html: '<i class="bi bi-pencil-square"></i>',
    title: 'Éditer'
  });

  $el.append($editBtn);
  container.append($el);

  let isDragging = false, isResizing = false;
  let offsetX = 0, offsetY = 0;
  let startX = 0, startY = 0, startW = 0, startH = 0;
  const resizeZone = 20;

  function getPoint(e) {
    return e.touches?.[0] || e;
  }

  function bringToFront() {
    if ($el.attr('data-bring-front') === '1') {
      $el.css('z-index', getNextZIndex());
    }
  }

  function onStart(e) {
    const point = getPoint(e);
    const rect = $el[0].getBoundingClientRect();
    const inResize = classes.includes('txt-resize') &&
      point.clientX >= rect.right - resizeZone &&
      point.clientY >= rect.bottom - resizeZone;

    bringToFront();

    if (inResize) {
      isResizing = true;
      startX = point.pageX;
      startY = point.pageY;
      startW = $el.outerWidth();
      startH = $el.outerHeight();
      $el.addClass('txt_cursor-se-resize');
    } else if (classes.includes('txt-move')) {
      isDragging = true;
      const offset = $el.offset();
      offsetX = point.pageX - offset.left;
      offsetY = point.pageY - offset.top;
      $el.addClass('txt_cursor-move');
    }

    $(document).on('pointermove.etiquette-' + id, onMove);
    $(document).on('pointerup.etiquette-' + id, onEnd);
  }

  function onMove(e) {
    const point = getPoint(e);
    const containerOffset = container.offset();
    const containerW = container.innerWidth();
    const containerH = container.innerHeight();

    if (isDragging) {
      let newLeft = point.pageX - containerOffset.left - offsetX;
      let newTop = point.pageY - containerOffset.top - offsetY;

      newLeft = Math.max(0, Math.min(containerW - $el.outerWidth(), newLeft));
      newTop = Math.max(0, Math.min(containerH - $el.outerHeight(), newTop));

      $el.css({ left: newLeft, top: newTop });
    }

    if (isResizing) {
      const dx = point.pageX - startX;
      const dy = point.pageY - startY;
      $el.css({
        width: Math.max(30, startW + dx),
        height: Math.max(20, startH + dy)
      });
    }
  }

  function onEnd() {
    isDragging = false;
    isResizing = false;
    $el.removeClass('txt_cursor-move txt_cursor-se-resize');
    $(document).off('.etiquette-' + id);
  }

  function onDelete() {
    $(document).off('.etiquette-' + id);
    $el.remove();
  }

  $editBtn.on('click', (e) => {
    e.stopPropagation();
    $el.attr('contenteditable', 'true').focus();
  });

  $el.on('blur keydown', (e) => {
    if (e.type === 'blur' || (e.type === 'keydown' && e.key === 'Enter')) {
      e.preventDefault();
      $el.removeAttr('contenteditable');
    }
  });

  $el.on('pointerdown', onStart);
  $el.on('dblclick', onDelete);
}

function creerImage(id, classes = [], x = 50, y = 50, src = '', alt = '', width = 100, height = 100, zIndex = null, bringToFrontOnInteract = true, container = $('#box-container')) {
  $('#' + id).remove();

  const $el = $('<div>', {
    id,
    class: `txt-widget ${classes.join(' ')}`,
    css: { top: y, left: x, zIndex: zIndex ?? getNextZIndex(), width, height, padding: 0 }
  });

  $el.attr('data-bring-front', bringToFrontOnInteract ? '1' : '0');

  const $img = $('<img>', {
    src,
    alt,
    css: { width: '100%', height: '100%', pointerEvents: 'none' },
    draggable: false
  });

  const $copyBtn = $('<span>', {
    class: 'edit-btn',
    html: '<i class="bi bi-copy"></i>',
    title: 'Dupliquer'
  });

  $copyBtn.on('click', (e) => {
    e.stopPropagation();
    const randId = id + '_' + Math.floor(Math.random() * 10000);
    const pos = $el.position();
    creerImage(randId, classes, pos.left + 10, pos.top + 10, src, alt, $el.width(), $el.height(), null, bringToFrontOnInteract);
  });

  $el.append($img, $copyBtn);
  container.append($el);

  let isDragging = false, isResizing = false;
  let offsetX = 0, offsetY = 0;
  let startX = 0, startY = 0, startW = 0, startH = 0;
  const resizeZone = 20;

  function getPoint(e) { return e.touches?.[0] || e; }

  function bringToFront() {
    if ($el.attr('data-bring-front') === '1') {
      $el.css('z-index', getNextZIndex());
    }
  }

  function onStart(e) {
    const point = getPoint(e);
    bringToFront();
    const rect = $el[0].getBoundingClientRect();
    const inResize = classes.includes('txt-resize') && point.clientX >= rect.right - resizeZone && point.clientY >= rect.bottom - resizeZone;

    if (inResize) {
      isResizing = true;
      startX = point.pageX;
      startY = point.pageY;
      startW = $el.outerWidth();
      startH = $el.outerHeight();
      $el.addClass('txt_cursor-se-resize');
    } else if (classes.includes('txt-move')) {
      isDragging = true;
      const offset = $el.offset();
      offsetX = point.pageX - offset.left;
      offsetY = point.pageY - offset.top;
      $el.addClass('txt_cursor-move');
    }

    $(document).on('pointermove.image-' + id, onMove);
    $(document).on('pointerup.image-' + id, onEnd);
  }

  function onMove(e) {
    const point = getPoint(e);
    const containerOffset = container.offset();

    if (isDragging) {
      let newLeft = point.pageX - containerOffset.left - offsetX;
      let newTop = point.pageY - containerOffset.top - offsetY;

      newLeft = Math.max(0, Math.min(container.innerWidth() - $el.width(), newLeft));
      newTop = Math.max(0, Math.min(container.innerHeight() - $el.height(), newTop));

      $el.css({ left: newLeft, top: newTop });
    }

    if (isResizing) {
      const dx = point.pageX - startX;
      const dy = point.pageY - startY;
      $el.css({
        width: Math.max(20, startW + dx),
        height: Math.max(20, startH + dy)
      });
    }
  }

  function onEnd() {
    isDragging = false;
    isResizing = false;
    $el.removeClass('txt_cursor-move txt_cursor-se-resize');
    $(document).off('.image-' + id);
  }

  function onDelete() {
    $(document).off('.image-' + id);
    $el.remove();
  }

  $el.on('pointerdown', onStart);
  $el.on('dblclick', onDelete);
}

function creerBox(id, width, height, classes = [], titre = 'Titre', x = 50, y = 50, html = null, zIndex = null, bringToFrontOnInteract = true, container = $('#box-container'), onClose = null) {
  $('#' + id).remove();

  zIndex = zIndex !== null ? zIndex : getNextZIndex();

  const $box = $('<div>', {
    id,
    class: `box-widget ${classes.join(' ')}`,
    css: {
      width,
      height,
      top: y,
      left: x,
      zIndex,
      position: 'absolute'
    }
  });

  const $header = $('<div>', { class: 'box-header' });
  const $title = $('<div>', { class: 'box-title', text: titre });
  const $controls = $('<div>', { class: 'box-controls d-flex gap-1' });

  const $btnToggle = $('<button>', { html: '<i class="bi bi-caret-up-square"></i>', title: 'Replier' });
  const $btnFullscreen = $('<button>', { html: '<i class="bi bi-window-fullscreen"></i>', title: 'Agrandir' });
  const $btnClose = $('<button>', { html: '<i class="bi bi-x-square-fill"></i>', title: 'Fermer' });

  const $content = $('<div>', { class: 'box-content' });

  $controls.append($btnToggle, $btnFullscreen, $btnClose);
  $header.append($title, $controls);
  $box.append($header, $content);

  if (html) {
    $content.html(html);
  } else {
    $content.text('Contenu');
  }

  container.append($box);

  let originalHeight = height;
  let isFullscreen = false;
  let savedStyles = {};

  function bringToFront() {
    const newZ = getNextZIndex();
    $box.css('z-index', newZ);
  }

  $btnToggle.on('click', () => {
    if ($content.is(':visible')) {
      originalHeight = $box.outerHeight();
      $content.slideUp(200, () => {
        $box.height($header.outerHeight());
      });
      $btnToggle
        .html('<i class="bi bi-caret-down-square"></i>')
        .attr('title', 'Déplier');
  } else {
      $content.slideDown(200, () => {
        $box.css('height', 'auto');   // 👈 Correction essentielle
      });
      $btnToggle
        .html('<i class="bi bi-caret-up-square"></i>')
        .attr('title', 'Replier');
    }
  });

  $btnFullscreen.on('click', () => {
    if (!isFullscreen) {
      savedStyles = {
        top: $box.css('top'),
        left: $box.css('left'),
        width: $box.css('width'),
        height: $box.css('height'),
        zIndex: $box.css('z-index')
      };
      bringToFront(); // fullscreen = toujours au top
      $box.css({ top: 0, left: 0, width: '100%', height: '100%' });
      $btnFullscreen.attr('title', 'Réduire');
      isFullscreen = true;
    } else {
      $box.css(savedStyles);
      $btnFullscreen.attr('title', 'Agrandir');
      isFullscreen = false;
    }
  });

  $btnClose.on('click', () => {
    $(document).off('.box-' + id);
    $box.remove();
    if (typeof onClose === 'function') {
      onClose(id);
    }
  });

  if (classes.includes('box-move')) {
    $title.css('cursor', 'move');
    $title.on('pointerdown', (e) => {
      e.preventDefault();
      const offset = $box.position();
      const dx = e.clientX - offset.left;
      const dy = e.clientY - offset.top;
      const containerWidth = container.innerWidth();
      const containerHeight = container.innerHeight();

      if (bringToFrontOnInteract) {
        bringToFront();
      }

      function onMove(e) {
        const x = Math.max(0, Math.min(e.clientX - dx, containerWidth - $box.outerWidth()));
        const y = Math.max(0, Math.min(e.clientY - dy, containerHeight - $box.outerHeight()));
        $box.css({ left: x, top: y });
      }

      function onUp() {
        $(document).off('.box-' + id);
      }

      $(document)
        .on('pointermove.box-' + id, onMove)
        .on('pointerup.box-' + id, onUp);
    });
  }

  if (classes.includes('box-resize')) {
    const $resizeHandle = $('<div>', { class: 'box-resize-handle' });
    $box.append($resizeHandle);

    $resizeHandle.on('pointerdown', (e) => {
      e.preventDefault();
      const startX = e.pageX;
      const startY = e.pageY;
      const startWidth = $box.width();
      const startHeight = $box.height();

      if (bringToFrontOnInteract) {
        bringToFront();
      }

      function onResize(e) {
        const newWidth = Math.max(100, startWidth + (e.pageX - startX));
        const newHeight = Math.max(50, startHeight + (e.pageY - startY));
        $box.css({ width: newWidth, height: newHeight });
      }

      function onUp() {
        $(document).off('.box-' + id);
      }

      $(document)
        .on('pointermove.box-' + id, onResize)
        .on('pointerup.box-' + id, onUp);
    });
  }
}

// Export
function exporterWidgets() {
  const widgets = [];

  $('#box-container').children().each(function () {
    const $el = $(this);
    const id = $el.attr('id');
    const classes = $el.attr('class').split(/\s+/);
    const type = $el.hasClass('box-widget') ? 'box' :
                $el.find('img').length ? 'image' : 'etiquette';
    const x = parseInt($el.css('left'));
    const y = parseInt($el.css('top'));
    const zIndex = parseInt($el.css('z-index'));
    const width = $el.outerWidth();
    const height = $el.outerHeight();
    const bringToFrontOnInteract = $el.attr('data-bring-front') !== '0';

    if (type === 'box') {
      const titre = $el.find('.box-title').text();

      // === Synchronisation des champs ===
      const $content = $el.find('.box-content');

      // input + textarea
      $content.find('input, textarea').each(function () {
        const $f = $(this);
        if ($f.is('textarea')) {
          $f.text($f.val());
        } else {
          $f.attr('value', $f.val());
        }
      });

      // select
      $content.find('select').each(function () {
        const val = $(this).val();
        $(this).find('option').each(function () {
          const isSelected = $(this).val() == val;
          $(this).prop('selected', isSelected);
          $(this).attr('selected', isSelected ? 'selected' : null); // 👈 nécessaire pour l’export
        });
      });

      // checkbox + radio
      $content.find('input[type="checkbox"], input[type="radio"]').each(function () {
        const checked = $(this).is(':checked');
        $(this).prop('checked', checked);
        $(this).attr('checked', checked ? 'checked' : null); // 👈 nécessaire pour l’export
      });

      // état replié
      const folded = !$content.is(':visible');
      const html = $el.find('.box-content').html();
      widgets.push({
        type, id, classes, x, y, width, height, zIndex,
        titre, html: $content.html(),
        bringToFrontOnInteract,
        folded   // 👈 NOUVEAU
      });

    } else if (type === 'image') {
      const img = $el.find('img');
      const src = img.attr('src');
      const alt = img.attr('alt');
      widgets.push({ type, id, classes, x, y, width, height, zIndex, src, alt, bringToFrontOnInteract });
    } else if (type === 'etiquette') {
      const html = $el.clone().children('.edit-btn').remove().end().html();
      widgets.push({ type, id, classes, x, y, zIndex, html, bringToFrontOnInteract });
    }
  });

  return JSON.stringify(widgets, null, 2);
}

// Import
function importerWidgets(json) {
  let tabMessages = [];
  $('#box-container').empty();
  $("#header_accueil_droit").hide();

  const widgets = JSON.parse(json);

  for (const widget of widgets) {
    const { id, classes, x, y, zIndex, bringToFrontOnInteract = true } = widget;

    if (widget.type === 'box') {
      creerBox(id, widget.width, widget.height, classes, widget.titre, x, y, widget.html, zIndex, bringToFrontOnInteract);

      // restauration des <select>
      setTimeout(() => {
        const $box = $('#' + widget.id);
        const $content = $box.find('.box-content');

        $content.find('select').each(function () {
          const selectedValues = $(this).find('option[selected]').map(function () {
            return $(this).val();
          }).get();

          $(this).val(selectedValues);
        });
      }, 0);

      // Appliquer l'état replié
      if (widget.folded) {
          const $box = $('#' + widget.id);

          // attendre que creerBox() ait fini son rendu
          setTimeout(() => {
            const $content = $box.find('.box-content');
            const $header  = $box.find('.box-header');
            const $btnToggle = $box.find('.box-controls button:first');

            // forcer header visible (sécurité)
            $header.show();

            // cacher contenu
            $content.hide();

            // hauteur = header uniquement
            const h = $header.outerHeight();
            if (h > 0) {
              $box.height(h);
            }

            // restaurer l’icône
            $btnToggle
              .html('<i class="bi bi-caret-down-square"></i>')
              .attr('title', 'Déplier');

          }, 0); // 👈 exécution après construction du DOM
      }

    } else if (widget.type === 'image') {
      creerImage(id, classes, x, y, widget.src, widget.alt, widget.width, widget.height, zIndex, bringToFrontOnInteract);
    } else if (widget.type === 'etiquette') {
      creerEtiquette(id, classes, x, y, widget.html, zIndex, bringToFrontOnInteract);
    }
  }

  if (boxImportSource === "accueil") {
    $("#outils_bts_options").show();
    $(".outils_ecran_tous").hide();
    $("#outils_ecran_activites").fadeIn(1000);
  }

  // Ajout pour assurer le fonctionnement des boxes dynamiques
  if ($('#box_lancer_des').closest('#box-container').length) box_lancer_desCode();
  if ($('#box_lancer_chiffres').closest('#box-container').length) box_lancer_chiffresCode();
  if ($('#box_lancer_lettres').closest('#box-container').length) box_lancer_lettresCode();
  if ($('#box_lancer_actions').closest('#box-container').length) box_lancer_actionsCode();
  if ($('#box_nombre_hasard').closest('#box-container').length) box_nombre_hasardCode();
  if ($('#box_mot_hasard').closest('#box-container').length) box_mot_hasardCode();
  if ($('#box_image_hasard').closest('#box-container').length) box_bt_image_hasardCode();
  /*if ($('#box_repartir_groupe').closest('#box-container').length) box_repartir_groupeCode();*/
  if ($('#box_repartir_groupe').closest('#box-container').length) {
    $('#box_repartir_groupe').remove();
    tabMessages.push("<div>Les widgets \"Répartir dans des groupes\" et \"Présence\" doivent être exportés et importés via les boutons d'importation et d'exportation intégrés au widget.</div>");
  }
  if ($('#box_svocale').closest('#box-container').length) box_svocaleCode();
  if ($('#box_prompteur').closest('#box-container').length) box_promteurCode();

  if ($('.box-pdf').closest('#box-container').length) {
    $('.box-pdf').remove();
    tabMessages.push("<div>Les widgets contenant des fichiers PDF sélectionnés depuis son poste ne peuvent pas être importés.</div>");
  }

  if ($('.lecteur_audio').closest('#box-container').length) {
    $('.lecteur_audio').remove();
    tabMessages.push("<div>Les widgets contenant des fichiers audios sélectionnés depuis son poste ne peuvent pas être importés.</div>");
  }

  if ($('.lecteur_video').closest('#box-container').length) {
    $('.lecteur_video').remove();
    tabMessages.push("<div>Les widgets contenant des fichiers vidéos sélectionnés depuis son poste ne peuvent pas être importés.</div>");
  }

  if ($('.lecteur_lien').closest('#box-container').length) box_bt_lienqrcodeCode();
  if ($('#box_lienfavori').closest('#box-container').length) box_lienfavoriCode();
  if ($('#box_recherche').closest('#box-container').length) box_rechercheCode();
  if ($('#box_horloge').closest('#box-container').length) box_horlogeCode();
  if ($('#box_minuterie').closest('#box-container').length) box_minuterieCode();
  if ($('#box_chrono_redim').closest('#box-container').length) box_chrono_redimCode();
  if ($('#box_chrono_multi').closest('#box-container').length) box_chrono_multiCode();
  if ($('#box_metronome').closest('#box-container').length) {
    box_metronomeCode();
    setTimeout(function(){
      const tfort = $("#metro_input_temps_fort").val();
      const tfaible = $("#metro_input_temps_faible").val();
      if (tfort === "") $("#metro_temps_fort option[value='grosse_caisse_1.mp3']").prop("selected", true);
      else $("#metro_temps_fort option[value='"+tfort+"']").prop("selected", true);
      if (tfaible === "") $("#metro_temps_faible option[value='caisse_claire_1.mp3']").prop("selected", true);
      else $("#metro_temps_faible option[value='"+tfaible+"']").prop("selected", true);
    }, 500);
  }
  if ($('#box_agenda').closest('#box-container').length) box_agendaCode();
  if ($('#box_jour').closest('#box-container').length) box_jourCode();

  if ($('.jeux_plateau').closest('#box-container').length) {
    let tabAttr = [];
    let cont = "";
    $(".jeux_plateau").each(function(i) {
      const id = $(this).attr("id");
      const top = $(this).css("top");
      const left = $(this).css("left");
      const src = $(this).find("img").attr("src");
      const alt = $(this).find("img").attr("alt");
      const width = $(this).css("width");
      const height = $(this).css("height");
      if (i == 0) cont = $(this).parent().parent().attr("id");
      tabAttr.push([id,top,left,src,alt,width,height,cont]);
    });
    $('.jeux_plateau').remove();
    for (let i=0 ; i < tabAttr.length ; i++) {
      creerImage(tabAttr[i][0], ["txt-move", "txt-transparent", "jeux_plateau"], tabAttr[i][2], tabAttr[i][1], tabAttr[i][3], tabAttr[i][4], tabAttr[i][5], tabAttr[i][6], null, true, container = $('#'+tabAttr[i][7]+' .box-content'));
    }
  }

  if ($('#box_parcours').closest('#box-container').length) box_parcoursCode();
  if ($('#box_sonometre').closest('#box-container').length) box_sonometreCode();
  if ($('.box-mod-w').closest('#box-container').length) box_modalitesTravailCode();
  if ($('#box_retroactions').closest('#box-container').length) box_retroactionsCode();
  if ($('.feux').closest('#box-container').length) box_feuxCode();
  if ($('#box_compteur_points').closest('#box-container').length) box_compteurPointsCode();

  if (tabMessages.length > 0) messageInfo(tabMessages.join(''), "warning", { type: "close" });
}

$('#box_bt_exporter').on('click', () => {
  const dataStr = exporterWidgets();
  const blob = new Blob([dataStr], { type: 'application/json' });
  const url = URL.createObjectURL(blob);
  const a = $('<a>', {
    href: url,
    //download: 'widgets.json'
    download: "zone-activites-"+afficherDateEnr()+".json"
  }).appendTo('body');
  a[0].click();
  a.remove();
  URL.revokeObjectURL(url);
});

let boxImportSource = "";
$('#box_bt_importer').on('click', () => {
  $('#box_input_importer').click();
  boxImportSource = "zone";
});

$('#box_bt_importer_accueil').on('click', () => {
  $('#box_input_importer').click();
  boxImportSource = "accueil";
});

$('#box_input_importer').on('change', function () {
  const file = this.files[0];
  if (!file) return;

  const reader = new FileReader();
  reader.onload = function (e) {
    try {
      importerWidgets(e.target.result);
    } catch (err) {
      alert('Erreur lors de l\'import : ' + err.message);
    }
  };
  reader.readAsText(file);
});

/*****************************************/
/* Outils => Fonctions de calque         */
/*****************************************/

$(".outils_bts_outil_ec").on("click", function (e) {
  $(".outils_bts_outil_ec").removeClass("btn-light").addClass("btn-outline-light");
  $(this).removeClass("btn-outline-light").addClass("btn-light");
});

const canvas = document.getElementById("outils_calque");
let annotator = null;

class Annotator {
  constructor(canvas) {
    this.canvas = canvas;
    this.ctx = canvas.getContext("2d");
    this.paths = [];
    this.undone = [];

    this.currentPath = null;
    this.tool = "pen";
    this.color = "#000000";
    this.size = 5;

    // Pour déplacer
    this.selectedPath = null;
    this.dragOffset = { x: 0, y: 0 };
    this.isDragging = false;

    this.cursorCircle = document.getElementById("calque_cursor_circle");

    this._bindEvents();
    this.resizeCanvas();
    window.addEventListener("resize", () => this.resizeCanvas());

    document.addEventListener("mousemove", (e) => {
      const rect = this.canvas.getBoundingClientRect();
      const x = e.clientX;
      const y = e.clientY;
      const insideCanvas = x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
      if (insideCanvas && ["pen", "highlight", "eraser"].includes(this.tool)) {
        this.cursorCircle.style.display = "block";
      } else {
        this.cursorCircle.style.display = "none";
      }
    });
  }

  resizeCanvas() {
    const rect = this.canvas.parentElement.getBoundingClientRect();
    this.canvas.width = rect.width;
    this.canvas.height = rect.height;
    this.redraw();
  }

  _bindEvents() {
    const getPos = (e) => {
      const rect = this.canvas.getBoundingClientRect();
      if (e.touches) e = e.touches[0];
      return { x: e.clientX - rect.left, y: e.clientY - rect.top };
    };

    let isDrawing = false;

    const start = (e) => {
      e.preventDefault();
      const pos = getPos(e);
      this.dragStartPos = pos;

      if (this.tool === "pen" || this.tool === "highlight") {
        isDrawing = true;
        this.currentPath = {
          tool: this.tool,
          color: this.tool === "highlight" ? this.color + "80" : this.color,
          size: parseInt(this.size),
          points: [pos],
        };
      } else if (this.tool === "eraser") {
        // Gomme au clic + en continue
        const erased = this.paths.filter((p) => this._isHit(p, pos));
        if (erased.length) {
          // Action à enregistrer pour undo
          this._pushUndo({
            type: "erase",
            paths: erased,
          });
          this.paths = this.paths.filter((p) => !erased.includes(p));
          this.redraw();
        }
      } else if (this.tool === "select") {
        // Sélection d'un path proche
        this.selectedPath = null;
        for (let i = this.paths.length - 1; i >= 0; i--) {
          if (this._isHit(this.paths[i], pos)) {
            this.selectedPath = this.paths[i];
            break;
          }
        }
        if (this.selectedPath) {
          this.isDragging = true;
          this.dragOffset = { x: pos.x, y: pos.y };
          // Sauvegarde position pour undo
          this.dragStartPos = pos;
          this.selectedPathStartPoints = this.selectedPath.points.map(p => ({ x: p.x, y: p.y }));
        }
      }
    };

    const move = (e) => {
      e.preventDefault();
      const pos = getPos(e);

      // Affiche curseur cercle
      this.cursorCircle.style.left = (pos.x + this.canvas.getBoundingClientRect().left) + "px";
      this.cursorCircle.style.top = (pos.y + this.canvas.getBoundingClientRect().top) + "px";

      if (this.tool === "pen" || this.tool === "highlight") {
        if (!isDrawing) return;
        this.currentPath.points.push(pos);
        this.redraw();
      } else if (this.tool === "eraser") {
        // Gomme en glissant
        /*
        const erased = this.paths.filter((p) => this._isHit(p, pos));
        if (erased.length) {
          this._pushUndo({
            type: "erase",
            paths: erased,
          });
          this.paths = this.paths.filter((p) => !erased.includes(p));
          this.redraw();
        }
        */
      } else if (this.tool === "select") {
        if (!this.isDragging || !this.selectedPath) return;
        const dx = pos.x - this.dragOffset.x;
        const dy = pos.y - this.dragOffset.y;
        this.selectedPath.points = this.selectedPathStartPoints.map(p => ({ x: p.x + dx, y: p.y + dy }));
        this.redraw();
      }
    };

    const end = (e) => {
      e.preventDefault();
      if (this.tool === "pen" || this.tool === "highlight") {
        if (isDrawing && this.currentPath.points.length > 1) {
          this.paths.push(this.currentPath);
          this._pushUndo({ type: "draw", path: this.currentPath });
        }
        this.currentPath = null;
        isDrawing = false;
        this.redraw();
      } else if (this.tool === "eraser") {
        // rien de particulier à faire
      } else if (this.tool === "select") {
        if (this.isDragging && this.selectedPath) {
          // Push undo de déplacement
          const newPoints = this.selectedPath.points.map(p => ({ x: p.x, y: p.y }));
          this._pushUndo({
            type: "move",
            path: this.selectedPath,
            before: this.selectedPathStartPoints,
            after: newPoints,
          });
        }
        this.selectedPath = null;
        this.isDragging = false;
      }
    };

    this.canvas.addEventListener("mousedown", start);
    this.canvas.addEventListener("touchstart", start);
    this.canvas.addEventListener("mousemove", move);
    this.canvas.addEventListener("touchmove", move);
    this.canvas.addEventListener("mouseup", end);
    this.canvas.addEventListener("mouseleave", end);
    this.canvas.addEventListener("touchend", end);
    this.canvas.addEventListener("touchcancel", end);
  }

  _isHit(path, pos) {
    if (!path || !path.points || path.points.length === 0) return false;
    const ctx = this.ctx;
    ctx.lineWidth = path.size + 10; // tolérance +10 px
    ctx.lineCap = "round";
    ctx.lineJoin = "round";

    // Cas des traits avec plusieurs points (segments)
    for (let i = 0; i < path.points.length - 1; i++) {
      const p1 = path.points[i];
      const p2 = path.points[i + 1];
      if (this._pointNearLine(pos, p1, p2, ctx.lineWidth)) return true;
    }

    // Cas des traits avec un seul point (point isolé)
    if (path.points.length === 1) {
      const pt = path.points[0];
      const dist = Math.hypot(pos.x - pt.x, pos.y - pt.y);
      if (dist <= ctx.lineWidth / 2) return true;
    }

    return false;
  }


  _pointNearLine(pt, lineStart, lineEnd, width) {
    // Distance point-segment
    const dx = lineEnd.x - lineStart.x;
    const dy = lineEnd.y - lineStart.y;
    if (dx === 0 && dy === 0) {
      // segment nul
      const dist = Math.hypot(pt.x - lineStart.x, pt.y - lineStart.y);
      return dist <= width / 2;
    }
    const t = ((pt.x - lineStart.x) * dx + (pt.y - lineStart.y) * dy) / (dx * dx + dy * dy);
    let closest;
    if (t < 0) closest = lineStart;
    else if (t > 1) closest = lineEnd;
    else closest = { x: lineStart.x + t * dx, y: lineStart.y + t * dy };
    const dist = Math.hypot(pt.x - closest.x, pt.y - closest.y);
    return dist <= width / 2;
  }

  redraw() {
    const ctx = this.ctx;
    ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);

    for (const p of this.paths) {
      ctx.lineCap = "round";
      ctx.lineJoin = "round";
      ctx.strokeStyle = p.color;
      ctx.lineWidth = p.size;
      ctx.beginPath();
      if (p.points.length === 1) {
        const pt = p.points[0];
        ctx.moveTo(pt.x, pt.y);
        ctx.lineTo(pt.x + 0.1, pt.y + 0.1); // for dot
      } else {
        ctx.moveTo(p.points[0].x, p.points[0].y);
        for (let i = 1; i < p.points.length; i++) {
          ctx.lineTo(p.points[i].x, p.points[i].y);
        }
      }
      ctx.stroke();
    }

    if (this.currentPath) {
      ctx.lineCap = "round";
      ctx.lineJoin = "round";
      ctx.strokeStyle = this.currentPath.color;
      ctx.lineWidth = this.currentPath.size;
      ctx.beginPath();
      if (this.currentPath.points.length === 1) {
        const pt = this.currentPath.points[0];
        ctx.moveTo(pt.x, pt.y);
        ctx.lineTo(pt.x + 0.1, pt.y + 0.1);
      } else {
        ctx.moveTo(this.currentPath.points[0].x, this.currentPath.points[0].y);
        for (let i = 1; i < this.currentPath.points.length; i++) {
          ctx.lineTo(this.currentPath.points[i].x, this.currentPath.points[i].y);
        }
      }
      ctx.stroke();
    }
  }

  setTool(tool) {
    this.tool = tool;
    this.currentPath = null;
    this.selectedPath = null;
    this.isDragging = false;
    this.undone = []; // clear redo stack on new action

    // Modifie le curseur
    if (tool === "pen" || tool === "highlight") {
      //this.cursorCircle.style.borderColor = this.color;
      this.cursorCircle.style.borderColor = "#000000";
      this.cursorCircle.style.width = this.size + "px";
      this.cursorCircle.style.height = this.size + "px";
      this.cursorCircle.style.display = "block";
      this.canvas.style.cursor = "none";
    } else if (tool === "eraser") {
      /*
      this.cursorCircle.style.borderColor = "#000";
      this.cursorCircle.style.width = this.size + "px";
      this.cursorCircle.style.height = this.size + "px";
      this.cursorCircle.style.display = "block";
      this.canvas.style.cursor = "none";
      */
      this.canvas.style.cursor = "crosshair"; // ou "pointer", selon le style souhaité ou SVG
      //this.canvas.style.cursor = "url('data:image/svg+xml;utf8,<svg ...>'), auto";
    } else if (tool === "select") {
      this.cursorCircle.style.display = "none";
      this.canvas.style.cursor = "move";
    } else {
      this.cursorCircle.style.display = "none";
      this.canvas.style.cursor = "default";
    }
  }

  setColor(color) {
    this.color = color;
    if (this.tool === "pen" || this.tool === "highlight") {
      this.cursorCircle.style.borderColor = this.color;
    }
  }

  setSize(size) {
    this.size = size;
    if (this.tool === "pen" || this.tool === "highlight" || this.tool === "eraser") {
      this.cursorCircle.style.width = this.size + "px";
      this.cursorCircle.style.height = this.size + "px";
    }
  }

  undo() {
    if (this.undone.length === 0 && this.paths.length === 0) return;
    if (this.undoStack.length === 0) {
      // build undoStack from existing paths (for first undo)
      this.undoStack = [];
    }

    const action = this.undoStack.pop();
    if (!action) return;

    switch (action.type) {
      case "draw":
        // remove last drawn path
        this.paths = this.paths.filter(p => p !== action.path);
        this.undone.push(action);
        break;
      case "erase":
        // restore erased paths
        this.paths.push(...action.paths);
        this.undone.push(action);
        break;
      case "move":
        // revert points to before
        action.path.points = action.before.map(p => ({ x: p.x, y: p.y }));
        this.undone.push(action);
        break;
    }
    this.redraw();
  }

  redo() {
    if (this.undone.length === 0) return;
    const action = this.undone.pop();
    if (!action) return;

    switch (action.type) {
      case "draw":
        this.paths.push(action.path);
        this.undoStack.push(action);
        break;
      case "erase":
        this.paths = this.paths.filter(p => !action.paths.includes(p));
        this.undoStack.push(action);
        break;
      case "move":
        action.path.points = action.after.map(p => ({ x: p.x, y: p.y }));
        this.undoStack.push(action);
        break;
    }
    this.redraw();
  }

  _pushUndo(action) {
    if (!this.undoStack) this.undoStack = [];
    this.undoStack.push(action);
    this.undone = [];
  }

  clear() {
    if (this.paths.length === 0) return;
    this._pushUndo({
      type: "clear",
      paths: [...this.paths],
    });
    this.paths = [];
    this.redraw();
  }
}

//const canvas = document.getElementById("outils_calque");
//const annotator = new Annotator(canvas);

// Relier les boutons import/export JSON / image
$("#outils_bt_importer_calque").on("click", () => {
  $("#outils_input_importer_calque").click();
});

function importJSON(event) {
  const file = event.target.files[0];
  if (!file) return;
  const reader = new FileReader();
  reader.onload = (e) => {
    try {
      const data = JSON.parse(e.target.result);
      if (!data.paths) throw new Error("Format invalide");
      annotator.paths = data.paths;
      annotator.undoStack = [];
      annotator.undone = [];
      annotator.redraw();
    } catch {
      alert("Le fichier JSON est invalide");
    }
  };
  reader.readAsText(file);
}

function exportJSON() {
  const data = { paths: annotator.paths };
  const blob = new Blob([JSON.stringify(data)], { type: "application/json" });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  //a.download = "annotations.json";
  a.download = "annotations-"+afficherDateEnr()+".json";
  a.click();
  URL.revokeObjectURL(url);
}

function exportImage() {
  const tempCanvas = document.createElement("canvas");
  tempCanvas.width = annotator.canvas.width;
  tempCanvas.height = annotator.canvas.height;
  const ctx = tempCanvas.getContext("2d");

  // Fond blanc transparent
  ctx.clearRect(0, 0, tempCanvas.width, tempCanvas.height);

  for (const p of annotator.paths) {
    ctx.lineCap = "round";
    ctx.lineJoin = "round";
    ctx.strokeStyle = p.color;
    ctx.lineWidth = p.size;
    ctx.beginPath();
    if (p.points.length === 1) {
      const pt = p.points[0];
      ctx.moveTo(pt.x, pt.y);
      ctx.lineTo(pt.x + 0.1, pt.y + 0.1);
    } else {
      ctx.moveTo(p.points[0].x, p.points[0].y);
      for (let i = 1; i < p.points.length; i++) {
        ctx.lineTo(p.points[i].x, p.points[i].y);
      }
    }
    ctx.stroke();
  }

  tempCanvas.toBlob((blob) => {
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    //a.download = "annotations.png";
    a.download = "annotations-"+afficherDateEnr()+".png";
    a.click();
    URL.revokeObjectURL(url);
  });
}

function exportMergedImage() {
  //html2canvas(document.body).then((canvas) => {
  //html2canvas("outils_ecran_activites").then((canvas) => {
  const aGenerer = document.querySelector('#outils_ecran_activites');
  html2canvas(aGenerer).then((canvas) => {
    canvas.toBlob((blob) => {
      const url = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = url;
      a.download = "screenshot.png";
      a.click();
      URL.revokeObjectURL(url);
    });
  });
}

/*****************************************/
/* Outils => Fonctions d'upload d'images */
/*****************************************/

/*
=== HTML
<div class="zone-images" data-image-selector data-max-size="80">
  <p>Zone 1 : Déposez ou sélectionnez des images</p>
  <div class="inputs">
    <input type="file" multiple accept="image/*" class="input-fichiers">
    <input type="file" multiple accept="image/*" webkitdirectory class="input-dossier">
  </div>
  <button class="btn-fichiers">Choisir des images</button>
  <button class="btn-dossier">Choisir un dossier</button>
  <button class="btn-supprimer-tout" type="button">Tout supprimer</button>
  <div class="apercu"></div>
  <button class="btn-test-img-apercu disabled" type="button" disabled>Tester images aperçu</button>
</div>
=== CSS à adapter
.zone-images {border: 2px dashed #999; padding: 30px; margin-bottom: 30px; border-radius: 10px; transition: background 0.3s; text-align: center;}
.zone-images.dragover {background: #eef;}
.apercu {display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin-top: 15px;}
.image-container {position: relative; display: inline-block;}
.image-container img {height: 80px; object-fit: cover; border: 1px solid #ccc; border-radius: 4px;}
.image-container .close-btn {position: absolute; top: -6px; right: -6px; background: red; color: white; border: none; border-radius: 50%; width: 24px; height: 24px; font-size: 16px; display: flex; align-items: center; justify-content: center; line-height: 1!important; padding: 0; cursor: pointer; box-shadow: 0 0 2px rgba(0,0,0,0.4); font-family: Arial, sans-serif; font-size: 16px;}
.inputs {display: none;}
.zone-images button {margin: 5px; padding: 8px 12px; font-size: 14px; cursor: pointer; border: 1px solid #888; border-radius: 5px; background-color: #f5f5f5;}
.zone-images button:hover {background-color: #ddd;}
*/

function initImageSelectorZone(zoneElement) {
  const dropZone = zoneElement;
  const apercu = zoneElement.querySelector('.apercu');
  const inputFichiers = zoneElement.querySelector('.input-fichiers');
  const inputDossier = zoneElement.querySelector('.input-dossier');
  const btnFichiers = zoneElement.querySelector('.btn-fichiers');
  const btnDossier = zoneElement.querySelector('.btn-dossier');
  const btnToutSupprimer = zoneElement.querySelector('.btn-supprimer-tout');
  const btnTest = zoneElement.querySelector('.btn-test-img-apercu'); // <== récupération du bouton
  const maxSize = parseInt(zoneElement.dataset.maxSize) || 80;

  // --- Nouvelle fonction pour mettre à jour le bouton ---
  function updateTestButton() {
    if (!btnTest) return; // si pas de bouton dans la zone, on ne fait rien
    const hasImages = apercu.querySelector('.image-container') !== null;
    if (hasImages) {
      btnTest.classList.remove('disabled');
      btnTest.removeAttribute('disabled');
    } else {
      btnTest.classList.add('disabled');
      btnTest.setAttribute('disabled', 'true');
    }
  }

  function redimensionnerImage(fichier) {
    const reader = new FileReader();
    reader.onload = function (event) {
      const img = new Image();
      img.onload = function () {
        let width = img.width;
        let height = img.height;

        if (width > height) {
          if (width > maxSize) {
            height *= maxSize / width;
            width = maxSize;
          }
        } else {
          if (height > maxSize) {
            width *= maxSize / height;
            height = maxSize;
          }
        }

        const canvas = document.createElement('canvas');
        canvas.width = width;
        canvas.height = height;
        const ctx = canvas.getContext('2d');
        ctx.drawImage(img, 0, 0, width, height);

        const miniature = new Image();
        miniature.src = canvas.toDataURL();
        miniature.alt = fichier.name.replace(/\.[^/.]+$/, '');
        miniature.title = fichier.name.replace(/\.[^/.]+$/, '');

        const container = document.createElement('div');
        container.className = 'image-container';

        const btnSuppr = document.createElement('button');
        btnSuppr.className = 'close-btn';
        btnSuppr.textContent = '×';
        btnSuppr.onclick = () => {
          container.remove();
          updateTestButton(); // <== mise à jour après suppression
        };

        container.appendChild(miniature);
        container.appendChild(btnSuppr);
        apercu.appendChild(container);

        updateTestButton(); // <== mise à jour après ajout
      };
      img.src = event.target.result;
    };
    reader.readAsDataURL(fichier);
  }

  function handleFiles(fichiers) {
    const images = Array.from(fichiers).filter(f => f.type.startsWith('image/'));
    if (images.length === 0) {
      alert("Aucune image valide détectée.");
      return;
    }
    images.forEach(redimensionnerImage);
  }

  inputFichiers?.addEventListener('change', e => handleFiles(e.target.files));
  inputDossier?.addEventListener('change', e => handleFiles(e.target.files));

  btnFichiers?.addEventListener('click', () => inputFichiers?.click());
  btnDossier?.addEventListener('click', () => inputDossier?.click());
  btnToutSupprimer?.addEventListener('click', () => {
    apercu.innerHTML = '';
    updateTestButton(); // <== mise à jour après "tout supprimer"
  });

  dropZone?.addEventListener('dragover', e => {
    e.preventDefault();
    dropZone.classList.add('dragover');
  });

  dropZone?.addEventListener('dragleave', () => {
    dropZone.classList.remove('dragover');
  });

  dropZone?.addEventListener('drop', e => {
    e.preventDefault();
    dropZone.classList.remove('dragover');

    const items = e.dataTransfer.items;
    if (items && items[0] && items[0].webkitGetAsEntry) {
      const fichiers = [];
      let pending = 0;

      function lireEntree(entry) {
        if (entry.isFile) {
          pending++;
          entry.file(file => {
            fichiers.push(file);
            pending--;
            if (pending === 0) handleFiles(fichiers);
          });
        } else if (entry.isDirectory) {
          const reader = entry.createReader();
          reader.readEntries(entries => {
            entries.forEach(ent => lireEntree(ent));
          });
        }
      }

      for (const item of items) {
        const entry = item.webkitGetAsEntry();
        if (entry) lireEntree(entry);
      }
    } else {
      handleFiles(e.dataTransfer.files);
    }
  });

  updateTestButton(); // <== état initial au démarrage
}

/* A lancer après l'affichage du html */
function activerinitImageSelector() {
  document.querySelectorAll('[data-image-selector]').forEach(zone => {
    initImageSelectorZone(zone);
  });
}
/* Si une seule zone active : initImageSelectorZone(document.querySelector('.zone1')); */




/*****************************************/
/* Fonctions de modèles d'écriture       */
/*****************************************/

// ---- Canvas et dessin ----
let canvasModele = document.getElementById("modele_canvas");
let ctxModele = canvasModele.getContext("2d");
let drawingModele = false;

function afficherModeleSaisi() {
  var texteSaisi = $('#modele_saisi').val().trim();
  //var police = $("#modele_choix_police").val();
  //if (police !== "p_cursif") texteSaisi += espacesInsecables;
  texteSaisi += espacesInsecables;
  $('#modele_zone_ecriture').html(texteSaisi);
  setTimeout(modeleResizeCanvas, 50);
  if ($("#modele_saisi").val() === "") $("#bt_sup_ctn_modele").hide();
  else $("#bt_sup_ctn_modele").show();
}

function modeleResizeCanvas() {
  //let zone = document.getElementById("modele_zone_ecriture");
  let zone = document.getElementById("zone_toutes");

  let width = zone.getBoundingClientRect().width;
  let height = zone.offsetHeight;

  // Ajout d’un tampon pour couvrir toute la zone
  canvasModele.width = width;
  canvasModele.height = height + 20;

  canvasModele.style.width = width + "px";
  canvasModele.style.height = (height + 20) + "px";
}

// ---- Fonctions utilitaires ----
function drawLine(x, y) {
  ctxModele.lineWidth = document.getElementById("modele_taille_trait").value;
  ctxModele.lineCap = "round";
  ctxModele.strokeStyle = "blue";
  ctxModele.lineTo(x, y);
  ctxModele.stroke();
}

function stopDrawing() {
  drawingModele = false;
  ctxModele.closePath();
}

function getTouchPos(e) {
  let rect = canvasModele.getBoundingClientRect();
  return {
    x: e.touches[0].clientX - rect.left,
    y: e.touches[0].clientY - rect.top
  };
}


/*****************************************/
/* Outils => Fonctions génériques        */
/*****************************************/

var cheminComplet = document.location.href;
var cheminRepertoire = cheminComplet.substring( 0 ,cheminComplet.lastIndexOf( "/" ) );
var nomDuFichierURL = cheminComplet.substring(cheminComplet.lastIndexOf( "/" )+1 );
if (cheminComplet.indexOf("?") == -1) {
	var cheminCompletSansParametre = cheminComplet;
	var nomDuFichierSansParametre = nomDuFichierURL;
} else {
	var cheminCompletSansParametre = cheminComplet.substring( 0 ,cheminComplet.lastIndexOf( "?" ) );
	var nomDuFichierSansParametre = nomDuFichierURL.substring( 0 ,nomDuFichierURL.lastIndexOf( "?" ) );
}

/* Modifier l'URL */
function changeUrl(page, url) {
	if (typeof (history.pushState) != "undefined") {
		var obj = { Page: page, Url: url };
		history.pushState(obj, obj.Page, obj.Url);
	} else {
		alert("Le navigateur ne supporte pas HTML5.");
	}
}

/* Gérer la flèche de retour en haut de page */
function retourHaut() {
	$(window).scroll(function() {
		//	Afficher le bouton si absence d'ascenseur
		if ( $(window).scrollTop() == 0 ) $('#returnOnTop').fadeOut();
		else $('#returnOnTop').fadeIn();
	});
}

/* Afficher tel onglet de telle modale */
function afficherModaleOnglet(activite,onglet) {
  const modale = new bootstrap.Modal(document.getElementById("outils_modal_act_"+activite));
  modale.show();
  $("#"+activite+"_tabs .nav-link").removeClass("active");
  $("#"+activite+"_"+onglet+"_tab").addClass("active");
  $("#"+activite+"_content .tab-pane").removeClass("active show").addClass("fade");
  $("#"+activite+"_"+onglet).removeClass("fade").addClass("active show");
}

function afficherHeure(id) {
  const container = document.getElementById(id);
  if (!container) return;

  function updateTime() {
    const now = new Date();
    const heures = String(now.getHours()).padStart(2, '0');
    const minutes = String(now.getMinutes()).padStart(2, '0');
    container.textContent = `${heures}:${minutes}`;
    const heureAlarme = $("#outil_alarme_heure option:selected").text()+":"+$("#outil_alarme_minute option:selected").text();
    const heureEC = $("#outils_heure").text();

    if ($('#outils_bt_choix_alarme').is(":checked") && heureAlarme === heureEC) {
      const choix = $("#outils_alarme_choix_media img.on").attr("data-choix");
      const texte = $("#outils_saisie_texte_alarme").val().trim();
      if (choix === "muet") {
        if (texte !== "") {
          messageInfo(texte, "warning", { type: "close" });
          lireSVfr(texte);
        }
      } else {// un son est sélectionné
        if (texte === "") {// pas de texte
          let sonNew = new Audio();
          sonNew.src = "medias/alarmes/"+choix+".mp3";
          sonNew.play();
        } else {// son et texte
          messageInfo(texte, "warning", { type: "close" });
          let sonNew = new Audio("medias/alarmes/"+choix+".mp3");
          sonNew.onended = () => {
            lireSVfr(texte);
          };
          sonNew.play();
        }
      }

      $('#outils_bt_choix_alarme').prop('checked', false);
      $('#outils_bt_choix_alarme').next().text("Alarme désactivée");
      $("#outils_alarme_medias").removeClass("active");
      $("#outils_alarme_cloche").html("<i class='bi bi-bell-slash'></i>");
    }
  }

  // Mise à jour immédiate
  updateTime();

  // Mise à jour chaque minute au bon moment
  const now = new Date();
  const msToNextMinute = (60 - now.getSeconds()) * 1000;

  setTimeout(() => {
    updateTime();
    setInterval(updateTime, 60 * 1000);
  }, msToNextMinute);
}

/* Obtenir un nombre aléatoire */
function aleatoire(min, max) {
  return (Math.floor((max-min+1)*Math.random())+min);
}

/* Mélanger les éléments d'un tableau https://css-tricks.com/snippets/javascript/shuffle-array/ */
function shuffle(o) {
  for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
  return o;
};

/* Cloner un tableau via Json */
function cloneViaJson(obj){
  try{
      var copy = JSON.parse(JSON.stringify(obj));
  } catch(ex){
      alert("Vous utilisez un vieux navigateur obsolète qui n'est pas pris en charge par ce site.");
  }
  return copy;
}

/* Synthèse vocale */
function lireSVfr(texte) {
  if (texte !== '' && !synth.speaking) {
    var utterThis = new SpeechSynthesisUtterance(texte);
    utterThis.onend = function (event) {
    };
    utterThis.onerror = function (event) {
    };
    utterThis.lang = "fr-FR";
    utterThis.rate = 1;
    utterThis.pitch = 1;
    synth.speak(utterThis);
  } else synth.cancel();
}

/* Copier un champ input ou textarea */
function copierChamp(selecteur) {
  const element = document.querySelector(selecteur);
  if (!element) {
    console.warn("Élément non trouvé :", selecteur);
    return;
  }

  const texte = element.value || element.textContent;

  navigator.clipboard.writeText(texte)
    .then(() => {
      //console.log("Texte copié :", texte);
      infoFlashTop("Texte copié",2000);
    })
    .catch(err => console.error("Erreur de copie :", err));
}

/* Générer un id unique */
function generateUniqueId() {
  return 'id_' + Math.random().toString(36).substr(2, 9);
}

/* Activer le focus pour tel input d'une modale */
function activerFocusModal(idModal,idInput) {
  const lamodale = document.getElementById(idModal);
  const linput = document.getElementById(idInput);
  lamodale.addEventListener('shown.bs.modal', () => {
    linput.focus();
  });
}

/* Afficher date et heures minutes sous forme 25-06-2025-16h36 */
function afficherDateEnr() {
	var ladate=new Date();
	return (ladate.getDate().toString().replace(/^(\d)$/,'0$1')+"-"+(ladate.getMonth()+1).toString().replace(/^(\d)$/,'0$1')+"-"+ladate.getFullYear()+"-"+ladate.getHours().toString().replace(/^(\d)$/,'0$1')+"h"+ladate.getMinutes().toString().replace(/^(\d)$/,'0$1'));
}// a.download = "acces-"+afficherDateEnr()+".json";

/* Convertir une date de 2025-01-30 à 30/01/2025 */
function convertDateTiretBarre(date) {
  const tabDate = date?.split("-");
  if (!tabDate || tabDate.length !== 3) return "";
  return `${tabDate[2]}/${tabDate[1]}/${tabDate[0]}`;
}

/* Convertir une date en Lundi 10 janvier 2000 */
function formaterDateFr(dateStr) {
  const jours = ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'];
  const mois = ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'];
  const date = new Date(dateStr);
  const jourSemaine = jours[date.getDay()];
  let jour = date.getDate();
  if (jour === 1) jour = "1er";
  const moisNom = mois[date.getMonth()];
  const annee = date.getFullYear();
  return `${jourSemaine} ${jour} ${moisNom} ${annee}`;
}

/* Afficher une info flash en haut de page */
function infoFlashTop(message,duree) {
  $("body").append(`<div id="info_flash_top" class="bg-warning-subtle text-warning-emphasis p-2 text-center" style="position:fixed; top:0; left:0; right:0; z-index:100000"><b>`+message+`</b></div>`)
  $("#info_flash_top").fadeOut(duree);
  setTimeout(function(){ $("#info_flash_top").remove(); }, duree);
}

/* Créer un message d'info pour une info ou uune confirmation */
function messageInfo(texte, couleur = "info", options = {}) {
  const { type = "close", duree = 3000 } = options;

  const $alert = $(`
    <div class="custom-alert alert alert-${couleur} ${type === 'close' ? 'alert-dismissible fade show' : ''}" 
          role="alert"
          style="position:fixed; top:0; left:0; width:100%; font-weight:bold; text-align:center; z-index:9999;">
        ${texte}
        ${type === 'close' ? '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>' : ''}
    </div>
  `);

  $("body").append($alert);

  if (type === "fade") {
    $alert.delay(duree).fadeOut(500, function() { $(this).remove(); });
  } else if (type === "slide") {
    $alert.delay(duree).slideUp(500, function() { $(this).remove(); });
  }
  // couleurs dispos : https://getbootstrap.com/docs/5.3/components/alerts/
  //messageInfo("Ceci est un message d'alerte fermable", "success", { type: "close" });
  //messageInfo("Message qui disparaît en fondu", "warning", { type: "fade", duree: 2000 });
  //messageInfo("Message qui glisse vers le haut", "danger", { type: "slide", duree: 3000 });
}

/* Nettoyer le code produit par Summernote */
function nettoyerSummernote(html) {
  const $temp = $('<div>').html(html);

  // Supprime les balises vides (hors médias)
  $temp.find('*').each(function () {
    const $el = $(this);
    const texte = $el.text().replace(/\u00A0/g, '').trim(); // enlève &nbsp;
    const hasChildren = $el.children().length > 0;

    // Supprime le style vide
    const style = $el.attr('style');
    if (style && style.trim() === '') {
      $el.removeAttr('style');
    }

    // Supprime les balises vides qui ne contiennent rien de significatif
    if (texte === '' && !hasChildren && !$el.is('img, iframe, video, audio, canvas')) {
      $el.remove();
    }
  });

  return $temp.html().trim();
}

/* Interdire certains caractères spéciaux */
function nomValide(nom) {
  // Interdit les caractères spéciaux potentiellement problématiques
  const interdit = /[<>"/\\{}[\]^`|]/;
  return nom && nom.trim().length > 0 && !interdit.test(nom);
}


