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.layout.bs.col;
  
  import java.util.LinkedHashMap;
  import java.util.List;
  import java.util.stream.Collectors;
  
  import org.apache.wicket.Component;
  import org.apache.wicket.markup.html.WebMarkupContainer;
  import org.apache.wicket.model.Model;
  
  import org.apache.causeway.applib.layout.component.ActionLayoutData;
  import org.apache.causeway.applib.layout.component.CollectionLayoutData;
  import org.apache.causeway.applib.layout.component.DomainObjectLayoutData;
  import org.apache.causeway.applib.layout.component.FieldSet;
  import org.apache.causeway.applib.layout.grid.bootstrap.BSCol;
  import org.apache.causeway.applib.layout.grid.bootstrap.BSRow;
  import org.apache.causeway.applib.layout.grid.bootstrap.BSTab;
  import org.apache.causeway.applib.layout.grid.bootstrap.BSTabGroup;
  import org.apache.causeway.commons.collections.Can;
  import org.apache.causeway.commons.internal.base._NullSafe;
  import org.apache.causeway.commons.internal.collections._Lists;
  import org.apache.causeway.core.metamodel.object.ManagedObject;
  import org.apache.causeway.viewer.commons.model.components.UiComponentType;
  import org.apache.causeway.viewer.wicket.model.models.ActionModel;
  import org.apache.causeway.viewer.wicket.model.models.UiObjectWkt;
  import org.apache.causeway.viewer.wicket.ui.ComponentFactory;
  import org.apache.causeway.viewer.wicket.ui.components.actionlinks.entityactions.ActionLinksPanel;
  import org.apache.causeway.viewer.wicket.ui.components.collection.parented.ParentedCollectionPanelFactory.CollectionOwnerAndLayout;
  import org.apache.causeway.viewer.wicket.ui.components.layout.bs.row.Row;
  import org.apache.causeway.viewer.wicket.ui.components.layout.bs.tabs.TabGroupPanel;
  import org.apache.causeway.viewer.wicket.ui.components.object.fieldset.PropertyGroup;
  import org.apache.causeway.viewer.wicket.ui.panels.HasDynamicallyVisibleContent;
  import org.apache.causeway.viewer.wicket.ui.panels.PanelAbstract;
  import org.apache.causeway.viewer.wicket.ui.util.Wkt;
  import org.apache.causeway.viewer.wicket.ui.util.WktComponents;
  
  public class Col
  extends PanelAbstract<ManagedObject, UiObjectWkt>
  implements HasDynamicallyVisibleContent {
  
      private static final long serialVersionUID = 1L;
  
      private static final String ID_COL = "col";
      private static final String ID_OBJECT_HEADER_PANEL = "objectHeaderPanel";
      private static final String ID_ROWS = "rows";
      private static final String ID_TAB_GROUPS = "tabGroups";
      private static final String ID_FIELD_SETS = "fieldSets";
      private static final String ID_COLLECTIONS = "collections";
  
      private final BSCol bsCol;
  
      public Col(
              final String id,
              final UiObjectWkt objectModel, final BSCol bsCol) {
  
          super(id, objectModel);
  
          this.bsCol = bsCol;
  
          buildGui();
      }
  
      private void buildGui() {
  
          setRenderBodyOnly(true);
  
          if(bsCol.getSpan() == 0) {
              WktComponents.permanentlyHide(this, ID_COL);
              return;
          }
  
          final WebMarkupContainer div = new WebMarkupContainer(ID_COL);
  
          Wkt.cssAppend(div, bsCol.toCssClass());
  
          // icon/title
          final DomainObjectLayoutData domainObject = bsCol.getDomainObject();
  
          final WebMarkupContainer actionOwner;
          final String actionIdToUse;
          final String actionIdToHide;
          if(domainObject != null) {
              final WebMarkupContainer entityHeaderPanel = new WebMarkupContainer(ID_OBJECT_HEADER_PANEL);
              div.add(entityHeaderPanel);
              final ComponentFactory componentFactory =
                      getComponentFactoryRegistry().findComponentFactory(UiComponentType.OBJECT_ICON_TITLE_AND_COPYLINK, getModel());
              final Component component = componentFactory.createComponent(getModel());
              entityHeaderPanel.addOrReplace(component);
  
              actionOwner = entityHeaderPanel;
              actionIdToUse = "objectActions";
              actionIdToHide = "actions";
  
              visible = true;
          } else {
              WktComponents.permanentlyHide(div, ID_OBJECT_HEADER_PANEL);
              actionOwner = div;
              actionIdToUse = "actions";
              actionIdToHide = null;
          }
  
          // actions
          // (rendering depends on whether also showing the icon/title)
          final List<ActionLayoutData> actionLayoutDataList = bsCol.getActions();
  
          var visibleActions = _NullSafe.stream(actionLayoutDataList)
          .filter(actionLayoutData -> actionLayoutData.getMetadataError() == null)
          .filter(_NullSafe::isPresent)
          .map(actionLayoutData ->
              getModel().getTypeOfSpecification().getAction(actionLayoutData.getId()).orElse(null)
          )
          .filter(_NullSafe::isPresent)
          .map(act->ActionModel.forEntity(act, getModel()))
          .collect(Can.toCan());
  
          if (!visibleActions.isEmpty()) {
              ActionLinksPanel.addActionLinks(actionOwner, actionIdToUse, visibleActions, ActionLinksPanel.Style.INLINE_LIST);
              visible = true;
          } else {
              WktComponents.permanentlyHide(actionOwner, actionIdToUse);
          }
          if(actionIdToHide != null) {
              WktComponents.permanentlyHide(div, actionIdToHide);
          }
  
          // rows
          final List<BSRow> rows = _Lists.newArrayList(this.bsCol.getRows());
          if(!rows.isEmpty()) {
              final RepeatingViewWithDynamicallyVisibleContent rowsRv = buildRows(ID_ROWS, rows);
              div.add(rowsRv);
              visible = visible || rowsRv.isVisible();
          } else {
              WktComponents.permanentlyHide(div, ID_ROWS);
          }
  
          // tab groups
          final List<BSTabGroup> tabGroupsWithNonEmptyTabs =
                  _NullSafe.stream(bsCol.getTabGroups())
                  .filter(_NullSafe::isPresent)
                  .filter(bsTabGroup ->
                          _NullSafe.stream(bsTabGroup.getTabs())
                                  .anyMatch(BSTab.Predicates.notEmpty())
                  )
                  .collect(Collectors.toList());
  
          if(!tabGroupsWithNonEmptyTabs.isEmpty()) {
              final RepeatingViewWithDynamicallyVisibleContent tabGroupRv =
                      new RepeatingViewWithDynamicallyVisibleContent(ID_TAB_GROUPS);
  
              for (BSTabGroup bsTabGroup : tabGroupsWithNonEmptyTabs) {
  
                  final String id = tabGroupRv.newChildId();
                  final List<BSTab> tabs = _NullSafe.stream(bsTabGroup.getTabs())
                          .filter(BSTab.Predicates.notEmpty())
                          .collect(Collectors.toList());
  
                  switch (tabs.size()) {
                  case 0:
                      // shouldn't occur; previously have filtered these out
                      throw new IllegalStateException("Cannot render tabGroup with no tabs");
                  case 1:
                      if(bsTabGroup.isCollapseIfOne() == null || bsTabGroup.isCollapseIfOne()) {
                          final BSTab bsTab = tabs.get(0);
                          // render the rows of the one-and-only tab of this tab group.
                          final List<BSRow> tabRows = bsTab.getRows();
                          final RepeatingViewWithDynamicallyVisibleContent rowsRv = buildRows(id, tabRows);
                          tabGroupRv.add(rowsRv);
                          break;
                      }
                      // else fall through...
                  default:
                      final WebMarkupContainer tabGroup = new TabGroupPanel(id, getModel(), bsTabGroup);
  
                      tabGroupRv.add(tabGroup);
                      break;
                  }
  
              }
              div.add(tabGroupRv);
              visible = visible || tabGroupRv.isVisible();
          } else {
              WktComponents.permanentlyHide(div, ID_TAB_GROUPS);
          }
  
          // fieldsets
          final List<FieldSet> fieldSetsWithProperties =
                  _NullSafe.stream(bsCol.getFieldSets())
                  .filter(_NullSafe::isPresent)
                  .filter(fieldSet -> ! _NullSafe.isEmpty(fieldSet.getProperties()))
                  .collect(Collectors.toList());
  
          if(!fieldSetsWithProperties.isEmpty()) {
              final RepeatingViewWithDynamicallyVisibleContent fieldSetRv =
                      new RepeatingViewWithDynamicallyVisibleContent(ID_FIELD_SETS);
  
              for (FieldSet fieldSet : fieldSetsWithProperties) {
  
                  final String id = fieldSetRv.newChildId();
  
                  final PropertyGroup propertyGroup = new PropertyGroup(id, getModel(), fieldSet);
                  fieldSetRv.add(propertyGroup);
              }
              div.add(fieldSetRv);
              visible = visible || fieldSetRv.isVisible();
          } else {
              WktComponents.permanentlyHide(div, ID_FIELD_SETS);
          }
  
          final UiObjectWkt objectModel = getModel();
          var ownerSpec = objectModel.getManagedObject().objSpec();
  
          // collection layout data by collection id (the collection's member-id)
          // preserving order, as order matters
          var collectionLayoutById = new LinkedHashMap<String, CollectionLayoutData>();
          _NullSafe.stream(bsCol.getCollections())
              .filter(colLayoutData->colLayoutData.getMetadataError() == null)
              .filter(colLayoutData->!ownerSpec.getCollection(colLayoutData.getId()).isEmpty())
              .forEach(colLayoutData->collectionLayoutById.put(colLayoutData.getId(), colLayoutData));
  
          if(!collectionLayoutById.isEmpty()) {
              final RepeatingViewWithDynamicallyVisibleContent collectionRv =
                      new RepeatingViewWithDynamicallyVisibleContent(ID_COLLECTIONS);
  
              collectionLayoutById.forEach((id, layout)->{
  
                  var helperModel = Model.of(new CollectionOwnerAndLayout(objectModel, layout));
  
                  final ComponentFactory componentFactory =
                          getComponentFactoryRegistry().findComponentFactory(
                                  UiComponentType.PARENTED_COLLECTION, helperModel);
  
                  final Component collectionPanel = componentFactory.createComponent(collectionRv.newChildId(), helperModel);
                  collectionRv.add(collectionPanel);
              });
  
              div.add(collectionRv);
              visible = visible || collectionRv.isVisible();
  
          } else {
              WktComponents.permanentlyHide(div, ID_COLLECTIONS);
          }
  
          final WebMarkupContainer panel = this;
          if(visible) {
              panel.add(div);
          } else {
              WktComponents.permanentlyHide(panel, div.getId());
          }
  
      }
  
      private RepeatingViewWithDynamicallyVisibleContent buildRows(final String owningId, final List<BSRow> rows) {
          final RepeatingViewWithDynamicallyVisibleContent rowRv =
                  new RepeatingViewWithDynamicallyVisibleContent(owningId);
  
          for(final BSRow bsRow: rows) {
              final String id = rowRv.newChildId();
              final Row row = new Row(id, getModel(), bsRow);
              rowRv.add(row);
          }
          return rowRv;
      }
  
      private boolean visible = false;
      @Override
      public boolean isVisible() {
          return visible;
      }
  
  }
  