#let path = sys.inputs.at("path", default: "menu.json")
#let all-combos = sys.inputs.at("all-combos", default: "false") == "true"
#let categories = sys.inputs.at("categories", default: "E,D,C,V").split(",")
#let dark = sys.inputs.at("dark", default: "false") == "true"
#let days = json(path)
#let nbsp = sym.space.nobreak

#let colors = if dark {(
  bg: rgb("#1f1f24"),
  text: white,
  categories: rgb("#70c947"),
  menu-sep: rgb("#ababab"),
  title-underline: rgb("#eb2324"),
  day-sep: rgb("#a3cf40")
)} else {(
  bg: white,
  text: black,
  categories: rgb("#577F25"),
  menu-sep: rgb("#ababab"),
  title-underline: rgb("#e34243"),
  day-sep: rgb("#82BC00")
)}

#let width = if days.len() > 1 {20cm} else {10cm}

#set page(
  margin: 0cm,
  height: auto,
  width: width,
  fill: colors.bg
)

#set text(
  fill: colors.text
)

#let parse-date(date-txt) = {
  let (year, month, day) = date-txt.split("-")
  return datetime(year: int(year), month: int(month), day: int(day))
}

#let fmt-number(
  number,
  decimals: 2,
  decimal-sep: ".",
  thousands-sep: "'"
) = {
  let integer = calc.trunc(number)
  let decimal = calc.fract(number)
  let res = str(integer).clusters()
                        .rev()
                        .chunks(3)
                        .map(c => c.join(""))
                        .join(thousands-sep)
                        .rev()
  
  if decimals != 0 {
    res += decimal-sep
    decimal = decimal * calc.pow(10, decimals)
    decimal = calc.round(decimal)
    decimal = str(decimal) + ("0" * decimals)
    res += decimal.slice(0, decimals)
  }

  return res
}

#let day-names = (
  "Lundi",
  "Mardi",
  "Mercredi",
  "Jeudi",
  "Vendredi",
  "Samedi",
  "Dimanche"
)

#let display-menus(
  days,
  categories,
  day-sep-stroke: colors.day-sep + 1pt,
  title-underline-stroke: colors.title-underline + 2pt,
  menu-sep-stroke: colors.menu-sep + 1pt,
  price-categ-color: colors.categories
) = {
  set text(font: "Liberation Sans", hyphenate: true)
  let cols = calc.min(2, days.len())

  let cells = ()

  for day in days {
    let date = parse-date(day.date)
    let weekday = date.weekday() - 1
    let day-name = day-names.at(weekday)
    let menus = ()
    for menu in day.menus {
      let price-cell = grid.cell(rowspan: 2)[]
      let prices = menu.at(
        "prices",
        default: (:)
      )
      if menu.keys().contains("price") {
        prices = ("": menu.price)
      }

      let pairs = prices.pairs()
                        .filter(p => p.first() in categories)
                        .sorted(key: p => p.last())
      
      let prices = pairs.map(p => {
        let categ = emph(
          text(
            fill: price-categ-color,
            p.first()
          )
        )
        let price = fmt-number(p.last())
        categ + ":" + nbsp + "CHF" + nbsp + price
      })
      price-cell = grid.cell(
        rowspan: menu.extra.len() + 1,
        stack(dir: ttb, spacing: .4em, ..prices)
      )
      //let price = "CHF" + sym.space.nobreak + str(menu.price)
      menus.push(
        grid(
          columns: (1fr, auto),
          column-gutter: .4em,
          row-gutter: .4em,
          [*#menu.name*],
          price-cell,
          ..menu.extra.map(l => text(10pt, l))
        )
      )
    }

    let title = align(
      center,
      stack(
        dir: ttb,
        spacing: .5em,
        text(size: 14pt, weight: "bold", day-name),
        text(size: 10pt, date.display("[day].[month]"))
      )
    )
    let title-box = box(
      width: 50%,
      inset: .6em,
      stroke: (bottom: title-underline-stroke),
      title
    )
    let title-cell = grid.cell(align: center, title-box)
    
    cells.push(box(width: 100%, grid(
      columns: (100%),
      inset: (x, y) => (
        top: if y == 0 {0pt} else {.8em},
        bottom: if y == 0 {0pt} else {2em}
      ),
      stroke: (_, y) => (top: if y < 2 {none} else {menu-sep-stroke}),
      title-cell,
      ..menus
    )))
  }

  if calc.rem(cells.len(), 2) == 1 {
    cells.last() = grid.cell(colspan: calc.min(2, cols), cells.last())
  }
  
  grid(
    columns: (100% / cols,) * cols,
    inset: (x: 1.5em, y: .8em),
    stroke: (x, y) => {
      let borders = (:)
      if x != 0 {
        borders.insert("left", day-sep-stroke)
      }
      if y != 0 {
        borders.insert("top", day-sep-stroke)
      }
      return borders
    },
    ..cells
  )
}

#{
  if all-combos {
    let n-combos = calc.pow(2, categories.len())
    for i in range(n-combos) {
      let filtered-categories = categories.enumerate().filter(p => {
        let (j, categ) = p
        return i.bit-rshift(j).bit-and(1) != 0
      }).map(p => p.last())
      display-menus(days, filtered-categories)
      pagebreak(weak: true)
    }
    
  } else {
    display-menus(days, categories)
  }
}
