content: /* Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements. See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership. The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License. You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an
   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   * KIND, either express or implied. See the License for the
   * specific language governing permissions and limitations
   * under the License. */
  package org.apache.causeway.viewer.wicket.ui.components.actionlinks.serviceactions;
  
  import java.util.List;
  
  import org.apache.wicket.AttributeModifier;
  import org.apache.wicket.Component;
  import org.apache.wicket.MarkupContainer;
  import org.apache.wicket.model.Model;
  import org.jspecify.annotations.Nullable;
  
  import org.apache.causeway.commons.collections.Can;
  import org.apache.causeway.commons.internal.base._Casts;
  import org.apache.causeway.commons.internal.collections._Lists;
  import org.apache.causeway.viewer.commons.model.decorators.ActionDecorators.ActionDecorationModel;
  import org.apache.causeway.viewer.commons.model.decorators.ActionDecorators.ActionStyle;
  import org.apache.causeway.viewer.wicket.model.links.Menuable;
  import org.apache.causeway.viewer.wicket.ui.components.widgets.actionlink.ActionLink;
  import org.apache.causeway.viewer.wicket.ui.util.Wkt;
  import org.apache.causeway.viewer.wicket.ui.util.WktComponents;
  import org.apache.causeway.viewer.wicket.ui.util.WktDecorators;
  
  import lombok.Getter;
  import lombok.experimental.Accessors;
  
  class CssMenuItem
  implements Menuable {
  
      private static final long serialVersionUID = 1L;
      private static final String ID_MENU_LINK = "menuLink";
      private static final String ID_MENU_LABEL = "menuLabel";
      private static final String ID_SUB_MENU_ITEMS = "subMenuItems";
  
      public static CssMenuItem newMenuItemWithLink(final String name, final ActionLink actionLink) {
          return new CssMenuItem(name, Menuable.Kind.LINK, actionLink);
      }
  
      public static CssMenuItem newMenuItemWithSubmenu(final String name) {
          return new CssMenuItem(name, Menuable.Kind.SUBMENU);
      }
  
      //refactoring hint: perhaps use Menuable.SectionSeparator instead
      public static CssMenuItem newSpacer() {
          return new CssMenuItem("---", Menuable.Kind.SECTION_SEPARATOR);
      }
  
      //refactoring hint: perhaps use Menuable.SectionLabel instead
      public static CssMenuItem newSectionLabel(final String named) {
          return new CssMenuItem(named, Menuable.Kind.SECTION_LABEL);
      }
  
      @Getter private final String name;
      /**
       * only available for kind LINK, otherwise null
       */
      private final ActionLink actionLink;
      public ActionLink actionLinkElseFail() {
          if(actionLink==null) throw new NullPointerException("this menu item has no action link");
          return actionLink;
      }
  
      private CssMenuItem(final String name, final Menuable.Kind menuableKind) {
          this(name, menuableKind, null);
      }
  
      private CssMenuItem(final String name, final Menuable.Kind menuableKind,
              final @Nullable ActionLink actionLink) {
          this.name = name;
          this.menuableKind = menuableKind;
          this.actionLink = actionLink;
      }
  
      private final List<CssMenuItem> subMenuItems = _Lists.newArrayList();
      protected final void addSubMenuItem(final CssMenuItem cssMenuItem) {
          subMenuItems.add(cssMenuItem);
      }
      public Can<CssMenuItem> getSubMenuItems() {
          return Can.ofCollection(subMenuItems);
      }
      public boolean hasSubMenuItems() {
          return subMenuItems.size() > 0;
      }
  
      // //////////////////////////////////////////////////////////////
      // Build wicket components from the menu item.
      // //////////////////////////////////////////////////////////////
  
      void addTo(final MarkupContainer markupContainer) {
  
          final Component menuItemComponent = addMenuItemComponentTo(markupContainer);
          addSubMenuItemComponentsIfAnyTo(markupContainer);
  
          addCssClassAttributesIfRequired(menuItemComponent);
      }
  
      private Component addMenuItemComponentTo(final MarkupContainer markupContainer) {
  
          var label = Wkt.labelAdd(markupContainer, CssMenuItem.ID_MENU_LABEL, this::getName);
  
          if (actionLink!=null) {
  
              // show link...
              markupContainer.add(actionLink);
  
              WktDecorators.decorateMenuAction(
                      actionLink, actionLink, label,
                      ActionDecorationModel.of(actionLinkElseFail(), ActionStyle.MENU_ITEM));
  
              // .. and hide label
              WktComponents.permanentlyHide(markupContainer, CssMenuItem.ID_MENU_LABEL);
              return actionLink;
          } else {
              // hide link...
              WktComponents.permanentlyHide(markupContainer, ID_MENU_LINK);
              label.add(new AttributeModifier("class", Model.of("disabled")));
              return label;
          }
      }
  
      private void addSubMenuItemComponentsIfAnyTo(final MarkupContainer menuItemMarkup) {
          var subMenuItems = getSubMenuItems();
          if (subMenuItems.isEmpty()) {
              WktComponents.permanentlyHide(menuItemMarkup, CssMenuItem.ID_SUB_MENU_ITEMS);
          } else {
              menuItemMarkup.add(
                      new CssSubMenuItemsPanel(CssMenuItem.ID_SUB_MENU_ITEMS, subMenuItems));
          }
      }
  
      private void addCssClassAttributesIfRequired(final Component linkComponent) {
          if (!hasSubMenuItems()) {
              return;
          }
          Wkt.cssAppend(linkComponent, this.hasParent() ? "parent" : "top-parent");
      }
  
      @Getter private CssMenuItem parent;
      protected void setParent(final CssMenuItem parent) {
          this.parent = parent;
          parent.addSubMenuItem(_Casts.uncheckedCast(this));
      }
      public boolean hasParent() {
          return parent != null;
      }
  
      @Getter(onMethod_={@Override}) @Accessors(fluent=true)
      private final Menuable.Kind menuableKind;
  
  }
  