(async () => {
    // check notification permission
    // This is useful to alert people that they should do something
    await Notification.requestPermission()
})()

const TFJM = JSON.parse(document.getElementById('TFJM_settings').textContent)
const RECOMMENDED_SOLUTIONS_COUNT = TFJM.RECOMMENDED_SOLUTIONS_COUNT

const problems_count = JSON.parse(document.getElementById('problems_count').textContent)

const tournaments = JSON.parse(document.getElementById('tournaments_list').textContent)
let socket = null

const messages = document.getElementById('messages')

/**
 * Request to abort the draw of the given tournament.
 * Only volunteers are allowed to do this.
 * @param tid The tournament id
 */
function abortDraw(tid) {
    socket.send(JSON.stringify({'tid': tid, 'type': 'abort'}))
}

/**
 * Request to cancel the last step.
 * Only volunteers are allowed to do this.
 * @param tid The tournament id
 */
function cancelLastStep(tid) {
    socket.send(JSON.stringify({'tid': tid, 'type': 'cancel'}))
}

/**
 * Request to launch a dice between 1 and 100, for the two first steps.
 * The parameter `trigram` can be specified (by volunteers) to launch a dice for a specific team.
 * @param tid The tournament id
 * @param trigram The trigram of the team that a volunteer wants to force the dice launch (default: null)
 * @param result The forced value. Null if unused (for regular people)
 */
function drawDice(tid, trigram = null, result = null) {
    socket.send(JSON.stringify({'tid': tid, 'type': 'dice', 'trigram': trigram, 'result': result}))
}

/**
 * Fetch the requested dice from the buttons and request to draw it.
 * Only available for debug purposes and for admins.
 * @param tid The tournament id
 */
function drawDebugDice(tid) {
    let dice_10 = parseInt(document.querySelector(`input[name="debug-dice-${tid}-10"]:checked`).value)
    let dice_1 = parseInt(document.querySelector(`input[name="debug-dice-${tid}-1"]:checked`).value)
    let result = (dice_10 + dice_1) || 100
    let team_div = document.querySelector(`div[id="dices-${tid}"] > div > div[class*="text-bg-warning"]`)
    let team = team_div.getAttribute("data-team")
    drawDice(tid, team, result)
}

/**
 * Request to draw a new problem.
 * @param tid The tournament id
 * @param problem The forced problem. Null if unused (for regular people)
 */
function drawProblem(tid, problem = null) {
    socket.send(JSON.stringify({'tid': tid, 'type': 'draw_problem', 'problem': problem}))
}

/**
 * Accept the current proposed problem.
 * @param tid The tournament id
 */
function acceptProblem(tid) {
    socket.send(JSON.stringify({'tid': tid, 'type': 'accept'}))
}

/**
 * Reject the current proposed problem.
 * @param tid The tournament id
 */
function rejectProblem(tid) {
    socket.send(JSON.stringify({'tid': tid, 'type': 'reject'}))
}

/**
 * Volunteers can export the draw to make it available for notation.
 * @param tid The tournament id
 */
function exportDraw(tid) {
    socket.send(JSON.stringify({'tid': tid, 'type': 'export'}))
}

/**
 * Volunteers can make the draw continue for the second round of the final.
 * @param tid The tournament id
 */
function continueFinal(tid) {
    socket.send(JSON.stringify({'tid': tid, 'type': 'continue_final'}))
}

/**
 * Display a new notification with the given title and the given body.
 * @param title The title of the notification
 * @param body The body of the notification
 * @param timeout The time (in milliseconds) after that the notification automatically closes. 0 to make indefinite. Default to 5000 ms.
 * @return Notification
 */
function showNotification(title, body, timeout = 5000) {
    let notif = new Notification(title, {'body': body, 'icon': "/static/tfjm.svg"})
    if (timeout)
        setTimeout(() => notif.close(), timeout)
    return notif
}

