modules/gestionInteractif.js

import { context } from './context.js'
import { addElement, get, setStyles } from './dom.js'
import { exerciceCliqueFigure } from './interactif/cliqueFigure.js'
import { exerciceListeDeroulante } from './interactif/questionListeDeroulante.js'
import { exerciceMathLive } from './interactif/questionMathLive.js'
import { exerciceQcm } from './interactif/questionQcm.js'
import { isUserIdOk } from './interactif/isUserIdOk.js'
import { gestionCan } from './interactif/gestionCan.js'
import FractionX from './FractionEtendue.js'
import Grandeur from './Grandeur.js'
import * as pkg from '@cortex-js/compute-engine'
const { ComputeEngine } = pkg
export function exerciceInteractif (exercice) {
  if (exercice.interactifType === 'qcm')exerciceQcm(exercice)
  if (exercice.interactifType === 'listeDeroulante')exerciceListeDeroulante(exercice)
  if (exercice.interactifType === 'cliqueFigure')exerciceCliqueFigure(exercice)
  if (exercice.interactifType === 'custom') exerciceCustom(exercice)
  // Pour les exercices de type custom, on appelle la méthode correctionInteractive() définie dans l'exercice
  if (exercice.interactifType === 'mathLive') exerciceMathLive(exercice)
  if (exercice.interactifType === undefined) exerciceNonInteractif(exercice)
}

/**
 *
 * @param {Exercice} exercice
 * @param {number} i
 * @param {*} param2
 * @returns {string} code HTML du champ texte avec identifiant champTexteEx__Q__ et le span pour le résultat de la question
 */
export function ajouteChampTexte (exercice, i, { texte = '', texteApres = '', inline = true, numeric = false, indice } = {}) {
  if (context.isHtml && exercice.interactif) {
    return `<div class="ui form ${inline ? 'inline' : ''}" >
      <div class="inline  field" >
      <label>${texte}</label>
        <input type="text" ${numeric ? 'type="number" min="0" inputmode="numeric" pattern="[0-9]*"' : ''}  id="champTexteEx${exercice.numeroExercice}Q${i}${indice || ''}" >
        <span>${texteApres}</span>
        <span id="resultatCheckEx${exercice.numeroExercice}Q${i}"></span>
      </div>
      </div>`
  } else {
    return ''
  }
}

/**
 * Précise la réponse attendue
 * @param {Exercice} exercice = this
 * @param {number} i numéro de la question
 * @param {any} valeurs Attention à ce que vous mettez ici : ça doit être en accord avec le formatInteractif ! pas de texNombre ou de stringNombre !
 * @param {object} options
 */

