#import "@preview/hydra:0.6.1": hydra

#import "matisse-thesis-var.typ": school-color-verso
#import "cover.typ": cover
#import "abstracts.typ": abstracts
#import "todos.typ": todos, show-todos, show-notes
#import "etablissements.typ": *

#let matisse-thesis(
  // English title, will be used for the document title metadata
  title-en: "",
  title-fr: "",
  author: "",
  affiliation: "",
  defense-place: "",
  defense-date: "",
  jury-content: [],
  university: "UR",
  speciality: "Informatique",
  // List of keyword for the abstract
  keywords-en: (),
  // List of french keywords for the abstract
  keywords-fr: (),
  abstract-en: [],
  abstract-fr: [],
  // If the current document is a draft
  draft: true,
  bibliography-style: "association-for-computing-machinery",
  // text font
  font: "New Computer Modern",
  // font for heading
  heading-font: "TeX Gyre Heros",
  // font for abstract
  abstract-font: "TeX Gyre Heros",
  // font size for abstract
  abstract-font-size: 11pt,
  // text lang
  lang: "en",
  school-color-verso: school-color-verso,
  show_cover: true,
  show_body: true,
  show_abstracts: true,
  preliminary: false,
  body
) = {
  assert-etablissements(university)

  if draft {
    set heading(numbering: none, outlined: false, bookmarked: true)
    [== DRAFT - #title-en]
    // if typst was called with `--input commit=$(git rev-parse --short HEAD)`, add the current commit:
    if "commit" in sys.inputs {
      [Current version: commit #sys.inputs.commit \ \ ]
    }
    todos() 
  } else {
    show-todos.update(false)
    show-notes.update(false)
  }

  // ---------- GENERAL ----------

  let draft-string = ""
  if draft { draft-string = "DRAFT - " }
  let preliminary-string = ""
  if preliminary { preliminary-string = " - PRELIMINARY VERSION" }

  set document(
    title: draft-string + title-en + preliminary-string,
    author: author,
  )
  set par(justify: true)
  set text(font: font, fill: black, lang: lang)

  // ---------- PAGE FORMAT ---------- //

  set page("a4",
    margin: (outside: 20mm, inside: 30mm, top: 50mm, bottom: 50mm),
    numbering: "1",
    number-align: center,
    header: context {
      // reset footnote numbering at each pages
      counter(footnote).update(0)
      // disable linebreaks in header
      show linebreak: none
      // get the current page number
      let current-page = here().page()

      let all-lvl1 = query(heading.where(level: 1))
      if all-lvl1.any(it => it.location().page() == current-page) {
        return
      }

      // if the page is odd
      if calc.odd(current-page) {
        // display the last level-1 heading
        let header-content = hydra(1,
          display: (_, it) => {
            if it.numbering != none {
              let nb = counter(heading).at(it.location())
              let nb-fmt = numbering(it.numbering, ..nb)
              [#it.supplement #nb-fmt -- _ #it.body _ ]
            } else { emph(it.body) }
          }
        )
        text(0.35cm, header-content)
      }
      // if the page is even
      else {
        // display last level-2 heading (current page included)
        let header-content = hydra(2, use-last: true,
          display: (_, it) => {
            if it.numbering == none [_ #it.body _]
            else {
              let nb = counter(heading).at(it.location())
              let nb-fmt = numbering(
                it.numbering.replace(" ", "."),
                ..nb
              )
              [ _ #nb-fmt #it.body _ ]
            }
          }
        )
        align(right, text(0.35cm, header-content))
      }
      // horizontal rule
      v(-.3cm)
      line(length: 100%, stroke: .2mm)
    }
  )

  // ---------- HEADINGS ----------
  
  // for the thesis' body:
  // 1. headings are normally numbered
  // (there is a small space after the last number)
  
  set heading(numbering: "1.1 ")
  // 2. level-1 headings are called chapters
  show heading.where(level: 1): set heading(supplement: [Chapter])

  // for level-1 headings
  show heading.where(level: 1): it => context {
    // always start on odd pages
    pagebreak(to: "odd")
    set align(right)
    v(-.8cm)
    // if numbering is enabled, display level-1 heading number
    if it.numbering != none {
      let sec-nb = counter(heading).get().first()
      let fmt-nb = numbering(heading.numbering, sec-nb)
      text(
        smallcaps[#heading.supplement #fmt-nb \ ],
        size: .45cm, weight: "regular", font: font,
      )
      v(0cm)
    }
    // level-1 heading name
    text(smallcaps(it.body), font: heading-font, size: .9cm)
    set align(left)
    // horizontal rule
    v(.7cm)
    line(length: 100%, stroke: .2mm)
    v(.7cm)
  }


  // ---------- FIGURES ---------- 
  show figure.caption: it => box(
    inset: (left: 1em, right: 1em),
    //align(left, it)
    it
  )

  set figure(placement: auto)

  // ---------- LISTING ----------
  show raw.where(block: true): it => {
    // Line number, not verry good (cannot copy code whitout line number)
    // see https://github.com/typst/typst/issues/344
    show raw.line: line => {
      if line.number < 10 {
        text(fill: gray)[~#line.number]
      } else {
        text(fill: gray)[#line.number]
      }
      h(1em)
      line.body
    }

    block(radius: 1em, fill: luma(240), inset: 1em, width: 90%, {
      set align(left)
      it
    })
  }

  // ---------- OUTLINES ----------
  
  // justify outline entries
  show outline.entry: set par(justify: true)

  show outline: out => {
    show outline.entry.where(level: 1): ent => {
      // for the table of contents
      if out.target == selector(heading) {
        block(above: 1.2em)[#ent]
      }
      // for other types of outlines
      else {
        link(
          ent.element.location(),
          [#ent.prefix(): #h(.5em) #ent.inner()]
        )
      }
    }
    out
  }

  // disable linebreaks in outlines
  show outline.entry: it => {
    show linebreak: none
    it
  }


  // ---------- FOOTNOTES ----------

  show footnote.entry: it => {
    let loc = it.note.location()
    block(breakable: false, {
      numbering(
        "1. ",
        ..counter(footnote).at(loc),
      )
      it.note.body
    })
  }

  // ---------- BIBLIOGRAPHY ----------

  set bibliography(style: bibliography-style)

  // ---------- COVER PAGE ----------

  if show_cover {
    cover(
      title-en: title-en,
      title-fr: title-fr,
      author: author,
      affiliation: affiliation,
      defense-place: defense-place,
      defense-date: defense-date,
      jury-content: jury-content,
      university: university,
      speciality: speciality,
      heading-font: heading-font,
      preliminary: preliminary,
    )
  }

  // ---------- BODY ----------
  
  set par.line(numbering: n => text(red)[#n]) if draft
  show figure: set par.line(numbering: none) if draft

  if show_body {
    body
  }

  // ---------- ABSTRACT ----------
  if show_abstracts {
    abstracts(
      university: university,
      title-fr: title-fr, keywords-fr: keywords-fr, abstract-fr: abstract-fr,
      title-en: title-en, keywords-en: keywords-en, abstract-en: abstract-en,
      font: abstract-font, font-size: abstract-font-size, school-color-verso: school-color-verso,
      align-to-even-page: show_body, // Don't add blank pages when the body is not shown
    )
  }
}
