#import "@preview/codly:1.3.0": *
#import "@preview/codly-languages:0.1.2": *

#import "constants.typ"
#import "glossary.typ": make-glossary, print-glossary, gls, glspl
#import "utils.typ": draft, inwriting, showNumberedLines

#let localized_month(month) = {
  month = calc.clamp(month, 1, 12)

  return constants.localized_months.at(month - 1)
}

#let titlepage(
  logo,
  authors,
  collaboration,
  title,
  subtitle,
  details,
  date
) = {
  set page(margin: (rest: 2.5cm, top: 3cm, bottom: 0cm))
  page(footer: [])[
    #if logo != none {
      place(top + center, float: true, dx: 0cm, dy:2.5cm, image(logo, height:2.5cm))
      place(bottom + left, float: true, dx: 0cm, dy:-2.3cm, image("./jkeffects.svg", height: 2cm))
    }

    // Information on the center
    #set align(center)
    // #text(constants.font_size * 1.5, authors.map(author => [
    //   #author.name#if author.at("enrollment_number", default: none) != none [, #author.enrollment_number]
    // ]).join("\n"))
    // #v(1cm, weak: true)
    #v(3cm)
    #set par(leading: 0.4em)
    #text(
      constants.font_size * 2.5,
      weight: "medium",
      fill: constants.accent_color,
      title//smallcaps(title)
    )
    #if subtitle != none {
      v(0.9cm, weak: true)
      text(
        constants.font_size * 1.5,
        fill: constants.accent_color,
        subtitle
      )
    }
    #v(1fr)

    // Information on the right
    #set align(right)
    // Information on the bottom right
    #set par(leading: 0.65em)
    #for line in details {
      if line != "" [
        #line\
      ] else {
        v(0em)
      }
    }
    #v(0em)
    #date.display("[day].") #localized_month(date.month()) #date.year()
  ]
}

#let acknowledgmentpage(
  acknowledgment
) = {
  if acknowledgment != none {
    v(1fr)
    set par(first-line-indent: 0em, justify: true)

    heading(constants.acknowledgment, numbering: none)
    v(1.5em, weak: true)
    acknowledgment

    v(1fr)

    pagebreak()
  }
}

#let abstractpage(
  abstract
) = {
  if abstract != none {
    v(1fr)
    set par(first-line-indent: 0em, justify: true)

    heading(constants.abstract, numbering: none)
    v(1.5em, weak: true)
    abstract

    v(1fr)

    pagebreak()
  }
}

#let outlinepages() = {
  show ref: none
  {
    show outline.entry.where(
      level: 1
    ): it => {
      v(constants.font_size * 1.25, weak: true)
      if it.body().fields().at("children", default: none) != none {
        strong(it)
      } else {
        it
      }
    }

    // Contents
    heading(constants.contents)
    outline(
      title: none,
      target: heading.where(outlined: true),
      indent: auto
    )
  }
}

#let listspages() = {
  context {
    // Figure list
    if query(figure.where(kind: image)).len() > 2 {
      pagebreak()
      heading(constants.figure_list)
      outline(
        title: none,
        target: figure.where(kind: image),
        indent: auto
      )
    }

    // Table list
    if query(figure.where(kind: table)).len() > 2 {
      pagebreak()
      heading(constants.table_list)
      outline(
        title: none,
        target: figure.where(kind: table),
        indent: auto
      )
    }

    // Code list
    if query(figure.where(kind: "code")).len() > 2 {
      pagebreak()
      heading(constants.code_list)
      outline(
        title: none,
        target: figure.where(kind: "code"),
        indent: auto
      )
    }
  }
}

#let glossarypage(glossary) = {
  if glossary != none and glossary.len() > 0 {
    pagebreak()
    heading(constants.glossary)
    v(1.5em, weak: true)
    print-glossary(glossary, disable-back-references: true)
  }
}

#let attachmentpages(attachments, references) = {
  if attachments != none and attachments.len() > 0 {
    if references != none {
      pagebreak()
    }
    heading(constants.attachments, outlined: false)
    v(1.5em, weak: true)

    // Reset heading counter
    counter(heading).update(0)
    // Display heading count as: "Attachment X:"
    set heading(
      numbering: (..nums) => constants.attachments + " " + nums
        .pos()
        .slice(1) // All headings are level 2 headings
        .map(str)
        .join("."),
      supplement: none,
      outlined: constants.outline_other_headings
    )
    for (index, attachment) in attachments.enumerate() {
      if index != 0 {
        pagebreak()
      }
      if attachment.at("ref", default: none) != none [
        #heading(depth: 2, "- " + attachment.title)
        #label(attachment.ref)
      ] else {
        heading(depth: 2, "- " + attachment.title)
      }
      if attachment.at("description", default: none) != none [
        #set par(justify: true)
        #text(attachment.description)
      ]
      v(1em)

      set align(horizon + center)
      context{
        let (width, height) = measure(image(attachment.file))
        if width < (100% - 1pt).length and height < (80% - 1pt).length {
          image(attachment.file, fit: "contain")
        } else if height < width {
          image(attachment.file, width: 100%, fit: "contain")
        } else {
          image(attachment.file, width: 100%, height: 80%, fit: "contain")
        }
      }

      if attachment.at("note", default: none) != none {
        v(1em, weak: true)
        text(attachment.note)
      }
    }
  }
}