export function setReponse (exercice, i, valeurs, { digits = 0, decimals = 0, signe = false, exposantNbChiffres = 0, exposantSigne = false, approx = 0, aussiCorrect, digitsNum, digitsDen, basePuissance, exposantPuissance, baseNbChiffres, milieuIntervalle, formatInteractif = 'calcul', precision = null } = {}) {
  let reponses = []

  if (Array.isArray(valeurs)) {
    reponses = valeurs // reponses contient donc directement le tableau valeurs
    // si valeur est un tableau ou prend le signe de la première valeur
    if (valeurs[0].num === undefined) {
      signe = valeurs[0] < 0 ? true : signe // on teste si elle est négative, si oui, on force la case signe pour AMC
    } else {
      signe = valeurs[0].signe === -1 ? true : signe // si c'est une fraction, alors on regarde son signe (valeur -1, 0 ou 1)
    }
  } else {
    reponses = [valeurs] // ici, valeurs n'est pas un tableau mais on le met dans reponses sous forme de tableau
    if (valeurs.num === undefined) {
      signe = valeurs < 0 ? true : signe // on teste si elle est négative, si oui, on force la case signe pour AMC
    } else {
      signe = valeurs.signe === -1 ? true : signe // si c'est une fraction, alors on regarde son signe (valeur -1, 0 ou 1)
    }
  }
  let laReponseDemandee
  let test

  const engine = new ComputeEngine()
  switch (formatInteractif) {
    case 'Num':
      if (!(reponses[0] instanceof FractionX)) window.notify('setReponse : type "Num" une fraction est attendue !', { reponses })
      else if (isNaN(reponses[0].num) || isNaN(reponses[0].den)) window.notify('setReponse : La fraction ne convient pas !', { reponses })
      break
    case 'Den':
      if (!(reponses[0] instanceof FractionX)) window.notify('setReponse : type "Den" une fraction est attendue !', { reponses })
      break
    case 'calcul':
      laReponseDemandee = reponses[0]
      if (typeof laReponseDemandee === 'string') {
        laReponseDemandee = laReponseDemandee.replaceAll('dfrac', 'frac')
      }
      if (typeof laReponseDemandee === 'number' || typeof laReponseDemandee === 'string') {
        laReponseDemandee = laReponseDemandee.toString().replace(/\s/g, '').replace(',', '.')
      }
      try {
        test = engine.parse(laReponseDemandee.toString()).canonical
      } catch (error) {
        window.notify('setReponse : type "calcul" la réponse n\'est pas un nombre valide', { reponses, test })
      }
      break
    case 'nombreDecimal':
      if (isNaN(reponses[0].toString())) window.notify('setReponse : type "nombreDecimal" un nombre est attendu !', { reponses })
      break
    case 'ecritureScientifique':
      if (!(typeof reponses[0] === 'string')) window.notify('setReponse : type "ecritureScientifique" la réponse n\'est pas un string !', { reponses })
      // ToFix : vérifier que la chaine est au bon format
      break

    case 'texte':
      if (!(typeof reponses[0] === 'string')) window.notify('setReponse : type "texte" la réponse n\'est pas un string !', { reponses })
      break

    case 'ignorerCasse':
      if (!(typeof reponses[0] === 'string')) window.notify('setReponse : type "ignorerCasse" la réponse n\'est pas un string !', { reponses })
      break
    case 'fractionPlusSimple':
      if (!(reponses[0] instanceof FractionX)) window.notify('setReponse : type "fractionPlusSimple" une fraction est attendue !', { reponses })
      else if (isNaN(reponses[0].num) || isNaN(reponses[0].den)) window.notify('setReponse : La fraction ne convient pas !', { reponses })
      break
    case 'fractionEgale':
      if (!(reponses[0] instanceof FractionX)) window.notify('setReponse : type "fractionEgale" une fraction est attendue !', { reponses })
      else if (isNaN(reponses[0].num) || isNaN(reponses[0].den)) window.notify('setReponse : La fraction ne convient pas !', { reponses })
      break
    case 'fraction':
      if (!(reponses[0] instanceof FractionX)) window.notify('setReponse : type "fraction" une fraction est attendue !', { reponses })
      else if (isNaN(reponses[0].num) || isNaN(reponses[0].den)) window.notify('setReponse : La fraction ne convient pas !', { reponses })
      break
    case 'longueur': // Pour les exercices où l'on attend une mesure avec une unité au choix
      if (!(reponses[0] instanceof Grandeur)) window.notify('setReponse : type "longueur" la réponse n\'est pas une instance de Grandeur !', { reponses })
      break
    case 'intervalleStrict':// Pour les exercice où la saisie doit être dans un intervalle
    // ToFix : vérifier que la réponse est bien un intervalle valide
      break
    case 'intervalle' :
      // ToFix : vérifier que la réponse est bien un intervalle valide
      break
    case 'puissance' :
    // ToFix : vérifier que la réponse est bien l'écriture d'une puissance ou en tout cas une réponse acceptable pour ce format
      break
  }

  if (exercice.autoCorrection[i] === undefined) {
    exercice.autoCorrection[i] = {}
  }
  if (exercice.autoCorrection[i].reponse === undefined) {
    exercice.autoCorrection[i].reponse = {}
  }
  exercice.autoCorrection[i].reponse.param = { digits, decimals, signe, exposantNbChiffres, exposantSigne, approx, aussiCorrect, digitsNum, digitsDen, basePuissance, exposantPuissance, milieuIntervalle, baseNbChiffres, formatInteractif, precision }
  exercice.autoCorrection[i].reponse.valeur = reponses
}

/**
 * Lorsque l'évènement 'exercicesAffiches' est lancé par mathalea.js
 * on vérifie la présence du bouton de validation d'id btnValidationEx{i} créé par listeQuestionsToContenu
 * et on y ajoute un listenner pour vérifier les réponses cochées
 * @param {object} exercice
 */
