/// This module defines all handout "objects"
/// (problems, theorems, definitions, etc)

/// Core render code for all objects (problems, theorems, etc)
/// This should never be used directly by client code.
///
/// Args:
/// - kind: the kind of object to make ("Problem", "Definition", etc)
/// - label_name: a string. If provided, generate metadata for this object
///   under the given label. Labels must be unique.
///   This label can then be used to reference this object.
///
/// For example:
/// ```
/// #problem(label: "problem1")
/// This is @problem1
/// ```
#let _obj_base(kind, ..args, label_name: none) = {
  counter("obj").step()
  let n = context counter("obj").get().first()

  // The complete title text of this object,
  // like "Problem 5:" or "Theorem: "
  let obj_content = if args.pos().len() == 0 {
    [#kind #n:]
  } else {
    [#kind #n: #args.pos().at(0)]
  }

  // Render the object
  block(
    above: 8mm,
    below: 2mm,
    text(weight: "bold", obj_content),
  )

  // Generate labeled metadata for this object.
  //
  // This can be viewed directly with `#context query(<label>).first().value`,
  // Or referenced with `@label` (we define a custom renderer for this metadata later)
  if label_name != none {
    let meta = (
      "obj_meta_ref_kind": kind,
      // "obj_content": obj_content,
      "label": label(label_name),
      "counter": counter("obj"),
    )
    [ #metadata(meta) #label(label_name) ]
  }
}

// `ref` implementation for object meta-references.
// Returns `none` if `it` is not object metadata.
#let ref_obj(it) = {
  let magic_key = "obj_meta_ref_kind"
  if not (
    it.element != none
      and it.element.has("value")
      and type(it.element.value) == "dictionary"
      and it.element.value.keys().contains(magic_key)
  ) {
    // This label is not attached to object metadata
    return none
  }

  let v = it.element.value
  let obj_type = v.at(magic_key)

  // The value of this object's counter at its label
  let obj_count = v.counter.at(v.label).first()

  // Produces text like "Problem 2",
  // which takes you to the referenced object when clicked.
  return link(v.label, [#obj_type #obj_count])
}

/// Factory function for objects.
/// Provided for convenience, lets us define objects in one line.
#let _mkobj(kind) = {
  let out(..args, label: none) = _obj_base(
    kind,
    ..args,
    label_name: label,
  )

  return out
}


//
// MARK: export
//
// Functions for client code are defined below

#let problem = _mkobj("Problem")
#let definition = _mkobj("Definition")
#let theorem = _mkobj("Theorem")
#let example = _mkobj("Example")
#let remark = _mkobj("Remark")

#let generic(obj_content) = {
  block(
    above: 8mm,
    below: 2mm,
    text(weight: "bold", obj_content),
  )
}