#let referenceattachmentpages(references, attachments) = {
  if references != none or (attachments != none and attachments.len() > 0) {
    pagebreak()
    set heading(numbering: none, outlined: constants.outline_other_headings)
    set page(numbering: "I", number-align: right, header: none)
    context {
      counter(page).update(counter(page).at(<end-intro>))
      counter(page).step()
    }

    if references != none {
      heading(constants.references)
      v(1.5em, weak: true)
      bibliography("../" + references, title: none, style: constants.bibliography_style)
    }

    listspages()

    attachmentpages(attachments, references)
  }
}

#let main(title, alttitle, authors, body) = {
  set page(numbering: "1", number-align: right)
  set heading(numbering: "1.1", outlined: true)
  show heading.where(level: 2): it => {
    v(.75em)
    it
    v(.75em)
  }
  show heading.where(level: 3): it => {
    v(.5em)
    it
    v(.5em)
  }
  // set par(justify: true, first-line-indent: 1em)
  set par.line(numbering: "1") if showNumberedLines
  show figure: set block(above: 1em, below: 1em)
  show figure.caption: set text(size: .95em)
  show figure.caption.where(kind: "source"): set text(size: .8em)
  show table: set par(justify: false)
  set page(
    header: {
      //smallcaps(
        if alttitle != none {
          alttitle
        } else {
          title
        }
      //)
      context {
        let headings_before = query(
          heading.where(level: 1, outlined: true, numbering: "1.1").before(here())
        )
        let lastheadbefore = headings_before.at(-1, default: none)
        let headings_after = query(
          heading.where(level: 1, outlined: true, numbering: "1.1").after(here())
        )
        let firstheadafter = headings_after.at(0, default: none)
        let currentpage = here().page()
        if lastheadbefore != none {
          text(" ")
          if firstheadafter == none {
            sym.dash.en
            text(" ")
            lastheadbefore.body//smallcaps(lastheadbefore.body)
          } else if firstheadafter.location().page() != currentpage {
            sym.dash.en
            text(" ")
            lastheadbefore.body//smallcaps(lastheadbefore.body)
          }
        }
      }
      //h(1fr)
      //authors.map(a => a.name).join(", ")
    }
  )
  counter(page).update(1)

  show: codly-init.with()
  codly(languages: codly-languages)

  set par(justify: true)

  body

}

#let template(
  // The title of the report
  title: [Report title],
  // An optional alternative title as page heading
  alttitle: none,
  // An optional subtitle of the report
  subtitle: none,
  // Details displayed on the cover page
  details: (),
  // Authors of the report:
  authors: (),
  // optional collaboration with
  collaboration: none,
  // Logo for the cover page. Must be in the image folder. Can be omitted.
  logo: "logo.svg",
  // Date displayed on the cover page
  date: datetime.today(),
  // The report's abstract. Can be omitted.
  abstract: none,
  // The report's disclaimer. Can be omitted.
  disclaimer: none,
  // The report's acknowledments. Can be omitted.
  acknowledgment: none,
  // Glossary for the report. Can be omitted.
  glossary: none,
  // The result of a call to the `bibliography` function. Can be omitted.
  references: none,
  // Attachments of the report. Can be omitted.
  attachments: none,
  // Keywords of the report.
  keywords: (),
  // Language of the report
  lang: "de",
  // The report's content
  body
) = {
  // Set the document's basic properties.
  set document(author: authors.map(a => a.name), title: title, keywords: keywords, date: date)

  let draft_background = none
  if draft {
    draft_background = rotate(
      45deg,
      rect(stroke: 2pt + constants.draft_color, radius: 15pt, inset: 15pt, text(100pt, fill: constants.draft_color)[*#constants.draft*])
    )
  }

  set page(paper: "a4", margin: (bottom: 3.5cm, top: 4cm, rest:2.5cm), numbering: "I", number-align: right, background: draft_background)
  set par(leading: .9em, spacing: .9em)
  set text(size: constants.font_size, font: constants.textfont, stretch: 120%, lang: lang, region: lang)
  set heading(numbering: none, outlined: constants.outline_other_headings)
  show heading.where(level: 1): set text(size: constants.font_size * 2)
  show heading.where(level: 2): set text(size: constants.font_size * 1.5)
  show heading.where(level: 3): set text(size: constants.font_size * 5/4)

  show link: set text(fill: constants.accent_color) //if inwriting
  show ref: set text(fill: constants.accent_color) //if inwriting
  show: make-glossary
  show bibliography: set text(constants.font_size)

  show math.equation: set text(size: constants.math_font_size, font: constants.mathfont)

  // Raw block format
  show raw: set text(size: constants.code_font_size, font: constants.codefont)

  titlepage(logo, authors, collaboration, title, subtitle, details, date)

  acknowledgmentpage(acknowledgment)
  abstractpage(abstract)

  show heading.where(level: 1): it => {
    pagebreak(weak: true)
    v(0cm)
    it
    v(1em)
  }

  outlinepages()
  glossarypage(glossary)

  // Main body
  [
    ~ <end-intro>
    #pagebreak()
  ]
  main(title, alttitle, authors, body)

  referenceattachmentpages(references, attachments)
}