/**
 * js/map.js
 */

/**
 * Typos
 * 
 * Après l'ajout de l'Afrique, on remplace "communes" par "zones"
 * Zones = Communes ou Pays
 */

/**
 * Récupérer le nom du fichier actuel (ex: 971.php) et en extraire le code du département (ex: 971).
 */
const currentFileName = window.location.pathname.split('/').pop()
const codeCarte = currentFileName.split('.')[0]

const startButton = document.getElementById('start')
const restartButton = document.getElementById('restart')

restartButton.setAttribute('disabled', true)

/**
 * Définir le centre , le niveau de zoom et le style de la carte en fonction du code du département.
 */
let centerCoordinates
let zoomLevel
let styleFileName

switch (codeCarte) {
    case 'KMT':
        centerCoordinates = [21.21, 1.36] // Afrique
        zoomLevel = 2.7
        styleFileName = 'ortho-jwe-KMT.json'
        break
    case 'KARUKERA':
        centerCoordinates = [-61.4167, 16.25] // Guadeloupe
        zoomLevel = 9
        styleFileName = 'ortho.json'
        break
    case 'WANAKAERA':
        centerCoordinates = [-61.01635, 14.60285] // Martinique
        zoomLevel = 9.5
        styleFileName = 'ortho.json'
        break
    case 'GUYANE':
        centerCoordinates = [-53.1258, 3.9339] // Guyane française
        zoomLevel = 7
        styleFileName = 'ortho-jwe.json'
        break
    case 'DINA-MORGABIN':
        centerCoordinates = [55.52905, -21.13014] // La Réunion
        styleFileName = 'ortho.json'
        zoomLevel = 9.5
        break
    default:
        centerCoordinates = [-61.4167, 16.25] // Par défaut (Guadeloupe)
        styleFileName = 'ortho.json'
        zoomLevel = 9.5
}

/**
 * Définit les décalages des popups en fonction de leur position.
 */
const popupOffsets = {
    'top': [0, 0],
    'top-left': [0, 0],
    'top-right': [0, 0],
    'bottom': [0, -30],
    'bottom-left': [0, -50],
    'bottom-right': [0, -50],
    'left': [25, -25],
    'right': [-25, -25]
}

/**
 * Référence aux éléments HTML pour afficher le score.
 */
let ok
let ko
let penaltyElement

/**
 * Gestion du temps écoulé
 */
let gameTime = 0
let gameMinute = 0
let gameSecond = 0
let gamePenalty = 0
let gameTimeWithPenalty = 0
let gameMinuteWithPenalty = 0
let gameSecondWithPenalty = 0
let intervalId

/**
 * État actuel de la commune survolée.
 */
let hoveredStateId = null

/**
 * Référence à la commune actuellement sélectionnée pour trouver.
 */
let currentZone = null

/**
 * Ensemble des codes des communes déjà trouvées correctement.
 */
const correctAnswers = new Set()

/**
 * Permet de remettre le jeu à 0
 */
function resetGame() {
    ok.innerText = 0
    ko.innerText = 0
    correctAnswers.clear()
    clearInterval(intervalId)
    intervalId = null
    gameTime = 0
    gamePenalty = 0
    gameTimeWithPenalty = 0
}

/**
 * Active le bouton "Commencer la partie" au chargement de la page
 */
window.onload = function() {
    const startElement = document.getElementById('start')
    startElement.removeAttribute('disabled')
}

function startGame() {
    loadMap()

    const question = document.getElementById('question')
    const score = document.getElementById('score')
    const chrono = document.getElementById('chrono')
    
    question.innerHTML = `<p style='font-weight: bold;'>Trouve ${codeCarte === 'KMT' ? 'le pays' : 'la commune'}</p><p id='commune' style='font-size: 30px; font-weight: bold; color: #1F51FF'>...</p>`
    score.innerHTML = "<p style='color: green; font-weight: bold; margin-right: 2em;'>Correct ✅ : <span style='color: black;' id='OK'>0</span></p><p style='color: red; font-weight: bold;'>Faux ❌ : <span style='color: black;' id='KO'>0</span></p>"
    chrono.innerHTML = "<p>Temps écoulé : <span id='time'></span><span style='margin-left: 1em;color: red; font-weight: bold;' id='penalty'></span></p>"

    communeObserver()

    ok = document.getElementById('OK')
    ko = document.getElementById('KO')
    penaltyElement = document.getElementById('penalty')


    startButton.setAttribute('disabled', true)
    restartButton.removeAttribute('disabled')

    resetGame()
}