document.addEventListener('DOMContentLoaded', () => {
    if (document.location.hash) {
        // Open the tab of the tournament that is present in the hash
        document.querySelectorAll('button[data-bs-toggle="tab"]').forEach(elem => {
            if ('#' + elem.innerText.toLowerCase() === document.location.hash.toLowerCase()) {
                elem.click()
            }
        })
    }

    // When a tab is opened, add the tournament name in the hash
    document.querySelectorAll('button[data-bs-toggle="tab"]').forEach(
        elem => elem.addEventListener(
            'click', () => document.location.hash = '#' + elem.innerText.toLowerCase()))

    /**
     * Add alert message on the top on the interface.
     * @param message The content of the alert.
     * @param type The alert type, which is a bootstrap color (success, info, warning, danger,…).
     * @param timeout The time (in milliseconds) before the alert is auto-closing. 0 to infinitely, default to 5000 ms.
     */
    function addMessage(message, type, timeout = 5000) {
        const wrapper = document.createElement('div')
        wrapper.innerHTML = [
            `<div class="alert alert-${type} alert-dismissible" role="alert">`,
            `<div>${message}</div>`,
            '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>',
        ].join('\n')
        messages.append(wrapper)

        if (timeout)
            setTimeout(() => wrapper.remove(), timeout)
    }

    /**
     * Update the information banner.
     * @param tid The tournament id
     * @param info The content to updated
     */
    function setInfo(tid, info) {
        document.getElementById(`messages-${tid}`).innerHTML = info
    }

    /**
     * Open the draw interface, given the list of teams.
     * @param tid The tournament id
     * @param teams The list of teams (represented by their trigrams) that are present on this draw.
     */
    function drawStart(tid, teams) {
        // Hide the not-started-banner
        document.getElementById(`banner-not-started-${tid}`).classList.add('d-none')
        // Display the full draw interface
        document.getElementById(`draw-content-${tid}`).classList.remove('d-none')

        let dicesDiv = document.getElementById(`dices-${tid}`)
        for (let team of teams) {
            // Add empty dice score badge for each team
            let col = document.createElement('div')
            col.classList.add('col-md-1')
            dicesDiv.append(col)

            let diceDiv = document.createElement('div')
            diceDiv.id = `dice-${tid}-${team}`
            diceDiv.classList.add('badge', 'rounded-pill', 'text-bg-warning')
            if (document.getElementById(`abort-${tid}`) !== null) {
                // Check if this is a volunteer, who can launch a die for a specific team
                diceDiv.onclick = (_) => drawDice(tid, team)
            }
            diceDiv.textContent = `${team} 🎲 ??`
            col.append(diceDiv)
        }
    }

    /**
     * Abort the current draw, and make all invisible, except the not-started-banner.
     * @param tid The tournament id
     */
    function drawAbort(tid) {
        document.getElementById(`banner-not-started-${tid}`).classList.remove('d-none')
        document.getElementById(`draw-content-${tid}`).classList.add('d-none')
        document.getElementById(`dices-${tid}`).innerHTML = ""
        document.getElementById(`recap-${tid}-round-list`).innerHTML = ""
        document.getElementById(`tables-${tid}`).innerHTML = ""
        updateDiceVisibility(tid, false)
        updateBoxVisibility(tid, false)
        updateButtonsVisibility(tid, false)
        updateExportVisibility(tid, false)
        updateContinueVisibility(tid, false)
    }

    /**
     * This function is triggered after a new dice result. We update the score of the team.
     * Can be resetted to empty values if the result is null.
     * @param tid The tournament id
     * @param trigram The trigram of the team that launched its dice
     * @param result The result of the dice. null if it is a reset.
     */
    function updateDiceInfo(tid, trigram, result) {
        let elem = document.getElementById(`dice-${tid}-${trigram}`)
        if (result === null) {
            elem.classList.remove('text-bg-success')
            elem.classList.add('text-bg-warning')
            elem.innerText = `${trigram} 🎲 ??`
        } else {
            elem.classList.remove('text-bg-warning')
            elem.classList.add('text-bg-success')
            elem.innerText = `${trigram} 🎲 ${result}`
        }

        let nextTeam = document.querySelector(` div[id="dices-${tid}"] > div > div[class*="text-bg-warning"]`).getAttribute("data-team")
        if (nextTeam) {
            // If there is one team that does not have launched its dice, then we update the debug section
            let debugSpan = document.getElementById(`debug-dice-${tid}-team`)
            if (debugSpan)
                debugSpan.innerText = nextTeam
        }
    }

    /**
     * Display or hide the dice button.
     * @param tid The tournament id
     * @param visible The visibility status
     */
    function updateDiceVisibility(tid, visible) {
        let div = document.getElementById(`launch-dice-${tid}`)
        let div_debug = document.getElementById(`debug-dice-form-${tid}`)
        if (visible) {
            div.classList.remove('d-none')
            div_debug.classList.remove('d-none')
        }
        else {
            div.classList.add('d-none')
            div_debug.classList.add('d-none')
        }
    }

    /**
     * Display or hide the box button.
     * @param tid The tournament id
     * @param visible The visibility status
     */
    function updateBoxVisibility(tid, visible) {
        let div = document.getElementById(`draw-problem-${tid}`)
        let div_debug = document.getElementById(`debug-problem-form-${tid}`)
        if (visible) {
            div.classList.remove('d-none')
            div_debug.classList.remove('d-none')
        }
        else {
            div.classList.add('d-none')
            div_debug.classList.add('d-none')
        }
    }

    /**
     * Display or hide the accept and reject buttons.
     * @param tid The tournament id
     * @param visible The visibility status
     */
    function updateButtonsVisibility(tid, visible) {
        let div = document.getElementById(`buttons-${tid}`)
        if (visible)
            div.classList.remove('d-none')
        else
            div.classList.add('d-none')
    }

    /**
     * Display or hide the export button.
     * @param tid The tournament id
     * @param visible The visibility status
     */
    function updateExportVisibility(tid, visible) {
        let div = document.getElementById(`export-${tid}`)
        if (visible)
            div.classList.remove('d-none')
        else
            div.classList.add('d-none')
    }

    /**
     * Display or hide the continuation button.
     * @param tid The tournament id
     * @param visible The visibility status
     */
    function updateContinueVisibility(tid, visible) {
        let div = document.getElementById(`continue-${tid}`)
        if (div !== null) {
            // Only present during the final
            if (visible)
                div.classList.remove('d-none')
            else
                div.classList.add('d-none')
        }
    }

    /**
     * Set the different pools for the given round, and update the interface.
     * @param tid The tournament id
     * @param round The round number, as integer (1 or 2, or 3 for ETEAM)
     * @param poules The list of poules, which are represented with their letters and trigrams,
     *                  [{'letter': 'A', 'teams': ['ABC', 'DEF', 'GHI']}]
     */
    function updatePoules(tid, round, poules) {
        let roundList = document.getElementById(`recap-${tid}-round-list`)
        let poolListId = `recap-${tid}-round-${round}-pool-list`
        let poolList = document.getElementById(poolListId)
        if (poolList === null) {
            // Add a div for the round in the recap div
            let div = document.createElement('div')
            div.id = `recap-${tid}-round-${round}`
            div.classList.add('col-md-6', 'px-3', 'py-3')
            div.setAttribute('data-tournament', tid)

            let title = document.createElement('strong')
            title.textContent = 'Tour ' + round

            poolList = document.createElement('ul')
            poolList.id = poolListId
            poolList.classList.add('list-group', 'list-group-flush')

            div.append(title, poolList)
            roundList.append(div)
        }

        let c = 1

        for (let poule of poules) {
            let teamListId = `recap-${tid}-round-${round}-pool-${poule.letter}-team-list`
            let teamList = document.getElementById(teamListId)
            if (teamList === null) {
                // Add a div for the pool in the recap div
                let li = document.createElement('li')
                li.id = `recap-${tid}-round-${round}-pool-${poule.letter}`
                li.classList.add('list-group-item', 'px-3', 'py-3')
                li.setAttribute('data-tournament', tid)

                let title = document.createElement('strong')
                title.textContent = 'Poule ' + poule.letter + round

                teamList = document.createElement('ul')
                teamList.id = teamListId
                teamList.classList.add('list-group', 'list-group-flush')

                li.append(title, teamList)
                poolList.append(li)
            }
            teamList.innerHTML = ""

            for (let team of poule.teams) {
                // Reorder dices
                let diceDiv = document.getElementById(`dice-${tid}-${team}`)
                diceDiv.parentElement.style.order = c.toString()
                c += 1

                let teamLiId = `recap-${tid}-round-${round}-team-${team}`

                // Add a line for the team in the recap
                let teamLi = document.createElement('li')
                teamLi.id = teamLiId
                teamLi.classList.add('list-group-item')
                teamLi.setAttribute('data-tournament', tid)

                teamList.append(teamLi)

                // Add the accepted problem div (empty for now)
                let acceptedDivId = `recap-${tid}-round-${round}-team-${team}-accepted`
                let acceptedDiv = document.getElementById(acceptedDivId)
                if (acceptedDiv === null) {
                    acceptedDiv = document.createElement('div')
                    acceptedDiv.id = acceptedDivId
                    acceptedDiv.classList.add('badge', 'rounded-pill', 'text-bg-warning')
                    acceptedDiv.textContent = `${team} 📃 ?`
                    teamLi.append(acceptedDiv)
                }

                // Add the rejected problems div (empty for now)
                let rejectedDivId = `recap-${tid}-round-${round}-team-${team}-rejected`
                let rejectedDiv = document.getElementById(rejectedDivId)
                if (rejectedDiv === null) {
                    rejectedDiv = document.createElement('div')
                    rejectedDiv.id = rejectedDivId
                    rejectedDiv.classList.add('badge', 'rounded-pill', 'text-bg-danger')
                    rejectedDiv.textContent = '🗑️'
                    teamLi.append(rejectedDiv)
                }
            }

            // Draw tables
            let tablesDiv = document.getElementById(`tables-${tid}`)
            let tablesRoundDiv = document.getElementById(`tables-${tid}-round-${round}`)
            if (tablesRoundDiv === null) {
                // Add the tables div for the current round if necessary
                let card = document.createElement('div')
                card.classList.add('card', 'col-md-6')
                tablesDiv.append(card)

                let cardHeader = document.createElement('div')
                cardHeader.classList.add('card-header')
                cardHeader.innerHTML = `<h2>Tour ${round}</h2>`
                card.append(cardHeader)

                tablesRoundDiv = document.createElement('div')
                tablesRoundDiv.id = `tables-${tid}-round-${round}`
                tablesRoundDiv.classList.add('card-body', 'd-flex', 'flex-wrap')
                card.append(tablesRoundDiv)
            }

            for (let poule of poules) {
                if (poule.teams.length === 0)
                    continue

                // Display the table for the pool
                updatePouleTable(tid, round, poule)
            }
        }
    }

    /**
     * Update the table for the given round and the given pool, where there will be the chosen problems.
     * @param tid The tournament id
     * @param round The round number, as integer (1 or 2, or 3 for ETEAM)
     * @param poule The current pool, which id represented with its letter and trigrams,
     *                  {'letter': 'A', 'teams': ['ABC', 'DEF', 'GHI']}
     */
    function updatePouleTable(tid, round, poule) {
        let tablesRoundDiv = document.getElementById(`tables-${tid}-round-${round}`)
        let pouleTable = document.getElementById(`table-${tid}-${round}-${poule.letter}`)
        if (pouleTable === null) {
            // Create table
            let card = document.createElement('div')
            card.classList.add('card', 'w-100', 'my-3', `order-${poule.letter.charCodeAt(0) - 64}`)
            tablesRoundDiv.append(card)

            let cardHeader = document.createElement('div')
            cardHeader.classList.add('card-header')
            cardHeader.innerHTML = `<h2>Poule ${poule.letter}${round}</h2>`
            card.append(cardHeader)

            let cardBody = document.createElement('div')
            cardBody.classList.add('card-body')
            card.append(cardBody)

            pouleTable = document.createElement('table')
            pouleTable.id = `table-${tid}-${round}-${poule.letter}`
            pouleTable.classList.add('table', 'table-stripped')
            cardBody.append(pouleTable)

            let thead = document.createElement('thead')
            pouleTable.append(thead)

            let phaseTr = document.createElement('tr')
            thead.append(phaseTr)

            let teamTh = document.createElement('th')
            teamTh.classList.add('text-center')
            teamTh.rowSpan = poule.teams.length === 5 ? 3 : 2
            teamTh.textContent = "Équipe"
            phaseTr.append(teamTh)

            // Add columns
            for (let i = 1; i <= (poule.teams.length === 4 ? 4 : 3); ++i) {
                let phaseTh = document.createElement('th')
                phaseTh.classList.add('text-center')
                if (poule.teams.length === 5 && i < 3)
                    phaseTh.colSpan = 2
                phaseTh.textContent = `Phase ${i}`
                phaseTr.append(phaseTh)
            }

            if (poule.teams.length === 5) {
                let roomTr = document.createElement('tr')
                thead.append(roomTr)

                for (let i = 0; i < 5; ++i) {
                    let roomTh = document.createElement('th')
                    roomTh.classList.add('text-center')
                    roomTh.textContent = `Salle ${1 + (i % 2)}`
                    roomTr.append(roomTh)
                }
            }

            let problemTr = document.createElement('tr')
            thead.append(problemTr)

            for (let team of poule.teams) {
                let problemTh = document.createElement('th')
                problemTh.classList.add('text-center')
                // Problem is unknown for now
                problemTh.innerHTML = `Pb. <span id="table-${tid}-round-${round}-problem-${team}">?</span>`
                problemTr.append(problemTh)
            }

            // Add body
            let tbody = document.createElement('tbody')
            pouleTable.append(tbody)

            for (let i = 0; i < poule.teams.length; ++i) {
                let team = poule.teams[i]

                let teamTr = document.createElement('tr')
                tbody.append(teamTr)

                // First create cells, then we will add them in the table
                let teamTd = document.createElement('td')
                teamTd.classList.add('text-center')
                teamTd.innerText = team
                teamTr.append(teamTd)

                let reporterTd = document.createElement('td')
                reporterTd.classList.add('text-center')
                reporterTd.innerText = 'Déf'

                let opponentTd = document.createElement('td')
                opponentTd.classList.add('text-center')
                opponentTd.innerText = 'Opp'

                let reviewerTd = document.createElement('td')
                reviewerTd.classList.add('text-center')
                reviewerTd.innerText = 'Rap'

                // Put the cells in their right places, according to the pool size and the row number.
                if (poule.teams.length === 3) {
                    switch (i) {
                        case 0:
                            teamTr.append(reporterTd, reviewerTd, opponentTd)
                            break
                        case 1:
                            teamTr.append(opponentTd, reporterTd, reviewerTd)
                            break
                        case 2:
                            teamTr.append(reviewerTd, opponentTd, reporterTd)
                            break
                    }
                } else if (poule.teams.length === 4) {
                    let emptyTd = document.createElement('td')
                    switch (i) {
                        case 0:
                            teamTr.append(reporterTd, emptyTd, reviewerTd, opponentTd)
                            break
                        case 1:
                            teamTr.append(opponentTd, reporterTd, emptyTd, reviewerTd)
                            break
                        case 2:
                            teamTr.append(reviewerTd, opponentTd, reporterTd, emptyTd)
                            break
                        case 3:
                            teamTr.append(emptyTd, reviewerTd, opponentTd, reporterTd)
                            break
                    }
                } else if (poule.teams.length === 5) {
                    let emptyTd = document.createElement('td')
                    let emptyTd2 = document.createElement('td')
                    switch (i) {
                        case 0:
                            teamTr.append(reporterTd, emptyTd, opponentTd, reviewerTd, emptyTd2)
                            break
                        case 1:
                            teamTr.append(emptyTd, reporterTd, reviewerTd, emptyTd2, opponentTd)
                            break
                        case 2:
                            teamTr.append(opponentTd, emptyTd, reporterTd, emptyTd2, reviewerTd)
                            break
                        case 3:
                            teamTr.append(reviewerTd, opponentTd, emptyTd, reporterTd, emptyTd2)
                            break
                        case 4:
                            teamTr.append(emptyTd, reviewerTd, emptyTd2, opponentTd, reporterTd)
                            break
                    }
                }
            }
        }
    }

    /**
     * Highlight the team that is currently choosing its problem.
     * @param tid The tournament id
     * @param round The current round number, as integer (1 or 2, or 3 for ETEAM)
     * @param pool The current pool letter (A, B, C or D) (null if non-relevant)
     * @param team The current team trigram (null if non-relevant)
     */
    function updateActiveRecap(tid, round, pool, team) {
        // Remove the previous highlights
        document.querySelectorAll(`div.text-bg-secondary[data-tournament="${tid}"]`)
            .forEach(elem => elem.classList.remove('text-bg-secondary'))
        document.querySelectorAll(`li.list-group-item-success[data-tournament="${tid}"]`)
            .forEach(elem => elem.classList.remove('list-group-item-success'))
        document.querySelectorAll(`li.list-group-item-info[data-tournament="${tid}"]`)
            .forEach(elem => elem.classList.remove('list-group-item-info'))

        // Highlight current round, if existing
        let roundDiv = document.getElementById(`recap-${tid}-round-${round}`)
        if (roundDiv !== null)
            roundDiv.classList.add('text-bg-secondary')

        // Highlight current pool, if existing
        let poolLi = document.getElementById(`recap-${tid}-round-${round}-pool-${pool}`)
        if (poolLi !== null)
            poolLi.classList.add('list-group-item-success')

        // Highlight current team, if existing
        let teamLi = document.getElementById(`recap-${tid}-round-${round}-team-${team}`)
        if (teamLi !== null)
            teamLi.classList.add('list-group-item-info')

        let debugSpan = document.getElementById(`debug-problem-${tid}-team`)
        if (debugSpan && team) {
            debugSpan.innerText = team
        }
    }

    /**
     * Update the recap and the table when a team accepts a problem.
     * @param tid The tournament id
     * @param round The current round, as integer (1 or 2, or 3 for ETEAM)
     * @param team The current team trigram
     * @param problem The accepted problem, as integer
     */
    function setProblemAccepted(tid, round, team, problem) {
        // Update recap
        let recapDiv = document.getElementById(`recap-${tid}-round-${round}-team-${team}-accepted`)
        if (problem !== null) {
            recapDiv.classList.remove('text-bg-warning')
            recapDiv.classList.add('text-bg-success')
        } else {
            recapDiv.classList.add('text-bg-warning')
            recapDiv.classList.remove('text-bg-success')
        }
        recapDiv.textContent = `${team} 📃 ${problem ? problem : '?'}`

        // Update table
        let tableSpan = document.getElementById(`table-${tid}-round-${round}-problem-${team}`)
        tableSpan.textContent = problem ? problem : '?'
    }

    /**
     * Update the recap when a team rejects a problem.
     * @param tid The tournament id
     * @param round The current round, as integer (1 or 2, or 3 for ETEAM)
     * @param team The current team trigram
     * @param rejected The full list of rejected problems
     */
    function setProblemRejected(tid, round, team, rejected) {
        // Update recap
        let recapDiv = document.getElementById(`recap-${tid}-round-${round}-team-${team}-rejected`)
        recapDiv.textContent = `🗑️ ${rejected.join(', ')}`

        let penaltyDiv = document.getElementById(`recap-${tid}-round-${round}-team-${team}-penalty`)
        if (rejected.length > problems_count - RECOMMENDED_SOLUTIONS_COUNT) {
            // If more than P - 5 problems were rejected, add a penalty of 25% of the coefficient of the oral reporter
            // This is P - 6 for the ETEAM
            if (penaltyDiv === null) {
                penaltyDiv = document.createElement('div')
                penaltyDiv.id = `recap-${tid}-round-${round}-team-${team}-penalty`
                penaltyDiv.classList.add('badge', 'rounded-pill', 'text-bg-info')
                recapDiv.parentNode.append(penaltyDiv)
            }
            penaltyDiv.textContent = `❌ ${25 * (rejected.length - (problems_count - RECOMMENDED_SOLUTIONS_COUNT))} %`
        } else {
            // Eventually remove this div
            if (penaltyDiv !== null)
                penaltyDiv.remove()
        }
    }

    /**
     * For a 5-teams pool, we may reorder the pool if two teams select the same problem.
     * Then, we redraw the table and set the accepted problems.
     * @param tid The tournament id
     * @param round The current round, as integer (1 or 2, or 3 for ETEAM)
     * @param poule The pool represented by its letter
     * @param teams The teams list represented by their trigrams, ["ABC", "DEF", "GHI", "JKL", "MNO"]
     * @param problems The accepted problems in the same order than the teams, [1, 1, 2, 2, 3]
     */
    function reorderPoule(tid, round, poule, teams, problems) {
        // Redraw the pool table
        let table = document.getElementById(`table-${tid}-${round}-${poule}`)
        table.parentElement.parentElement.remove()

        updatePouleTable(tid, round, {'letter': poule, 'teams': teams})

        // Put the problems in the table
        for (let i = 0; i < teams.length; ++i) {
            let team = teams[i]
            let problem = problems[i]

            setProblemAccepted(tid, round, team, problem)

            let recapTeam = document.getElementById(`recap-${tid}-round-${round}-team-${team}`)
            recapTeam.style.order = i.toString()
        }
    }

    /**
     * Process the received data from the server.
     * @param tid The tournament id
     * @param data The received message
     */
    function processMessage(tid, data) {
        switch (data.type) {
            case 'alert':
                // Add alert message
                addMessage(data.message, data.alert_type)
                break
            case 'notification':
                // Add notification
                showNotification(data.title, data.body)
                break
            case 'set_info':
                // Update information banner
                setInfo(tid, data.information)
                break
            case 'draw_start':
                // Start the draw and update the interface
                drawStart(tid, data.trigrams)
                break
            case 'abort':
                // Abort the current draw
                drawAbort(tid)
                break
            case 'dice':
                // Update the interface after a dice launch
                updateDiceInfo(tid, data.team, data.result)
                break
            case 'dice_visibility':
                // Update the dice button visibility
                updateDiceVisibility(tid, data.visible)
                break
            case 'box_visibility':
                // Update the box button visibility
                updateBoxVisibility(tid, data.visible)
                break
            case 'buttons_visibility':
                // Update the accept/reject buttons visibility
                updateButtonsVisibility(tid, data.visible)
                break
            case 'export_visibility':
                // Update the export button visibility
                updateExportVisibility(tid, data.visible)
                break
            case 'continue_visibility':
                // Update the continue button visibility for the final tournament
                updateContinueVisibility(tid, data.visible)
                break
            case 'set_poules':
                // Set teams order and pools and update the interface
                updatePoules(tid, data.round, data.poules)
                break
            case 'set_active':
                // Highlight the team that is selecting a problem
                updateActiveRecap(tid, data.round, data.poule, data.team)
                break
            case 'set_problem':
                // Mark a problem as accepted and update the interface
                setProblemAccepted(tid, data.round, data.team, data.problem)
                break
            case 'reject_problem':
                // Mark a problem as rejected and update the interface
                setProblemRejected(tid, data.round, data.team, data.rejected)
                break
            case 'reorder_poule':
                // Reorder a pool and redraw the associated table
                reorderPoule(tid, data.round, data.poule, data.teams, data.problems)
                break
        }
    }

    function setupSocket(nextDelay = 1000) {
        // Open a global websocket
        socket = new WebSocket(
            (document.location.protocol === 'https:' ? 'wss' : 'ws') + '://' + window.location.host + '/ws/draw/'
        )

        // Listen on websockets and process messages from the server
        socket.addEventListener('message', e => {
            // Parse received data as JSON
            const data = JSON.parse(e.data)

            processMessage(data['tid'], data)
        })

        // Manage errors
        socket.addEventListener('close', e => {
            console.error('Chat socket closed unexpectedly, restarting…')
            setTimeout(() => setupSocket(2 * nextDelay), nextDelay)
        })

        // When the socket is opened, set the language in order to receive alerts in the good language
        socket.addEventListener('open', e => {
            socket.send(JSON.stringify({
                'tid': tournaments[0].id,
                'type': 'set_language',
                'language': document.getElementsByName('language')[0].value,
            }))
        })

        for (let tournament of tournaments) {
            // Manage the start form
            let format_form = document.getElementById('format-form-' + tournament.id)
            if (format_form !== null) {
                format_form.addEventListener('submit', function (e) {
                    e.preventDefault()

                    socket.send(JSON.stringify({
                        'tid': tournament.id,
                        'type': 'start_draw',
                        'fmt': document.getElementById('format-' + tournament.id).value
                    }))
                })
            }
        }
    }

    setupSocket()

    if (document.querySelector('a[href="/admin/"]')) {
        // Administrators can fake the draw
        // This is useful for debug purposes, or
        document.getElementsByTagName('body')[0].addEventListener('keyup', event => {
            if (event.key === 'f') {
                let activeTab = document.querySelector('#tournaments-tab button.active')
                let tid = activeTab.id.substring(4)

                let dice = document.getElementById(`launch-dice-${tid}`)
                let box = document.getElementById(`draw-problem-${tid}`)
                let value = NaN
                if (!dice.classList.contains('d-none')) {
                    value = parseInt(prompt("Entrez la valeur du dé (laissez vide pour annuler) :"))
                    if (!isNaN(value) && 1 <= value && value <= 100)
                        drawDice(tid, null, value)

                } else if (!box.classList.contains('d-none')) {
                    value = parseInt(prompt("Entrez le numéro du problème à choisir (laissez vide pour annuler) :"))
                    if (!isNaN(value) && 1 <= value && value <= 8)
                        drawProblem(tid, value)
                }
            }
        })
    }
})