content: /*
   * Copyright (C) 2020 The Android Open Source Project
   *
   * Licensed 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 androidx.constraintlayout.core.widgets.analyzer;
  
  import static androidx.constraintlayout.core.widgets.ConstraintWidget.BOTH;
  import static androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour.FIXED;
  import static androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour.MATCH_PARENT;
  import static androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour.WRAP_CONTENT;
  import static androidx.constraintlayout.core.widgets.ConstraintWidget.HORIZONTAL;
  import static androidx.constraintlayout.core.widgets.ConstraintWidget.VERTICAL;
  
  import androidx.constraintlayout.core.widgets.Barrier;
  import androidx.constraintlayout.core.widgets.ConstraintAnchor;
  import androidx.constraintlayout.core.widgets.ConstraintWidget;
  import androidx.constraintlayout.core.widgets.ConstraintWidgetContainer;
  import androidx.constraintlayout.core.widgets.Flow;
  import androidx.constraintlayout.core.widgets.Guideline;
  import androidx.constraintlayout.core.widgets.HelperWidget;
  
  import java.util.ArrayList;
  
  /**
   * Implements a simple grouping mechanism, to group interdependent widgets together.
   *
   * TODO: we should move towards a more leaner implementation
   *          -- this is more expensive as it could be.
   */
  public class Grouping {
  
      private static final boolean DEBUG = false;
      private static final boolean DEBUG_GROUPING = false;
      private static final boolean FORCE_USE = true;
  
      // @TODO: add description
      public static boolean validInGroup(ConstraintWidget.DimensionBehaviour layoutHorizontal,
              ConstraintWidget.DimensionBehaviour layoutVertical,
              ConstraintWidget.DimensionBehaviour widgetHorizontal,
              ConstraintWidget.DimensionBehaviour widgetVertical) {
          boolean fixedHorizontal = widgetHorizontal == FIXED || widgetHorizontal == WRAP_CONTENT
                  || (widgetHorizontal == MATCH_PARENT && layoutHorizontal != WRAP_CONTENT);
          boolean fixedVertical = widgetVertical == FIXED || widgetVertical == WRAP_CONTENT
                  || (widgetVertical == MATCH_PARENT && layoutVertical != WRAP_CONTENT);
          if (fixedHorizontal || fixedVertical) {
              return true;
          }
          return false;
      }
  
      // @TODO: add description
      public static boolean simpleSolvingPass(ConstraintWidgetContainer layout,
              BasicMeasure.Measurer measurer) {
  
          if (DEBUG) {
              System.out.println("*** GROUP SOLVING ***");
          }
          ArrayList<ConstraintWidget> children = layout.getChildren();
  
          final int count = children.size();
  
          ArrayList<Guideline> verticalGuidelines = null;
          ArrayList<Guideline> horizontalGuidelines = null;
          ArrayList<HelperWidget> horizontalBarriers = null;
          ArrayList<HelperWidget> verticalBarriers = null;
          ArrayList<ConstraintWidget> isolatedHorizontalChildren = null;
          ArrayList<ConstraintWidget> isolatedVerticalChildren = null;
  
          for (int i = 0; i < count; i++) {
              ConstraintWidget child = children.get(i);
              if (!validInGroup(layout.getHorizontalDimensionBehaviour(),
                      layout.getVerticalDimensionBehaviour(),
                      child.getHorizontalDimensionBehaviour(),
                      child.getVerticalDimensionBehaviour())) {
                  if (DEBUG) {
                      System.out.println("*** NO GROUP SOLVING ***");
                  }
                  return false;
              }
              if (child instanceof Flow) {
                  return false;
              }
          }
          if (layout.mMetrics != null) {
              layout.mMetrics.grouping++;
          }
          for (int i = 0; i < count; i++) {
              ConstraintWidget child = children.get(i);
              if (!validInGroup(layout.getHorizontalDimensionBehaviour(),
                      layout.getVerticalDimensionBehaviour(),
                      child.getHorizontalDimensionBehaviour(),
                      child.getVerticalDimensionBehaviour())) {
                  ConstraintWidgetContainer.measure(0, child, measurer,
                          layout.mMeasure, BasicMeasure.Measure.SELF_DIMENSIONS);
              }
              if (child instanceof Guideline) {
                  Guideline guideline = (Guideline) child;
                  if (guideline.getOrientation() == HORIZONTAL) {
                      if (horizontalGuidelines == null) {
                          horizontalGuidelines = new ArrayList<>();
                      }
                      horizontalGuidelines.add(guideline);
                  }
                  if (guideline.getOrientation() == VERTICAL) {
                      if (verticalGuidelines == null) {
                          verticalGuidelines = new ArrayList<>();
                      }
                      verticalGuidelines.add(guideline);
                  }
              }
              if (child instanceof HelperWidget) {
                  if (child instanceof Barrier) {
                      Barrier barrier = (Barrier) child;
                      if (barrier.getOrientation() == HORIZONTAL) {
                          if (horizontalBarriers == null) {
                              horizontalBarriers = new ArrayList<>();
                          }
                          horizontalBarriers.add(barrier);
                      }
                      if (barrier.getOrientation() == VERTICAL) {
                          if (verticalBarriers == null) {
                              verticalBarriers = new ArrayList<>();
                          }
                          verticalBarriers.add(barrier);
                      }
                  } else {
                      HelperWidget helper = (HelperWidget) child;
                      if (horizontalBarriers == null) {
                          horizontalBarriers = new ArrayList<>();
                      }
                      horizontalBarriers.add(helper);
                      if (verticalBarriers == null) {
                          verticalBarriers = new ArrayList<>();
                      }
                      verticalBarriers.add(helper);
                  }
              }
              if (child.mLeft.mTarget == null && child.mRight.mTarget == null
                      && !(child instanceof Guideline) && !(child instanceof Barrier)) {
                  if (isolatedHorizontalChildren == null) {
                      isolatedHorizontalChildren = new ArrayList<>();
                  }
                  isolatedHorizontalChildren.add(child);
              }
              if (child.mTop.mTarget == null && child.mBottom.mTarget == null
                      && child.mBaseline.mTarget == null
                      && !(child instanceof Guideline) && !(child instanceof Barrier)) {
                  if (isolatedVerticalChildren == null) {
                      isolatedVerticalChildren = new ArrayList<>();
                  }
                  isolatedVerticalChildren.add(child);
              }
          }
          ArrayList<WidgetGroup> allDependencyLists = new ArrayList<>();
  
          if (FORCE_USE || layout.getHorizontalDimensionBehaviour()
                  == ConstraintWidget.DimensionBehaviour.WRAP_CONTENT) {
              //horizontalDependencyLists; //new ArrayList<>();
              ArrayList<WidgetGroup> dependencyLists = allDependencyLists;
  
              if (verticalGuidelines != null) {
                  for (Guideline guideline : verticalGuidelines) {
                      findDependents(guideline, HORIZONTAL, dependencyLists, null);
                  }
              }
              if (horizontalBarriers != null) {
                  for (HelperWidget barrier : horizontalBarriers) {
                      WidgetGroup group = findDependents(barrier, HORIZONTAL, dependencyLists, null);
                      barrier.addDependents(dependencyLists, HORIZONTAL, group);
                      group.cleanup(dependencyLists);
                  }
              }
  
              ConstraintAnchor left = layout.getAnchor(ConstraintAnchor.Type.LEFT);
              if (left.getDependents() != null) {
                  for (ConstraintAnchor first : left.getDependents()) {
                      findDependents(first.mOwner, ConstraintWidget.HORIZONTAL,
                              dependencyLists, null);
                  }
              }
  
              ConstraintAnchor right = layout.getAnchor(ConstraintAnchor.Type.RIGHT);
              if (right.getDependents() != null) {
                  for (ConstraintAnchor first : right.getDependents()) {
                      findDependents(first.mOwner, ConstraintWidget.HORIZONTAL,
                              dependencyLists, null);
                  }
              }
  
              ConstraintAnchor center = layout.getAnchor(ConstraintAnchor.Type.CENTER);
              if (center.getDependents() != null) {
                  for (ConstraintAnchor first : center.getDependents()) {
                      findDependents(first.mOwner, ConstraintWidget.HORIZONTAL,
                              dependencyLists, null);
                  }
              }
  
              if (isolatedHorizontalChildren != null) {
                  for (ConstraintWidget widget : isolatedHorizontalChildren) {
                      findDependents(widget, HORIZONTAL, dependencyLists, null);
                  }
              }
          }
  
          if (FORCE_USE || layout.getVerticalDimensionBehaviour()
                  == ConstraintWidget.DimensionBehaviour.WRAP_CONTENT) {
              //verticalDependencyLists; //new ArrayList<>();
              ArrayList<WidgetGroup> dependencyLists = allDependencyLists;
  
              if (horizontalGuidelines != null) {
                  for (Guideline guideline : horizontalGuidelines) {
                      findDependents(guideline, VERTICAL, dependencyLists, null);
                  }
              }
              if (verticalBarriers != null) {
                  for (HelperWidget barrier : verticalBarriers) {
                      WidgetGroup group = findDependents(barrier, VERTICAL, dependencyLists, null);
                      barrier.addDependents(dependencyLists, VERTICAL, group);
                      group.cleanup(dependencyLists);
                  }
              }
  
              ConstraintAnchor top = layout.getAnchor(ConstraintAnchor.Type.TOP);
              if (top.getDependents() != null) {
                  for (ConstraintAnchor first : top.getDependents()) {
                      findDependents(first.mOwner, VERTICAL, dependencyLists, null);
                  }
              }
  
              ConstraintAnchor baseline = layout.getAnchor(ConstraintAnchor.Type.BASELINE);
              if (baseline.getDependents() != null) {
                  for (ConstraintAnchor first : baseline.getDependents()) {
                      findDependents(first.mOwner, VERTICAL, dependencyLists, null);
                  }
              }
  
              ConstraintAnchor bottom = layout.getAnchor(ConstraintAnchor.Type.BOTTOM);
              if (bottom.getDependents() != null) {
                  for (ConstraintAnchor first : bottom.getDependents()) {
                      findDependents(first.mOwner, VERTICAL, dependencyLists, null);
                  }
              }
  
              ConstraintAnchor center = layout.getAnchor(ConstraintAnchor.Type.CENTER);
              if (center.getDependents() != null) {
                  for (ConstraintAnchor first : center.getDependents()) {
                      findDependents(first.mOwner, VERTICAL, dependencyLists, null);
                  }
              }
  
              if (isolatedVerticalChildren != null) {
                  for (ConstraintWidget widget : isolatedVerticalChildren) {
                      findDependents(widget, VERTICAL, dependencyLists, null);
                  }
              }
          }
          // Now we may have to merge horizontal/vertical dependencies
          for (int i = 0; i < count; i++) {
              ConstraintWidget child = children.get(i);
              if (child.oppositeDimensionsTied()) {
                  WidgetGroup horizontalGroup = findGroup(allDependencyLists, child.horizontalGroup);
                  WidgetGroup verticalGroup = findGroup(allDependencyLists, child.verticalGroup);
                  if (horizontalGroup != null && verticalGroup != null) {
                      if (DEBUG_GROUPING) {
                          System.out.println("Merging " + horizontalGroup
                                  + " to " + verticalGroup + " for " + child);
                      }
                      horizontalGroup.moveTo(HORIZONTAL, verticalGroup);
                      verticalGroup.setOrientation(BOTH);
                      allDependencyLists.remove(horizontalGroup);
                  }
              }
              if (DEBUG_GROUPING) {
                  System.out.println("Widget " + child + " => "
                          + child.horizontalGroup + " : " + child.verticalGroup);
              }
          }
  
          if (allDependencyLists.size() <= 1) {
              return false;
          }
  
          if (DEBUG) {
              System.out.println("----------------------------------");
              System.out.println("-- Horizontal dependency lists:");
              System.out.println("----------------------------------");
              for (WidgetGroup list : allDependencyLists) {
                  if (list.getOrientation() != VERTICAL) {
                      System.out.println("list: " + list);
                  }
              }
              System.out.println("----------------------------------");
              System.out.println("-- Vertical dependency lists:");
              System.out.println("----------------------------------");
              for (WidgetGroup list : allDependencyLists) {
                  if (list.getOrientation() != HORIZONTAL) {
                      System.out.println("list: " + list);
                  }
              }
              System.out.println("----------------------------------");
          }
  
          WidgetGroup horizontalPick = null;
          WidgetGroup verticalPick = null;
  
          if (layout.getHorizontalDimensionBehaviour()
                  == ConstraintWidget.DimensionBehaviour.WRAP_CONTENT) {
              int maxWrap = 0;
              WidgetGroup picked = null;
              for (WidgetGroup list : allDependencyLists) {
                  if (list.getOrientation() == VERTICAL) {
                      continue;
                  }
                  list.setAuthoritative(false);
                  int wrap = list.measureWrap(layout.getSystem(), HORIZONTAL);
                  if (wrap > maxWrap) {
                      picked = list;
                      maxWrap = wrap;
                  }
                  if (DEBUG) {
                      System.out.println("list: " + list + " => " + wrap);
                  }
              }
              if (picked != null) {
                  if (DEBUG) {
                      System.out.println("Horizontal MaxWrap : " + maxWrap + " with group " + picked);
                  }
                  layout.setHorizontalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.FIXED);
                  layout.setWidth(maxWrap);
                  picked.setAuthoritative(true);
                  horizontalPick = picked;
              }
          }
  
          if (layout.getVerticalDimensionBehaviour()
                  == ConstraintWidget.DimensionBehaviour.WRAP_CONTENT) {
              int maxWrap = 0;
              WidgetGroup picked = null;
              for (WidgetGroup list : allDependencyLists) {
                  if (list.getOrientation() == HORIZONTAL) {
                      continue;
                  }
                  list.setAuthoritative(false);
                  int wrap = list.measureWrap(layout.getSystem(), VERTICAL);
                  if (wrap > maxWrap) {
                      picked = list;
                      maxWrap = wrap;
                  }
                  if (DEBUG) {
                      System.out.println("      " + list + " => " + wrap);
                  }
              }
              if (picked != null) {
                  if (DEBUG) {
                      System.out.println("Vertical MaxWrap : " + maxWrap + " with group " + picked);
                  }
                  layout.setVerticalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.FIXED);
                  layout.setHeight(maxWrap);
                  picked.setAuthoritative(true);
                  verticalPick = picked;
              }
          }
          return horizontalPick != null || verticalPick != null;
      }
  
      private static WidgetGroup findGroup(ArrayList<WidgetGroup> horizontalDependencyLists,
              int groupId) {
          final int count = horizontalDependencyLists.size();
          for (int i = 0; i < count; i++) {
              WidgetGroup group = horizontalDependencyLists.get(i);
              if (groupId == group.getId()) {
                  return group;
              }
          }
          return null;
      }
  
      // @TODO: add description
      public static WidgetGroup findDependents(ConstraintWidget constraintWidget,
              int orientation,
              ArrayList<WidgetGroup> list,
              WidgetGroup group) {
          int groupId = -1;
          if (orientation == ConstraintWidget.HORIZONTAL) {
              groupId = constraintWidget.horizontalGroup;
          } else {
              groupId = constraintWidget.verticalGroup;
          }
          if (DEBUG_GROUPING) {
              System.out.println("--- find " + (orientation == HORIZONTAL ? "Horiz" : "Vert")
                      + " dependents of " + constraintWidget.getDebugName()
                      + " group " + group + " widget group id " + groupId);
          }
          if (groupId != -1 && (group == null || (groupId != group.getId()))) {
              // already in a group!
              if (DEBUG_GROUPING) {
                  System.out.println("widget " + constraintWidget.getDebugName()
                          + " already in group " + groupId + " group: " + group);
              }
              for (int i = 0; i < list.size(); i++) {
                  WidgetGroup widgetGroup = list.get(i);
                  if (widgetGroup.getId() == groupId) {
                      if (group != null) {
                          if (DEBUG_GROUPING) {
                              System.out.println("Move group " + group + " to " + widgetGroup);
                          }
                          group.moveTo(orientation, widgetGroup);
                          list.remove(group);
                      }
                      group = widgetGroup;
                      break;
                  }
              }
          } else if (groupId != -1) {
              return group;
          }
          if (group == null) {
              if (constraintWidget instanceof HelperWidget) {
                  HelperWidget helper = (HelperWidget) constraintWidget;
                  groupId = helper.findGroupInDependents(orientation);
                  if (groupId != -1) {
                      for (int i = 0; i < list.size(); i++) {
                          WidgetGroup widgetGroup = list.get(i);
                          if (widgetGroup.getId() == groupId) {
                              group = widgetGroup;
                              break;
                          }
                      }
                  }
              }
              if (group == null) {
                  group = new WidgetGroup(orientation);
              }
              if (DEBUG_GROUPING) {
                  System.out.println("Create group " + group
                          + " for widget " + constraintWidget.getDebugName());
              }
              list.add(group);
          }
          if (group.add(constraintWidget)) {
              if (constraintWidget instanceof Guideline) {
                  Guideline guideline = (Guideline) constraintWidget;
                  guideline.getAnchor().findDependents(guideline.getOrientation()
                          == Guideline.HORIZONTAL ? VERTICAL : HORIZONTAL, list, group);
              }
              if (orientation == ConstraintWidget.HORIZONTAL) {
                  constraintWidget.horizontalGroup = group.getId();
                  if (DEBUG_GROUPING) {
                      System.out.println("Widget " + constraintWidget.getDebugName()
                              + " H group is " + constraintWidget.horizontalGroup);
                  }
                  constraintWidget.mLeft.findDependents(orientation, list, group);
                  constraintWidget.mRight.findDependents(orientation, list, group);
              } else {
                  constraintWidget.verticalGroup = group.getId();
                  if (DEBUG_GROUPING) {
                      System.out.println("Widget " + constraintWidget.getDebugName()
                              + " V group is " + constraintWidget.verticalGroup);
                  }
                  constraintWidget.mTop.findDependents(orientation, list, group);
                  constraintWidget.mBaseline.findDependents(orientation, list, group);
                  constraintWidget.mBottom.findDependents(orientation, list, group);
              }
              constraintWidget.mCenter.findDependents(orientation, list, group);
          }
          return group;
      }
  }
  