/**
 * Recommencer la partie
 */
function restartGame() {
    startGame()
}

/**
 * Observe le contenu de l'élément ayant pour id "commune"
 */
function communeObserver(){
    const communeElement = document.getElementById('commune')
    const timeElement = document.getElementById('time')

    // Options de l'observateur (quelles sont les mutations à observer)
    const config = { childList: true, subtree: true, characterData: true }

    // Fonction callback à éxécuter quand une mutation est observée
    const callback = function (mutationsList) {
        for (var mutation of mutationsList) {
            if (mutation.type == "childList" || mutation.type == "characterData") {
                // Vérifie si le contenu de la balise span n'est pas égal à "..."
                if (communeElement.textContent.trim() !== "...") {
                    // Vérifie si setInterval n'est pas déjà en cours
                    if (!intervalId) {
                        intervalId = setInterval(() => {
                            gameTime++
                            gameTimeWithPenalty = gameTime + gamePenalty

                            if (gameTimeWithPenalty >= 60) {
                                gameMinuteWithPenalty = String(gameTimeWithPenalty / 60).split('.')[0]
                                gameMinute = String(gameTime / 60).split('.')[0]

                                gameSecondWithPenalty = gameTimeWithPenalty % 60
                                gameSecond = gameTime % 60
                                timeElement.innerText = `${gameMinuteWithPenalty} minute${gameMinuteWithPenalty > 1 ? 's' : ''} ${gameSecondWithPenalty} seconde${gameSecondWithPenalty > 1 ? 's' : ''}`
                            } else {
                                timeElement.innerText = `${gameTimeWithPenalty} seconde${gameTimeWithPenalty > 1 ? 's' : ''}`
                            }
                        }, 1000)
                    }
                }
            }
        }
    }

    // Créé une instance de l'observateur lié à la fonction de callback
    const observer = new MutationObserver(callback)

    // Commence à observer le noeud cible pour les mutations précédemment configurées
    observer.observe(communeElement, config)
}

/**
 * Récupère les propriétés des communes depuis un fichier GeoJSON en fonction du code du département.
 * 
 * @async
 * @param {string} codeCarte - Code de la carte.
 * @returns {Promise<Object[]>} Promesse résolue avec les propriétés des communes.
 */
async function getFile(codeCarte) {
    try {
        const response = await fetch(`../kat/data/${codeCarte}/contours-${codeCarte === 'KMT' ? 'pays' : 'communes'}.geojson`)
        if (!response.ok) {
            throw new Error(`Échec du chargement du fichier JSON : ${response.statusText}`)
        }

        data = await response.json()
        return data.features.map(({properties}) => properties)
    } catch (error) {
        console.error('Erreur:', error)
        throw new Error(error)
    }
}

/**
 * Récupère les coordonnées du centre de la commune depuis le fichier "centres.json" en fonction du code du département.
 * 
 * @param {string} codeCarte - Code de la carte.
 * @param {string} codeZone - Code de la zone.
 * @returns {Promise<number[]>} Promesse résolue avec les coordonnées du centre de la commune.
 */
async function getZoneCenterCoordinates(codeCarte, codeZone) {
    try {
        // Utiliser fetch pour lire le fichier "centres.json"
        const response = await fetch(`../kat/data/${codeCarte}/centres.json`)
        if (!response.ok) {
            throw new Error(`Échec du chargement du fichier JSON : ${response.statusText}`)
        }
        
        const centerData = await response.json()
        
        // Vérifier si le code de la zone existe dans les données
        if (codeZone in centerData) {
            const coordinates = centerData[codeZone]
            return coordinates
        } else {
            throw new Error(`Aucune donnée de centre trouvée pour la commune : ${codeZone}`)
        }
    } catch (error) {
        console.error('Erreur lors de la récupération des coordonnées du centre de la commune :', error)
        throw new Error(error)
    }
}


/**
 * Met à jour le score en fonction de la réponse.
 * 
 * @param {boolean} isCorrect - Indique si la réponse est correcte.
 */
function updateScore(isCorrect) {
    if (isCorrect) {
        ok.innerText = Number.parseInt(ok.innerText, 10) + 1
    } else {
        ko.innerText = Number.parseInt(ko.innerText, 10) + 1
        gamePenalty += 5

        penaltyElement.style.opacity = 1
        penaltyElement.innerText = '+ 5 secondes'

        setTimeout(() => {
            penaltyElement.style.opacity = 0
            penaltyElement.innerText = ''
        }, 2000)
    }
}

