#import "/src/cetz.typ": draw, styles

#import "/src/consts.typ": *
#import "/src/core/utils.typ": expand-parent-group, get-ctx, normalize-units, set-ctx

#let group-default-style = (
  name: (
    inset: (
      x: 5pt,
      y: 3pt
    ),
    stroke: auto,
    fill: auto
  ),
  desc: (
    inset: 3pt
  ),
  divider: (dash: (2pt, 1pt), thickness: .5pt),
  padding: 10pt,
  stroke: auto
)

#let render-start(grp) = get-ctx(ctx => {
  let style = styles.resolve(
    ctx.style,
    merge: grp.style,
    root: "group",
    base: group-default-style
  )

  let grp = grp
  grp.insert("resolved-style", style)
  ctx.y -= ctx.style.y-space

  let name = box(
    text(grp.name, weight: "bold"),
    inset: style.name.inset
  )
  grp.insert("rendered-name", name)

  let desc = box(
    text([\[#grp.desc\]], weight: "bold", size: .8em),
    inset: style.desc.inset
  )
  grp.insert("rendered-desc", desc)

  let m = measure(name)
  ctx.groups = ctx.groups.map(g => {
    if g.group.min-i == grp.min-i { g.start-lvl += 1 }
    if g.group.max-i == grp.max-i { g.end-lvl += 1 }
    g
  })
  if grp.grp-type == "alt" {
    grp.insert("elses", ())
  }
  ctx.groups.push((
    start-y: ctx.y,
    group: grp,
    start-lvl: 0,
    end-lvl: 0,
    min-x: ctx.x-pos.at(grp.min-i) - 10,
    max-x: ctx.x-pos.at(grp.max-i) + 10
  ))
  ctx.y -= m.height / 1pt
  ctx.y += ctx.style.y-space / 2

  set-ctx(c => {
    c.y = ctx.y
    c.groups = ctx.groups
    return c
  })
})


#let draw-group(x0, x1, y0, y1, group) = {
  let style = group.resolved-style
  let name = group.rendered-name
  let desc = group.rendered-desc
  let m = measure(name)
  let w = m.width / 1pt
  let h = m.height / 1pt
  draw.rect(
    (x0, y0),
    (x1, y1),
    stroke: style.stroke
  )

  let x1 = x0 + w
  let x2 = x1 + 5
  draw.line(
    (x0, y0),
    (x2, y0),
    (x2, y0 - h / 2),
    (x1, y0 - h),
    (x0, y0 - h),
    stroke: style.name.stroke,
    fill: style.name.fill,
    close: true
  )
  draw.content(
    (x0, y0),
    name,
    anchor: "north-west"
  )

  if group.desc != none {
    draw.content(
      (x2, y0),
      desc,
      anchor: "north-west"
    )
  }
}

#let draw-else(x0, x1, y, elmt) = {
  let style = elmt.resolved-style
  draw.line(
    (x0, y),
    (x1, y),
    stroke: style.divider
  )
  draw.content(
    (x0, y),
    elmt.rendered-desc,
    anchor: "north-west"
  )
}

#let render-end(group) = get-ctx(ctx => {
  let y = ctx.y - ctx.style.y-space / 2
  ctx.y -= ctx.style.y-space / 2
  let (
    start-y,
    group,
    start-lvl,
    end-lvl,
    min-x,
    max-x
  ) = ctx.groups.pop()
  let padding = normalize-units(group.resolved-style.padding)
  let x0 = min-x - padding
  let x1 = max-x + padding
  
  draw-group(x0, x1, start-y, y, group)

  if group.grp-type == "alt" {
    for (else-y, else-elmt) in group.elses {
      draw-else(x0, x1, else-y, else-elmt)
    }
  }

  set-ctx(c => {
    c.y = ctx.y
    c.groups = ctx.groups
    return c
  })

  expand-parent-group(x0, x1)
})

#let render-else(else_) = get-ctx(ctx => {
  let group = ctx.groups.last().group
  let style = styles.resolve(
    ctx.style,
    merge: group.style,
    root: "group",
    base: group-default-style
  )
  ctx.y -= ctx.style.y-space / 2

  let desc = box(
    text([\[#else_.desc\]], weight: "bold", size: .8em),
    inset: style.desc.inset
  )
  let m = measure(desc)
  let else_ = else_
  else_.insert("resolved-style", style)
  else_.insert("rendered-desc", desc)
  ctx.groups.last().group.elses.push((
    ctx.y, else_
  ))
  ctx.y -= m.height / 1pt
  ctx.y += ctx.style.y-space / 2
  
  set-ctx(c => {
    c.y = ctx.y
    c.groups = ctx.groups
    return c
  })
})