// Glossary code by Hugo Cartigny (BlueskyFR) 🍉

#let glossary(indent-defs: false, doc) = {
  // ✨ The glossary displays its items using level 99 headings
  let glossary = state("wow", (:))
  
  // Hide the numbering for level 99 titles
  show heading.where(level: 99): it => text(weight: "regular", it.body)
  
  let page-refs-color = rgb("#7630EA")
  
  show terms: list => {
    let terms-grid = ()
    // Add terms to glossary
    for item in list.children {
      glossary.update(v => {
          v.insert(
            item.term.text,
            (
              // Holds the list of the locations referencing the term
              ref-locs: (),
              // The actual term definition
              def: item.description,
            )
          )
  
          // Return the new state with the added entry
          v
      })

      if indent-defs {
        // Term
        terms-grid.push([
          #heading(level: 99, numbering: "1")[*#item.term*]
          #label(item.term.text)
        ])
  
        // Definition
        terms-grid.push([
          #item.description
          // Pages where the term is referenced
          #show: text.with(page-refs-color)
          #locate(loc => {
            glossary.final(loc).at(item.term.text).ref-locs
              .map(l => link(l, str(l.page)))
              .join(", ")
          })
        ])
      
      } else [
        // Display items directly one by one since
        // we don't need to build a grid
        
        // Use a level 99 title so it doesn't conflict with regular ones
        // and it can be refered to by @citations
        #heading(level: 99, numbering: "1")[
          *#item.term*:~~#item.description
          // Pages where the term is referenced
          #show: text.with(page-refs-color)
          #locate(loc => {
            glossary.final(loc).at(item.term.text).ref-locs
              .map(l => link(l, str(l.page)))
              .join(", ")
          })
        ]
        #label(item.term.text) \
      ]
    }

    if indent-defs {
      grid(
        columns: (1fr, 4fr),
        column-gutter: 2mm,
        row-gutter: 8mm,
        ..terms-grid
      )
    }
  
    // 🐛 Debug
    //glossary.display()
  }
  
  show ref: r => {
    locate(loc => {
      // Search for the source of the ref
      let term = str(r.target)
      let res = query(r.target, loc)
  
      // If the source exists and is the glossary (heading level 99)
      if res.len() > 0 and res.first().level == 99 {
        let entry = glossary.at(loc).at(term)
  
        // Replace term by the user-specified supplement if not none
        let custom-term = {
          if r.citation.supplement != none { r.citation.supplement }
          else { term }
        }
        
        // If it is the first reference to the term, display its definition too
        link(res.first().location(), {
          if entry.ref-locs.len() == 0 [*#entry.def* (#custom-term)]
          else [#custom-term]
        })
  
        // Add location to the term's ref list if the current page
        // is not already listed
        glossary.update(v => {
          // If this page is not in, push the loc in!
          if v.at(term).ref-locs.all(l => l.page != loc.page()) {
            v.at(term).ref-locs.push(
              // Current page loc
              loc.position()
            )
          }
          v
        })
      }
      else { r } // Otherwise just return the ref as it is
    })
  }

  doc
}