/**
 * Sélectionne une commune au hasard parmi celles qui n'ont pas encore été trouvées.
 * 
 * @param {Array} zones - Liste de toutes les zones.
 * @returns {Object|null} Retourne la commune sélectionnée ou null si toutes les zones ont été trouvées.
 */
function pickRandomZone(zones) {
    const zonesRestantes = zones.filter(commune => !correctAnswers.has(commune.code))

    if (zonesRestantes.length === 0) {
        return null
    }

    const index = Math.floor(Math.random() * zonesRestantes.length)
    return zonesRestantes[index]
}

/**
 * Vérifie si le code de la zone cliquée correspond à la commune actuelle à trouver.
 * 
 * @param {string} codeZone - code de la zone à vérifier.
 * @returns {boolean} Résultat de la vérification.
 */
function checkAnswer(codeZone) {
    return currentZone && codeZone === currentZone.code
}

/**
 * Charge la carte
 */
function loadMap() {
    /**
     * Initialise et configure la carte
     */
    const map = new maplibregl.Map({
        container: 'map',
        style: `../kat/styles/${styleFileName}`,
        center: centerCoordinates,
        zoom: zoomLevel
    })

    if (codeCarte !== 'GUYANE') {
        /**
         * Désactive le zoom, le double-clic, le déplacement de la carte pour les cartes autres que celle la Guyane.
         */
        map.scrollZoom.disable()
        map.doubleClickZoom.disable()
        map.dragPan.disable()
    } else {
        /**
         * Ajoute les contrôles de navigation à la carte, et définit les niveaux de zoom minimum et maximum pour la Guyane.
         */
        map.addControl(new maplibregl.NavigationControl({showCompass: false}))
        map.setMinZoom(7)
        map.setMaxZoom(9.5)
    }
    
    if (codeCarte === 'KMT') {
        /**
         * Ajoute les contrôles de navigation à la carte, et définit les niveaux de zoom minimum et maximum pour l'Afrique'.
         */
        map.addControl(new maplibregl.NavigationControl({showCompass: false}))
        map.setMinZoom(2.4)
        map.setMaxZoom(9)
        map.dragPan.enable()
    }

    /**
     * Code pour la gestion des événements de la carte (load, mousemove, mouseleave, click).
     */
    map.on('load', async () => {
        const zones = await getFile(codeCarte)

        /**
         * Définit la zone actuelle à trouver et met à jour l'affichage.
         * 
         * @param {Object} zone - La zone à définir comme actuelle.
         */
        function setZone(zone) {
            if (currentZone) {
                map.setFeatureState(
                    {source: 'communes', id: currentZone.code},
                    {current: false}
                )
            }

            currentZone = zone
            document.getElementById('commune').innerText = zone.nom

            map.setFeatureState(
                {source: 'communes', id: zone.code},
                {current: true}
            )
        }

        /**
         * Ajouter une source de données pour les communes.
         */
        map.addSource('communes', {
            'type': 'geojson',
            'data': `../kat/data/${codeCarte}/contours-${codeCarte === 'KMT' ? 'pays' : 'communes'}.geojson`,
            'promoteId': 'code'
        })

        /**
         * Ajouter une couche de remplissage pour les communes.
         */
        map.addLayer({
            'id': 'drom',
            'type': 'fill',
            'source': 'communes',
            'layout': {},
            'paint': {
                'fill-color': '#1F51FF',
                'fill-opacity': [
                    'case',
                    ['boolean', ['feature-state', 'hover'], false],
                    0.8,
                    0
                ]
            }
        })

        /**
         * Ajouter une couche de remplissage pour les communes (2ème couche), en prenant en compte les réponses correctes et incorrectes.
         */
        map.addLayer({
            'id': 'drom-2',
            'type': 'fill',
            'source': 'communes',
            'layout': {},
            'paint': {
                'fill-color': [
                    'case',
                    ['boolean', ['feature-state', 'correct'], false], '#00FF00',
                    ['boolean', ['feature-state', 'incorrect'], false], '#FF0000',
                    'rgba(0,0,0,0)'
                ],
                'fill-opacity': [
                    'case',
                    ['boolean', ['feature-state', 'hover'], false], 0.5,
                    0.8
                ]
            }
        })

        /**
         * Gère l'evenement mousemove (quand la souris survole la commune)
         */
        map.on('mousemove', 'drom', e => {
            map.getCanvas().style.cursor = 'pointer'
            if (e.features.length > 0) {
                if (hoveredStateId) {
                    map.setFeatureState(
                        {source: 'communes', id: hoveredStateId},
                        {hover: false}
                    )
                }
                hoveredStateId = e.features[0].id
                map.setFeatureState(
                    {source: 'communes', id: hoveredStateId},
                    {hover: true}
                )
            }
        })

        /**
         * Gère l'evenement mouseleave (quand la souris quitte la zone)
         */
        map.on('mouseleave', 'drom', () => {
            map.getCanvas().style.cursor = ''
            if (hoveredStateId) {
                map.setFeatureState(
                    {source: 'communes', id: hoveredStateId},
                    {hover: false}
                )
            }
            hoveredStateId = null
        })

        /**
         * Gère l'evenement click (quand on clique sur la zone)
         */
        map.on('click', 'drom', async e => {
            if (e.features.length > 0) {
                const clickedCodeZone = e.features[0].id
                const clickedZoneNom = e.features[0].properties.nom
                const isCorrect = checkAnswer(clickedCodeZone)

                if (isCorrect) {
                    updateScore(true)
                    const centerCoordinates = await getZoneCenterCoordinates(codeCarte, clickedCodeZone)

                    const popup = new maplibregl.Popup({offset: popupOffsets, closeButton: false, className: 'popup-ok'})
                        .setLngLat([centerCoordinates[0], centerCoordinates[1]])
                        .setHTML(`<h3>Bravo, c'est ${clickedZoneNom}</h3>`)
                        .setMaxWidth('200px')
                        .addTo(map)

                    setTimeout(() => {
                        popup.remove()
                    }, 1500)

                    correctAnswers.add(clickedCodeZone)
                    zones.forEach(commune => {
                        map.setFeatureState(
                            {source: 'communes', id: commune.code},
                            {incorrect: false}
                        )
                    })
                    map.setFeatureState(
                        {source: 'communes', id: clickedCodeZone},
                        {correct: true, current: false}
                    )

                    const newZone = pickRandomZone(zones)
                    if (newZone) {
                        setZone(newZone)
                    } else {
                        const totalAnswers = Number.parseInt(ok.innerText, 10) + Number.parseInt(ko.innerText, 10)
                        const percentageCorrectAnswers = (Number.parseInt(ok.innerText, 10) / totalAnswers) * 100

                        let elapsedTime = `${gameTime} seconde${gameTime > 1 ? 's' : ''}`
                        let elapsedTimeWithPenalty = `${gameTimeWithPenalty} seconde${gameTimeWithPenalty > 1 ? 's' : ''}`

                        if (gameTime >= 60) {
                            elapsedTime = `${gameMinute} minute${gameMinute > 1 ? 's' : ''} ${gameSecond} seconde${gameSecond > 1 ? 's' : ''}`
                        }

                        if (gameTimeWithPenalty >= 60) {
                            elapsedTimeWithPenalty = `${gameMinuteWithPenalty} minute${gameMinuteWithPenalty > 1 ? 's' : ''} ${gameSecondWithPenalty} seconde${gameSecondWithPenalty > 1 ? 's' : ''}`
                        }

                        alert(`${Math.round(percentageCorrectAnswers)}% de bonnes réponses !
                            \nTemps écoulé : ${elapsedTime} ! ${gamePenalty ? `
                            \nPénalité : ${gamePenalty} secondes` : '\nAucune pénalité !'}
                            \nTemps total : ${elapsedTimeWithPenalty}`)
                        clearInterval(intervalId)
                        gameTime = 0
                        gameTimeWithPenalty = 0

                        setTimeout(() => {
                            location.reload()
                        }, 1000)
                    }
                } else {
                    updateScore(false)
                    const centerCoordinates = await getZoneCenterCoordinates(codeCarte, clickedCodeZone)

                    const popup = new maplibregl.Popup({offset: popupOffsets, closeButton: false, className: 'popup-ko'})
                        .setLngLat([centerCoordinates[0], centerCoordinates[1]])
                        .setHTML(`<h3>Faux, c'est ${clickedZoneNom}</h3>`)
                        .setMaxWidth('200px')
                        .addTo(map)

                    setTimeout(() => {
                        popup.remove()
                    }, 1500)

                    map.setFeatureState(
                        {source: 'communes', id: clickedCodeZone},
                        {incorrect: true}
                    )
                }
            }
        })

        const zone = pickRandomZone(zones)
        setZone(zone)
    })
}
