///|
pub(all) enum MenuIndicatorPlacement {
  IndicatorStart
  IndicatorEnd
} derive(Eq, Debug)

///|
priv struct MenuPresentation {
  content : @minimoon.Node
  shortcut : String
  inset : Bool
  destructive : Bool
  close_on_select : Bool?
  indicator_placement : MenuIndicatorPlacement
}

///|
fn default_menu_presentation(label : String) -> MenuPresentation {
  {
    content: @minimoon.text(label),
    shortcut: "",
    inset: false,
    destructive: false,
    close_on_select: None,
    indicator_placement: IndicatorStart,
  }
}

///|
fn menu_item_content(
  presentation : MenuPresentation,
  checked? : Bool,
  radio? : Bool = false,
  submenu? : Bool = false,
) -> @minimoon.Node {
  let marker = match checked {
    Some(checked) =>
      ui_container(
        "menu-indicator",
        semantics=@minimoon.semantics(hidden=true),
        [
          @minimoon.text(
            if checked {
              if radio {
                "●"
              } else {
                "✓"
              }
            } else {
              ""
            },
          ),
        ],
      )
    None => @minimoon.nothing()
  }
  @minimoon.fragment([
    if presentation.indicator_placement == IndicatorStart {
      marker
    } else {
      @minimoon.nothing()
    },
    presentation.content,
    if presentation.shortcut == "" {
      @minimoon.nothing()
    } else {
      menu_shortcut(presentation.shortcut)
    },
    if presentation.indicator_placement == IndicatorEnd {
      marker
    } else {
      @minimoon.nothing()
    },
    if submenu {
      ui_container("menu-chevron", semantics=@minimoon.semantics(hidden=true), [
        @minimoon.text("›"),
      ])
    } else {
      @minimoon.nothing()
    },
  ])
}

///|
pub fn menu_customize(
  item : MenuItem,
  content? : @minimoon.Node,
  shortcut? : String = "",
  inset? : Bool = false,
  destructive? : Bool = false,
  close_on_select? : Bool,
  indicator_placement? : MenuIndicatorPlacement = IndicatorStart,
) -> MenuItem {
  {
    ..item,
    presentation: Some({
      content: content.unwrap_or(@minimoon.text(item.label)),
      shortcut,
      inset,
      destructive,
      close_on_select,
      indicator_placement,
    }),
    stamp: @minimoon.nothing(),
  }
}

///|
pub fn menu_group(
  key~ : String,
  items~ : Array[MenuItem],
  label? : String = "",
) -> MenuItem {
  {
    key,
    label,
    disabled: false,
    kind: Group(items),
    presentation: None,
    stamp: @minimoon.nothing(),
  }
}

///|
pub fn[C : @minimoon.IsChildren] menu_shortcut(children : C) -> @minimoon.Node {
  ui_container("menu-shortcut", children.to_nodes())
}

///|
/// A controlled menubar has at most one active menu; all children share one
/// application-owned value and report selection through its Emit.
pub fn menubar_controlled(
  context : UiContext,
  id~ : String,
  groups~ : Array[MenuGroup],
  value~ : @minimoon.Val[String?],
  on_value_change~ : @minimoon.Emit[String?],
) -> @minimoon.Val[@minimoon.Node] {
  let keys : Set[String] = Set([])
  for group in groups {
    guard group.key != "" && !keys.contains(group.key) else {
      abort("menubar keys must be unique and nonempty")
    }
    keys.add(group.key)
  }
  stack(
    groups.map(group => {
      dropdown_menu_controlled(
        context,
        id=id + "-" + group.key,
        trigger_label=group.label,
        items=group.items,
        open=value.map(value => value == Some(group.key)),
        on_change=on_value_change.map(open => {
          if open {
            Some(group.key)
          } else {
            None
          }
        }),
      )
    }),
  ).map(content => ui_container("menubar", [content]))
}