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.HORIZONTAL;
  import static androidx.constraintlayout.core.widgets.ConstraintWidget.VERTICAL;
  
  import androidx.constraintlayout.core.LinearSystem;
  import androidx.constraintlayout.core.widgets.Chain;
  import androidx.constraintlayout.core.widgets.ConstraintWidget;
  import androidx.constraintlayout.core.widgets.ConstraintWidgetContainer;
  
  import java.lang.ref.WeakReference;
  import java.util.ArrayList;
  import java.util.Arrays;
  
  /**
   * Represents a group of widget for the grouping mechanism.
   */
  public class WidgetGroup {
      private static final boolean DEBUG = false;
      ArrayList<ConstraintWidget> mWidgets = new ArrayList<>();
      static int sCount = 0;
      int mId = -1;
      boolean mAuthoritative = false;
      int mOrientation = HORIZONTAL;
      ArrayList<MeasureResult> mResults = null;
      private int mMoveTo = -1;
  
      public WidgetGroup(int orientation) {
          mId = sCount++;
          this.mOrientation = orientation;
      }
  
      public int getOrientation() {
          return mOrientation;
      }
  
      public int getId() {
          return mId;
      }
  
      // @TODO: add description
      public boolean add(ConstraintWidget widget) {
          if (mWidgets.contains(widget)) {
              return false;
          }
          mWidgets.add(widget);
          return true;
      }
  
      public void setAuthoritative(boolean isAuthoritative) {
          mAuthoritative = isAuthoritative;
      }
  
      public boolean isAuthoritative() {
          return mAuthoritative;
      }
  
      private String getOrientationString() {
          if (mOrientation == HORIZONTAL) {
              return "Horizontal";
          } else if (mOrientation == VERTICAL) {
              return "Vertical";
          } else if (mOrientation == BOTH) {
              return "Both";
          }
          return "Unknown";
      }
  
      @Override
      public String toString() {
          String ret = getOrientationString() + " [" + mId + "] <";
          for (ConstraintWidget widget : mWidgets) {
              ret += " " + widget.getDebugName();
          }
          ret += " >";
          return ret;
      }
  
      // @TODO: add description
      public void moveTo(int orientation, WidgetGroup widgetGroup) {
          if (DEBUG) {
              System.out.println("Move all widgets (" + this + ") from "
                      + mId + " to " + widgetGroup.getId() + "(" + widgetGroup + ")");
              System.out.println("" +
                      "do not call  "+ measureWrap(  orientation, new ConstraintWidget()));
          }
          for (ConstraintWidget widget : mWidgets) {
              widgetGroup.add(widget);
              if (orientation == HORIZONTAL) {
                  widget.horizontalGroup = widgetGroup.getId();
              } else {
                  widget.verticalGroup = widgetGroup.getId();
              }
          }
          mMoveTo = widgetGroup.mId;
      }
  
      // @TODO: add description
      public void clear() {
          mWidgets.clear();
      }
  
      private int measureWrap(int orientation, ConstraintWidget widget) {
          ConstraintWidget.DimensionBehaviour behaviour = widget.getDimensionBehaviour(orientation);
          if (behaviour == ConstraintWidget.DimensionBehaviour.WRAP_CONTENT
                  || behaviour == ConstraintWidget.DimensionBehaviour.MATCH_PARENT
                  || behaviour == ConstraintWidget.DimensionBehaviour.FIXED) {
              int dimension;
              if (orientation == HORIZONTAL) {
                  dimension = widget.getWidth();
              } else {
                  dimension = widget.getHeight();
              }
              return dimension;
          }
          return -1;
      }
  
      // @TODO: add description
      public int measureWrap(LinearSystem system, int orientation) {
          int count = mWidgets.size();
          if (count == 0) {
              return 0;
          }
          // TODO: add direct wrap computation for simpler cases instead of calling the solver
          return solverMeasure(system, mWidgets, orientation);
      }
  
      private int solverMeasure(LinearSystem system,
              ArrayList<ConstraintWidget> widgets,
              int orientation) {
          ConstraintWidgetContainer container =
                  (ConstraintWidgetContainer) widgets.get(0).getParent();
          system.reset();
          @SuppressWarnings("unused") boolean prevDebug = LinearSystem.FULL_DEBUG;
          container.addToSolver(system, false);
          for (int i = 0; i < widgets.size(); i++) {
              ConstraintWidget widget = widgets.get(i);
              widget.addToSolver(system, false);
          }
          if (orientation == HORIZONTAL) {
              if (container.mHorizontalChainsSize > 0) {
                  Chain.applyChainConstraints(container, system, widgets, HORIZONTAL);
              }
          }
          if (orientation == VERTICAL) {
              if (container.mVerticalChainsSize > 0) {
                  Chain.applyChainConstraints(container, system, widgets, VERTICAL);
              }
          }
  
          try {
              system.minimize();
          } catch (Exception e) {
              //TODO remove fancy version of e.printStackTrace()
              System.err.println(e.toString()+"\n"+Arrays.toString(e.getStackTrace())
                      .replace("[","   at ")
                      .replace(",","\n   at")
                      .replace("]",""));
          }
  
          // save results
          mResults = new ArrayList<>();
          for (int i = 0; i < widgets.size(); i++) {
              ConstraintWidget widget = widgets.get(i);
              MeasureResult result = new MeasureResult(widget, system, orientation);
              mResults.add(result);
          }
  
          if (orientation == HORIZONTAL) {
              int left = system.getObjectVariableValue(container.mLeft);
              int right = system.getObjectVariableValue(container.mRight);
              system.reset();
              return right - left;
          } else {
              int top = system.getObjectVariableValue(container.mTop);
              int bottom = system.getObjectVariableValue(container.mBottom);
              system.reset();
              return bottom - top;
          }
      }
  
      public void setOrientation(int orientation) {
          this.mOrientation = orientation;
      }
  
      // @TODO: add description
      public void apply() {
          if (mResults == null) {
              return;
          }
          if (!mAuthoritative) {
              return;
          }
          for (int i = 0; i < mResults.size(); i++) {
              MeasureResult result = mResults.get(i);
              result.apply();
          }
      }
  
      // @TODO: add description
      public boolean intersectWith(WidgetGroup group) {
          for (int i = 0; i < mWidgets.size(); i++) {
              ConstraintWidget widget = mWidgets.get(i);
              if (group.contains(widget)) {
                  return true;
              }
          }
          return false;
      }
  
      private boolean contains(ConstraintWidget widget) {
          return mWidgets.contains(widget);
      }
  
      // @TODO: add description
      public int size() {
          return mWidgets.size();
      }
  
      // @TODO: add description
      public void cleanup(ArrayList<WidgetGroup> dependencyLists) {
          final int count = mWidgets.size();
          if (mMoveTo != -1 && count > 0) {
              for (int i = 0; i < dependencyLists.size(); i++) {
                  WidgetGroup group = dependencyLists.get(i);
                  if (mMoveTo == group.mId) {
                      moveTo(mOrientation, group);
                  }
              }
          }
          if (count == 0) {
              dependencyLists.remove(this);
              return;
          }
      }
  
  
      static class MeasureResult {
          WeakReference<ConstraintWidget> mWidgetRef;
          int mLeft;
          int mTop;
          int mRight;
          int mBottom;
          int mBaseline;
          int mOrientation;
  
          MeasureResult(ConstraintWidget widget, LinearSystem system, int orientation) {
              mWidgetRef = new WeakReference<>(widget);
              mLeft = system.getObjectVariableValue(widget.mLeft);
              mTop = system.getObjectVariableValue(widget.mTop);
              mRight = system.getObjectVariableValue(widget.mRight);
              mBottom = system.getObjectVariableValue(widget.mBottom);
              mBaseline = system.getObjectVariableValue(widget.mBaseline);
              this.mOrientation = orientation;
          }
  
          public void apply() {
              ConstraintWidget widget = mWidgetRef.get();
              if (widget != null) {
                  widget.setFinalFrame(mLeft, mTop, mRight, mBottom, mBaseline, mOrientation);
              }
          }
      }
  }
  