content: /*
   * Copyright (C) 2019 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.HORIZONTAL;
  import static androidx.constraintlayout.core.widgets.ConstraintWidget.VERTICAL;
  
  import androidx.constraintlayout.core.widgets.ConstraintWidgetContainer;
  
  import java.util.ArrayList;
  
  class RunGroup {
      public static final int START = 0;
      public static final int END = 1;
      public static final int BASELINE = 2;
  
      public static int index;
  
      public int position = 0;
      public boolean dual = false;
  
      WidgetRun mFirstRun = null;
      WidgetRun mLastRun = null;
      ArrayList<WidgetRun> mRuns = new ArrayList<>();
  
      int mGroupIndex = 0;
      int mDirection;
  
      RunGroup(WidgetRun run, int dir) {
          mGroupIndex = index;
          index++;
          mFirstRun = run;
          mLastRun = run;
          mDirection = dir;
      }
  
      public void add(WidgetRun run) {
          mRuns.add(run);
          mLastRun = run;
      }
  
      private long traverseStart(DependencyNode node, long startPosition) {
          WidgetRun run = node.mRun;
          if (run instanceof HelperReferences) {
              return startPosition;
          }
          long position = startPosition;
  
          // first, compute stuff dependent on this node.
  
          final int count = node.mDependencies.size();
          for (int i = 0; i < count; i++) {
              Dependency dependency = node.mDependencies.get(i);
              if (dependency instanceof DependencyNode) {
                  DependencyNode nextNode = (DependencyNode) dependency;
                  if (nextNode.mRun == run) {
                      // skip our own sibling node
                      continue;
                  }
                  position = Math.max(position,
                          traverseStart(nextNode, startPosition + nextNode.mMargin));
              }
          }
  
          if (node == run.start) {
              // let's go for our sibling
              long dimension = run.getWrapDimension();
              position = Math.max(position, traverseStart(run.end, startPosition + dimension));
              position = Math.max(position, startPosition + dimension - run.end.mMargin);
          }
  
          return position;
      }
  
      private long traverseEnd(DependencyNode node, long startPosition) {
          WidgetRun run = node.mRun;
          if (run instanceof HelperReferences) {
              return startPosition;
          }
          long position = startPosition;
  
          // first, compute stuff dependent on this node.
  
          final int count = node.mDependencies.size();
          for (int i = 0; i < count; i++) {
              Dependency dependency = node.mDependencies.get(i);
              if (dependency instanceof DependencyNode) {
                  DependencyNode nextNode = (DependencyNode) dependency;
                  if (nextNode.mRun == run) {
                      // skip our own sibling node
                      continue;
                  }
                  position = Math.min(position,
                          traverseEnd(nextNode, startPosition + nextNode.mMargin));
              }
          }
  
          if (node == run.end) {
              // let's go for our sibling
              long dimension = run.getWrapDimension();
              position = Math.min(position, traverseEnd(run.start, startPosition - dimension));
              position = Math.min(position, startPosition - dimension - run.start.mMargin);
          }
  
          return position;
      }
  
      public long computeWrapSize(ConstraintWidgetContainer container, int orientation) {
          if (mFirstRun instanceof ChainRun) {
              ChainRun chainRun = (ChainRun) mFirstRun;
              if (chainRun.orientation != orientation) {
                  return 0;
              }
          } else {
              if (orientation == HORIZONTAL) {
                  if (!(mFirstRun instanceof HorizontalWidgetRun)) {
                      return 0;
                  }
              } else {
                  if (!(mFirstRun instanceof VerticalWidgetRun)) {
                      return 0;
                  }
              }
          }
          DependencyNode containerStart = orientation == HORIZONTAL
                  ? container.mHorizontalRun.start : container.mVerticalRun.start;
          DependencyNode containerEnd = orientation == HORIZONTAL
                  ? container.mHorizontalRun.end : container.mVerticalRun.end;
  
          boolean runWithStartTarget = mFirstRun.start.mTargets.contains(containerStart);
          boolean runWithEndTarget = mFirstRun.end.mTargets.contains(containerEnd);
  
          long dimension = mFirstRun.getWrapDimension();
  
          if (runWithStartTarget && runWithEndTarget) {
              long maxPosition = traverseStart(mFirstRun.start, 0);
              long minPosition = traverseEnd(mFirstRun.end, 0);
  
              // to compute the gaps, we subtract the margins
              long endGap = maxPosition - dimension;
              if (endGap >= -mFirstRun.end.mMargin) {
                  endGap += mFirstRun.end.mMargin;
              }
              long startGap = -minPosition - dimension - mFirstRun.start.mMargin;
              if (startGap >= mFirstRun.start.mMargin) {
                  startGap -= mFirstRun.start.mMargin;
              }
              float bias = mFirstRun.mWidget.getBiasPercent(orientation);
              long gap = 0;
              if (bias > 0) {
                  gap = (long) ((startGap / bias) + (endGap / (1f - bias)));
              }
  
              startGap = (long) (0.5f + (gap * bias));
              endGap = (long) (0.5f + (gap * (1f - bias)));
  
              long runDimension = startGap + dimension + endGap;
              dimension = mFirstRun.start.mMargin + runDimension - mFirstRun.end.mMargin;
  
          } else if (runWithStartTarget) {
              long maxPosition = traverseStart(mFirstRun.start, mFirstRun.start.mMargin);
              long runDimension = mFirstRun.start.mMargin + dimension;
              dimension = Math.max(maxPosition, runDimension);
          } else if (runWithEndTarget) {
              long minPosition = traverseEnd(mFirstRun.end, mFirstRun.end.mMargin);
              long runDimension = -mFirstRun.end.mMargin + dimension;
              dimension = Math.max(-minPosition, runDimension);
          } else {
              dimension = mFirstRun.start.mMargin
                      + mFirstRun.getWrapDimension() - mFirstRun.end.mMargin;
          }
  
          return dimension;
      }
  
      private boolean defineTerminalWidget(WidgetRun run, int orientation) {
          if (!run.mWidget.isTerminalWidget[orientation]) {
              return false;
          }
          for (Dependency dependency : run.start.mDependencies) {
              if (dependency instanceof DependencyNode) {
                  DependencyNode node = (DependencyNode) dependency;
                  if (node.mRun == run) {
                      continue;
                  }
                  if (node == node.mRun.start) {
                      if (run instanceof ChainRun) {
                          ChainRun chainRun = (ChainRun) run;
                          for (WidgetRun widgetChainRun : chainRun.mWidgets) {
                              defineTerminalWidget(widgetChainRun, orientation);
                          }
                      } else {
                          if (!(run instanceof HelperReferences)) {
                              run.mWidget.isTerminalWidget[orientation] = false;
                          }
                      }
                      defineTerminalWidget(node.mRun, orientation);
                  }
              }
          }
          for (Dependency dependency : run.end.mDependencies) {
              if (dependency instanceof DependencyNode) {
                  DependencyNode node = (DependencyNode) dependency;
                  if (node.mRun == run) {
                      continue;
                  }
                  if (node == node.mRun.start) {
                      if (run instanceof ChainRun) {
                          ChainRun chainRun = (ChainRun) run;
                          for (WidgetRun widgetChainRun : chainRun.mWidgets) {
                              defineTerminalWidget(widgetChainRun, orientation);
                          }
                      } else {
                          if (!(run instanceof HelperReferences)) {
                              run.mWidget.isTerminalWidget[orientation] = false;
                          }
                      }
                      defineTerminalWidget(node.mRun, orientation);
                  }
              }
          }
          return false;
      }
  
  
      public void defineTerminalWidgets(boolean horizontalCheck, boolean verticalCheck) {
          if (horizontalCheck && mFirstRun instanceof HorizontalWidgetRun) {
              defineTerminalWidget(mFirstRun, HORIZONTAL);
          }
          if (verticalCheck && mFirstRun instanceof VerticalWidgetRun) {
              defineTerminalWidget(mFirstRun, VERTICAL);
          }
      }
  }
  