export function exerciceCustom (exercice) {
  document.addEventListener('exercicesAffiches', () => {
    if (context.vue === 'can') {
      gestionCan(exercice)
    }
    const button = document.querySelector(`#btnValidationEx${exercice.numeroExercice}-${exercice.id}`)
    if (button) {
      if (!button.hasMathaleaListener) {
        button.addEventListener('click', event => {
          let nbBonnesReponses = 0
          let nbMauvaisesReponses = 0
          // Le get est non strict car on sait que l'élément n'existe pas à la première itération de l'exercice
          let eltFeedback = get(`feedbackEx${exercice.numeroExercice}`, false)
          // On ajoute le div pour le feedback
          if (!eltFeedback) {
            const eltExercice = get(`exercice${exercice.numeroExercice}`)
            eltFeedback = addElement(eltExercice, 'div', { id: `feedbackEx${exercice.numeroExercice}` })
          }
          setStyles(eltFeedback, 'marginBottom: 20px')
          if (eltFeedback) eltFeedback.innerHTML = ''
          // On utilise la correction définie dans l'exercice
          if (exercice.exoCustomResultat) {
            for (let i = 0; i < exercice.nbQuestions; i++) {
              exercice.correctionInteractive(i) === 'OK' ? nbBonnesReponses++ : nbMauvaisesReponses++
            }
            afficheScore(exercice, nbBonnesReponses, nbMauvaisesReponses)
          } else {
            for (let i = 0; i < exercice.nbQuestions; i++) {
              exercice.correctionInteractive(i) === 'OK' ? nbBonnesReponses++ : nbMauvaisesReponses++
            }
            afficheScore(exercice, nbBonnesReponses, nbMauvaisesReponses)
          }
          button.classList.add('disabled')
        })
        button.hasMathaleaListener = true
      }
    }
  })
}

/**
 * Lorsque l'évènement 'exercicesAffiches' est lancé par mathalea.js
 * on vérifie la présence du bouton de validation d'id btnValidationEx{i} créé par listeQuestionsToContenu
 * et on y ajoute un listenner pour vérifier les réponses saisies dans les math-field
 * Si le bouton n'existe pas on le crée
 * @param {object} exercice
 */
export function exerciceNonInteractif (exercice) {
  document.addEventListener('exercicesAffiches', () => {
    if (context.vue === 'can') {
      gestionCan(exercice)
    }

    if (context.vue === 'eval') {
      // Si c'est un exo dnb, bac, e3c ou crpe  on affiche plutôt :
      // => l'image de l'énoncé avec une largeur de 70% du container
      // => l'image de la correction avec une largeur de 60% du container
      const myImgs = []
      const myImgsCor = []
      if (exercice.typeExercice === 'dnb' || exercice.typeExercice === 'bac' || exercice.typeExercice === 'e3c') {
        myImgs.push(document.querySelector(`#${exercice.id}`))
        if (!exercice.correctionIsCachee) myImgsCor.push(document.querySelector(`#${exercice.id}Cor`))
      }
      if (exercice.typeExercice === 'crpe') {
        for (let i = 1; i < exercice.png.length + 1; i++) {
          myImgs.push(document.querySelector(`#${exercice.id}-${i}`))
          if (!exercice.correctionIsCachee) myImgsCor.push(document.querySelector(`#${exercice.id}-${i}Cor`))
        }
      }
      for (const myImg of myImgs) {
        myImg.removeAttribute('width')
        myImg.style.width = '70%'
      }
      if (!exercice.correctionIsCachee) {
        for (const myImgCor of myImgsCor) {
          myImgCor.removeAttribute('width')
          myImgCor.style.width = '60%'
        }
      }

      const divAffichageExo = document.querySelector(`#exercice${exercice.numeroExercice}`)

      const button = document.querySelector(`#btnValidationEx${exercice.numeroExercice}-${exercice.id}`)
      button.innerHTML = 'Voir la correction pour s\'auto-corriger'
      button.style.margin = '1em'

      let divMsg = document.querySelector(`#msgExNonIteractif${exercice.numeroExercice}-${exercice.id}`)
      if (!divMsg) divMsg = addElement(divAffichageExo, 'div', { className: '', id: `msgExNonIteractif${exercice.numeroExercice}-${exercice.id}` })
      divMsg.innerHTML = 'Cet exercice n’est pas interactif, faites-le au brouillon avant de vous auto-corriger.'
      divMsg.style.color = '#f15929'
      divMsg.style.fontWeight = 'bold'
      divMsg.style.fontSize = 'x-large'
      divMsg.style.display = 'block'
      divMsg.style.margin = '1em'

      if (button) {
        if (!button.hasMathaleaListener) {
          button.addEventListener('click', event => {
            // Ici on met 1 bonne réponse dans tous les cas car les exos ne sont pas interactifs
            // Cela signifie que l'exo a été visualisé
            // À améliorer pour l'enregistrement dans le fichier de scores
            const nbBonnesReponses = 1
            const nbMauvaisesReponses = 0
            const besoinDe2eEssai = false
            if (!besoinDe2eEssai) {
              button.classList.add('disabled')
              afficheScore(exercice, nbBonnesReponses, nbMauvaisesReponses)
            }
          })
          button.hasMathaleaListener = true
        }
      }
    }
  })
}

export function afficheScore (exercice, nbBonnesReponses, nbMauvaisesReponses) {
  if (context.vue === 'exMoodle') {
    const hauteurExercice = window.document.querySelector('section').scrollHeight + 20
    if (!new URLSearchParams(window.location.search).get('moodleJson')) {
      const scoreRetenu = (score) => {
        const scoreAcceptes = [100, 90, 80, 75, 66.666, 60, 50, 40, 33.333, 30, 25, 20, 16.666, 14.2857, 12.5, 11.111, 10, 5, 0]
        return scoreAcceptes.reduce((prev, curr) => {
          return (Math.abs(curr - score) < Math.abs(prev - score) ? curr : prev)
        })
      }
      const score = scoreRetenu(nbBonnesReponses / (nbBonnesReponses + nbMauvaisesReponses) * 100)
      const reponses = {}
      try {
        for (let i = 0; i < exercice.autoCorrection.length; i++) {
          if (document.getElementById(`champTexteEx0Q${i}`)) {
            reponses[`reponse${i}`] = document.getElementById(`champTexteEx0Q${i}`).value
          }
          if (document.getElementById(`checkEx0Q${i}R0`)) {
            for (let j = 0; j < exercice.autoCorrection[i].propositions.length; j++) {
              if (document.getElementById(`checkEx0Q${i}R${j}`)) {
                reponses[`reponse${i}R${j}`] = document.getElementById(`checkEx0Q${i}R${j}`).checked
              }
            }
          }
        }
      } catch (error) {
        console.log('Réponse non sauvegardée')
      }
      window.parent.postMessage({ score, hauteurExercice, iMoodle: parseInt(new URLSearchParams(window.location.search).get('iMoodle')), reponses }, '*')
    } else {
      window.parent.postMessage({ hauteurExercice, iMoodle: parseInt(new URLSearchParams(window.location.search).get('iMoodle')) }, '*')
    }
  } else {
    // Envoie un message post avec le nombre de réponses correctes
    window.parent.postMessage({ url: window.location.href, graine: context.graine, titre: exercice.titre, nbBonnesReponses, nbMauvaisesReponses }, '*')
  }
  if (context.timer) {
    clearInterval(context.timer)
    // ToDo à sauvegarder dans les résultats
    // const tempsRestant = document.getElementById('timer').innerText
  }
  const divExercice = get(`exercice${exercice.numeroExercice}`)
  let divScore = get(`score${exercice.numeroExercice}`, false)
  // Appel Fecth via une fonction est-ce que c'est ça qui multiplie les appels ?
  isUserIdOk(exercice, nbBonnesReponses, nbMauvaisesReponses)
  if (!divScore) divScore = addElement(divExercice, 'div', { className: 'score', id: `score${exercice.numeroExercice}` })
  divScore.innerHTML = `${nbBonnesReponses} / ${nbBonnesReponses + nbMauvaisesReponses}`
  divScore.style.color = '#f15929'
  divScore.style.fontWeight = 'bold'
  divScore.style.fontSize = 'x-large'
  divScore.style.display = 'inline'
  // Si l'exercice n'est pas interactif on n'affiche pas la div pour le score
  if (exercice.interactifType === undefined) {
    divScore.style.display = 'none'
  }
  if (context.vue === 'eval') {
    const divCorr = get(`divexcorr${exercice.numeroExercice}`)
    divCorr.style.display = 'block'
    const divBoutonExercice = get(`btnEx${exercice.numeroExercice + 1}`)
    divBoutonExercice.classList.add('green')
    const divExercice = get(`exercice${exercice.numeroExercice}`)
    if (exercicesEvalRestants()[0]) {
      const btnExerciceSuivant = addElement(divExercice, 'button', { id: 'btnSuivant', class: 'ui blue button', style: 'display: block' }, 'Exercice suivant')
      btnExerciceSuivant.focus()
      if (!btnExerciceSuivant.hasMathaleaListener) {
        btnExerciceSuivant.addEventListener('click', () => {
          exercicesEvalRestants()[0].click()
        })
        btnExerciceSuivant.hasMathaleaListener = true
      }
    }
  }
}

const exercicesEvalRestants = () => document.querySelectorAll('[id ^= "btnEx"].circular.ui.button:not(.green):not(.red)')