content: /*
   * MIT License
   *
   * Copyright (c) 2018 Alibaba Group
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in all
   * copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   * SOFTWARE.
   */
  
  package com.tmall.wireless.vaf.virtualview.layout;
  
  import android.graphics.drawable.Drawable;
  import android.support.annotation.IntDef;
  import android.support.annotation.NonNull;
  import android.support.v4.view.ViewCompat;
  import android.util.SparseIntArray;
  import android.view.View;
  
  import com.libra.virtualview.common.StringBase;
  import com.libra.virtualview.common.ViewBaseCommon;
  import com.tmall.wireless.vaf.framework.VafContext;
  import com.tmall.wireless.vaf.virtualview.core.Layout;
  import com.tmall.wireless.vaf.virtualview.core.ViewBase;
  import com.tmall.wireless.vaf.virtualview.core.ViewCache;
  
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.List;
  
  import static com.libra.virtualview.common.FlexLayoutCommon.ALIGN_CONTENT_CENTER;
  import static com.libra.virtualview.common.FlexLayoutCommon.ALIGN_CONTENT_FLEX_END;
  import static com.libra.virtualview.common.FlexLayoutCommon.ALIGN_CONTENT_FLEX_START;
  import static com.libra.virtualview.common.FlexLayoutCommon.ALIGN_CONTENT_SPACE_AROUND;
  import static com.libra.virtualview.common.FlexLayoutCommon.ALIGN_CONTENT_SPACE_BETWEEN;
  import static com.libra.virtualview.common.FlexLayoutCommon.ALIGN_CONTENT_STRETCH;
  import static com.libra.virtualview.common.FlexLayoutCommon.ALIGN_ITEMS_BASELINE;
  import static com.libra.virtualview.common.FlexLayoutCommon.ALIGN_ITEMS_CENTER;
  import static com.libra.virtualview.common.FlexLayoutCommon.ALIGN_ITEMS_FLEX_END;
  import static com.libra.virtualview.common.FlexLayoutCommon.ALIGN_ITEMS_FLEX_START;
  import static com.libra.virtualview.common.FlexLayoutCommon.ALIGN_ITEMS_STRETCH;
  import static com.libra.virtualview.common.FlexLayoutCommon.FLEX_DIRECTION_COLUMN;
  import static com.libra.virtualview.common.FlexLayoutCommon.FLEX_DIRECTION_COLUMN_REVERSE;
  import static com.libra.virtualview.common.FlexLayoutCommon.FLEX_DIRECTION_ROW;
  import static com.libra.virtualview.common.FlexLayoutCommon.FLEX_DIRECTION_ROW_REVERSE;
  import static com.libra.virtualview.common.FlexLayoutCommon.FLEX_WRAP_NOWRAP;
  import static com.libra.virtualview.common.FlexLayoutCommon.FLEX_WRAP_WRAP;
  import static com.libra.virtualview.common.FlexLayoutCommon.FLEX_WRAP_WRAP_REVERSE;
  import static com.libra.virtualview.common.FlexLayoutCommon.JUSTIFY_CONTENT_CENTER;
  import static com.libra.virtualview.common.FlexLayoutCommon.JUSTIFY_CONTENT_FLEX_END;
  import static com.libra.virtualview.common.FlexLayoutCommon.JUSTIFY_CONTENT_FLEX_START;
  import static com.libra.virtualview.common.FlexLayoutCommon.JUSTIFY_CONTENT_SPACE_AROUND;
  import static com.libra.virtualview.common.FlexLayoutCommon.JUSTIFY_CONTENT_SPACE_BETWEEN;
  
  /**
   * Created by gujicheng on 16/8/22.
   */
  @Deprecated
  public class FlexLayout extends Layout {
      private final static String TAG = "FlexLayout_TMTEST";
  
      @IntDef({FLEX_DIRECTION_ROW, FLEX_DIRECTION_ROW_REVERSE, FLEX_DIRECTION_COLUMN,
              FLEX_DIRECTION_COLUMN_REVERSE})
      @Retention(RetentionPolicy.SOURCE)
      public @interface FlexDirection {
      }
  
  //    public static final int FLEX_DIRECTION_ROW = 0;
  //
  //    public static final int FLEX_DIRECTION_ROW_REVERSE = 1;
  //
  //    public static final int FLEX_DIRECTION_COLUMN = 2;
  //
  //    public static final int FLEX_DIRECTION_COLUMN_REVERSE = 3;
  //
      private int mFlexDirection;
  
      @IntDef({FLEX_WRAP_NOWRAP, FLEX_WRAP_WRAP, FLEX_WRAP_WRAP_REVERSE})
      @Retention(RetentionPolicy.SOURCE)
      public @interface FlexWrap {
      }
  
  //    public static final int FLEX_WRAP_NOWRAP = 0;
  //
  //    public static final int FLEX_WRAP_WRAP = 1;
  //
  //    public static final int FLEX_WRAP_WRAP_REVERSE = 2;
  
      private int mFlexWrap;
  
  
      @IntDef({JUSTIFY_CONTENT_FLEX_START, JUSTIFY_CONTENT_FLEX_END, JUSTIFY_CONTENT_CENTER,
              JUSTIFY_CONTENT_SPACE_BETWEEN, JUSTIFY_CONTENT_SPACE_AROUND})
      @Retention(RetentionPolicy.SOURCE)
      public @interface JustifyContent {
      }
  
  //    public static final int JUSTIFY_CONTENT_FLEX_START = 0;
  //
  //    public static final int JUSTIFY_CONTENT_FLEX_END = 1;
  //
  //    public static final int JUSTIFY_CONTENT_CENTER = 2;
  //
  //    public static final int JUSTIFY_CONTENT_SPACE_BETWEEN = 3;
  //
  //    public static final int JUSTIFY_CONTENT_SPACE_AROUND = 4;
  
      private int mJustifyContent;
  
  
      @IntDef({ALIGN_ITEMS_FLEX_START, ALIGN_ITEMS_FLEX_END, ALIGN_ITEMS_CENTER,
              ALIGN_ITEMS_BASELINE, ALIGN_ITEMS_STRETCH})
      @Retention(RetentionPolicy.SOURCE)
      public @interface AlignItems {
  
      }
  
  //    public static final int ALIGN_ITEMS_FLEX_START = 0;
  //
  //    public static final int ALIGN_ITEMS_FLEX_END = 1;
  //
  //    public static final int ALIGN_ITEMS_CENTER = 2;
  //
  //    public static final int ALIGN_ITEMS_BASELINE = 3;
  //
  //    public static final int ALIGN_ITEMS_STRETCH = 4;
  
      private int mAlignItems;
  
  
      @IntDef({ALIGN_CONTENT_FLEX_START, ALIGN_CONTENT_FLEX_END, ALIGN_CONTENT_CENTER,
              ALIGN_CONTENT_SPACE_BETWEEN, ALIGN_CONTENT_SPACE_AROUND, ALIGN_CONTENT_STRETCH})
      @Retention(RetentionPolicy.SOURCE)
      public @interface AlignContent {
  
      }
  
  //    public static final int ALIGN_CONTENT_FLEX_START = 0;
  //
  //    public static final int ALIGN_CONTENT_FLEX_END = 1;
  //
  //    public static final int ALIGN_CONTENT_CENTER = 2;
  //
  //    public static final int ALIGN_CONTENT_SPACE_BETWEEN = 3;
  //
  //    public static final int ALIGN_CONTENT_SPACE_AROUND = 4;
  //
  //    public static final int ALIGN_CONTENT_STRETCH = 5;
  
      private int mAlignContent;
  
      @IntDef(flag = true,
              value = {
                      SHOW_DIVIDER_NONE,
                      SHOW_DIVIDER_BEGINNING,
                      SHOW_DIVIDER_MIDDLE,
                      SHOW_DIVIDER_END
              })
      @Retention(RetentionPolicy.SOURCE)
      public @interface DividerMode {
  
      }
  
      /** Constant to how no dividers */
      public static final int SHOW_DIVIDER_NONE = 0;
  
      /** Constant to show a divider at the beginning of the flex lines (or flex items). */
      public static final int SHOW_DIVIDER_BEGINNING = 1;
  
      /** Constant to show dividers between flex lines or flex items. */
      public static final int SHOW_DIVIDER_MIDDLE = 1 << 1;
  
      /** Constant to show a divider at the end of the flex lines or flex items. */
      public static final int SHOW_DIVIDER_END = 1 << 2;
  
      /** The drawable to be drawn for the horizontal dividers. */
      private Drawable mDividerDrawableHorizontal;
  
      /** The drawable to be drawn for the vertical dividers. */
      private Drawable mDividerDrawableVertical;
  
      /**
       * Indicates the divider mode for the {@link #mDividerDrawableHorizontal}. The value needs to
       * be the combination of the value of {@link #SHOW_DIVIDER_NONE},
       * {@link #SHOW_DIVIDER_BEGINNING}, {@link #SHOW_DIVIDER_MIDDLE} and {@link #SHOW_DIVIDER_END}
       */
      private int mShowDividerHorizontal;
  
      /**
       * Indicates the divider mode for the {@link #mDividerDrawableVertical}. The value needs to
       * be the combination of the value of {@link #SHOW_DIVIDER_NONE},
       * {@link #SHOW_DIVIDER_BEGINNING}, {@link #SHOW_DIVIDER_MIDDLE} and {@link #SHOW_DIVIDER_END}
       */
      private int mShowDividerVertical;
  
      /** The height of the {@link #mDividerDrawableHorizontal}. */
      private int mDividerHorizontalHeight;
  
      /** The width of the {@link #mDividerDrawableVertical}. */
      private int mDividerVerticalWidth;
  
      /**
       */
      private int[] mReorderedIndices;
  
      /**
       * Key: the index of the view ({@link #mReorderedIndices} isn't taken into account)
       * Value: the value for the order attribute
       */
      private SparseIntArray mOrderCache;
  
      private List<FlexLine> mFlexLines = new ArrayList<>();
  
      /**
       * Holds the 'frozen' state of children during measure. If a view is frozen it will no longer
       * expand or shrink regardless of flexGrow/flexShrink. Items are indexed by the child's
       * reordered index.
       */
      private boolean[] mChildrenFrozen;
  
      public FlexLayout(VafContext context, ViewCache viewCache) {
          super(context, viewCache);
  
          mFlexDirection = FLEX_DIRECTION_ROW;
          mFlexWrap = FLEX_WRAP_NOWRAP;
          mJustifyContent = JUSTIFY_CONTENT_FLEX_START;
          mAlignItems = ALIGN_ITEMS_FLEX_START;
          mAlignContent = ALIGN_CONTENT_FLEX_START;
      }
  
      @Override
      public Params generateParams() {
          return new Params();
      }
  
      @Override
      public void onComMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  //        Log.d(TAG, "onComMeasure");
          if (isOrderChangedFromLastMeasurement()) {
              mReorderedIndices = createReorderedIndices();
          }
  //        if (mChildrenFrozen == null || mChildrenFrozen.length < getChildCount()) {
  //            mChildrenFrozen = new boolean[getChildCount()];
  //        }
          if (mChildrenFrozen == null || mChildrenFrozen.length < mSubViews.size()) {
              mChildrenFrozen = new boolean[mSubViews.size()];
          }
  
          // TODO: Only calculate the children views which are affected from the last measure.
  
          switch (mFlexDirection) {
              case FLEX_DIRECTION_ROW: // Intentional fall through
              case FLEX_DIRECTION_ROW_REVERSE:
                  measureHorizontal(widthMeasureSpec, heightMeasureSpec);
                  break;
              case FLEX_DIRECTION_COLUMN: // Intentional fall through
              case FLEX_DIRECTION_COLUMN_REVERSE:
                  measureVertical(widthMeasureSpec, heightMeasureSpec);
                  break;
              default:
                  throw new IllegalStateException(
                          "Invalid value for the flex direction is set: " + mFlexDirection);
          }
  
          Arrays.fill(mChildrenFrozen, false);
      }
  
      private void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
          int heightMode = View.MeasureSpec.getMode(heightMeasureSpec);
          int heightSize = View.MeasureSpec.getSize(heightMeasureSpec);
          int childState = 0;
  
          mFlexLines.clear();
  
          // Determine how many flex lines are needed in this layout by measuring each child.
          // (Expand or shrink the view depending on the flexGrow and flexShrink attributes in a later
          // loop)
          int childCount = mSubViews.size();
          int paddingTop = getComPaddingTop();
          int paddingBottom = getComPaddingBottom();
          int largestWidthInColumn = Integer.MIN_VALUE;
          FlexLine flexLine = new FlexLine();
          flexLine.mMainSize = paddingTop + paddingBottom;
          // The index of the view in a same flex line.
          int indexInFlexLine = 0;
          for (int i = 0; i < childCount; i++) {
              ViewBase child = getReorderedChildAt(i);
              if (child == null) {
                  addFlexLineIfLastFlexItem(i, childCount, flexLine);
                  continue;
  //            } else if (child.getVisibility() == View.GONE) {
  //            } else if (!child.isVisible()) {
              } else if (child.getVisibility() == ViewBaseCommon.GONE) {
                  flexLine.mItemCount++;
                  addFlexLineIfLastFlexItem(i, childCount, flexLine);
                  continue;
              }
  
              Params lp = (Params) child.getComLayoutParams();
              if (lp.alignSelf == Params.ALIGN_SELF_STRETCH) {
                  flexLine.mIndicesAlignSelfStretch.add(i);
              }
  
              int childHeight = lp.mLayoutHeight;
              if (lp.flexBasisPercent != Params.FLEX_BASIS_PERCENT_DEFAULT
                      && heightMode == View.MeasureSpec.EXACTLY) {
                  childHeight = Math.round(heightSize * lp.flexBasisPercent);
                  // Use the dimension from the layout_height attribute if the heightMode is not
                  // MeasureSpec.EXACTLY even if any fraction value is set to layout_flexBasisPercent.
                  // There are likely quite few use cases where assigning any fraction values
                  // with heightMode is not MeasureSpec.EXACTLY (e.g. FlexboxLayout's layout_height
                  // is set to wrap_content)
              }
  
              int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                      getComPaddingLeft() + getComPaddingRight() + lp.mLayoutMarginLeft
                              + lp.mLayoutMarginRight, lp.mLayoutWidth);
              int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
                      getComPaddingTop() + getComPaddingBottom() + lp.mLayoutMarginTop
                              + lp.mLayoutMarginBottom, childHeight);
              child.measureComponent(childWidthMeasureSpec, childHeightMeasureSpec);
  
              // Check the size constraint after the first measurement for the child
              // To prevent the child's width/height violate the size constraints imposed by the
              // {@link LayoutParams#minWidth}, {@link LayoutParams#minHeight},
              // {@link LayoutParams#maxWidth} and {@link LayoutParams#maxHeight} attributes.
              // E.g. When the child's layout_height is wrap_content the measured height may be
              // less than the min height after the first measurement.
              checkSizeConstraints(child);
  
              childState = ViewCompat
                      .combineMeasuredStates(childState, 0);
  //                    .combineMeasuredStates(childState, ViewCompat.getMeasuredState(child));
              largestWidthInColumn = Math.max(largestWidthInColumn,
                      child.getComMeasuredWidth() + lp.mLayoutMarginLeft + lp.mLayoutMarginRight);
  
              if (isWrapRequired(heightMode, heightSize, flexLine.mMainSize,
                      child.getComMeasuredHeight() + lp.mLayoutMarginTop + lp.mLayoutMarginBottom, lp,
                      i, indexInFlexLine)) {
                  if (flexLine.mItemCount > 0) {
                      addFlexLine(flexLine);
                  }
  
                  flexLine = new FlexLine();
                  flexLine.mItemCount = 1;
                  flexLine.mMainSize = paddingTop + paddingBottom;
                  largestWidthInColumn = child.getComMeasuredWidth() + lp.mLayoutMarginLeft + lp.mLayoutMarginRight;
                  indexInFlexLine = 0;
              } else {
                  flexLine.mItemCount++;
                  indexInFlexLine++;
              }
              flexLine.mMainSize += child.getComMeasuredHeight() + lp.mLayoutMarginTop + lp.mLayoutMarginBottom;
              flexLine.mTotalFlexGrow += lp.flexGrow;
              flexLine.mTotalFlexShrink += lp.flexShrink;
              // Temporarily set the cross axis length as the largest child width in the column
              // Expand along the cross axis depending on the mAlignContent property if needed
              // later
              flexLine.mCrossSize = Math.max(flexLine.mCrossSize, largestWidthInColumn);
  
              if (hasDividerBeforeChildAtAlongMainAxis(i, indexInFlexLine)) {
                  flexLine.mMainSize += mDividerHorizontalHeight;
              }
              addFlexLineIfLastFlexItem(i, childCount, flexLine);
          }
  
          determineMainSize(mFlexDirection, widthMeasureSpec, heightMeasureSpec);
          determineCrossSize(mFlexDirection, widthMeasureSpec, heightMeasureSpec,
                  getComPaddingLeft() + getComPaddingRight());
          // Now cross size for each flex line is determined.
          // Expand the views if alignItems (or alignSelf in each child view) is set to stretch
          stretchViews(mFlexDirection, mAlignItems);
          setMeasuredDimensionForFlex(mFlexDirection, widthMeasureSpec, heightMeasureSpec,
                  childState);
      }
  
      private void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) {
          int widthMode = View.MeasureSpec.getMode(widthMeasureSpec);
          int widthSize = View.MeasureSpec.getSize(widthMeasureSpec);
          int childState = 0;
  
          mFlexLines.clear();
  
          // Determine how many flex lines are needed in this layout by measuring each child.
          // (Expand or shrink the view depending on the flexGrow and flexShrink attributes in a later
          // loop)
          {
  //            int childCount = getChildCount();
              int childCount = mSubViews.size();
              int paddingStart = mPaddingLeft;
  //            int paddingStart = ViewCompat.getPaddingStart(this);
  //            int paddingEnd = ViewCompat.getPaddingEnd(this);
              int paddingEnd = mPaddingRight;
              int largestHeightInRow = Integer.MIN_VALUE;
              FlexLine flexLine = new FlexLine();
  
              // The index of the view in a same flex line.
              int indexInFlexLine = 0;
              flexLine.mMainSize = paddingStart + paddingEnd;
              for (int i = 0; i < childCount; i++) {
  //                View child = getReorderedChildAt(i);
                  ViewBase child = getReorderedChildAt(i);
                  if (child == null) {
                      addFlexLineIfLastFlexItem(i, childCount, flexLine);
                      continue;
  //                } else if (child.getVisibility() == View.GONE) {
  //                } else if (!child.isVisible()) {
                  } else if (child.getVisibility() == ViewBaseCommon.GONE) {
                      flexLine.mItemCount++;
                      addFlexLineIfLastFlexItem(i, childCount, flexLine);
                      continue;
                  }
  
                  Params lp = (Params) child.getComLayoutParams();
                  if (lp.alignSelf == Params.ALIGN_SELF_STRETCH) {
                      flexLine.mIndicesAlignSelfStretch.add(i);
                  }
  
                  int childWidth = lp.mLayoutWidth;
                  if (lp.flexBasisPercent != Params.FLEX_BASIS_PERCENT_DEFAULT
                          && widthMode == View.MeasureSpec.EXACTLY) {
                      childWidth = Math.round(widthSize * lp.flexBasisPercent);
                      // Use the dimension from the layout_width attribute if the widthMode is not
                      // MeasureSpec.EXACTLY even if any fraction value is set to
                      // layout_flexBasisPercent.
                      // There are likely quite few use cases where assigning any fraction values
                      // with widthMode is not MeasureSpec.EXACTLY (e.g. FlexboxLayout's layout_width
                      // is set to wrap_content)
                  }
                  int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                          getComPaddingLeft() + getComPaddingRight() + lp.mLayoutMarginLeft
                                  + lp.mLayoutMarginRight, childWidth);
                  int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
                          getComPaddingTop() + getComPaddingBottom() + lp.mLayoutMarginTop
                                  + lp.mLayoutMarginBottom, lp.mLayoutHeight);
                  child.measureComponent(childWidthMeasureSpec, childHeightMeasureSpec);
  
                  // Check the size constraint after the first measurement for the child
                  // To prevent the child's width/height violate the size constraints imposed by the
                  // {@link LayoutParams#minWidth}, {@link LayoutParams#minHeight},
                  // {@link LayoutParams#maxWidth} and {@link LayoutParams#maxHeight} attributes.
                  // E.g. When the child's layout_width is wrap_content the measured width may be
                  // less than the min width after the first measurement.
                  checkSizeConstraints(child);
  
                  childState = ViewCompat
                          .combineMeasuredStates(childState, 0);
  //                        .combineMeasuredStates(childState, ViewCompat.getMeasuredState(child));
                  largestHeightInRow = Math.max(largestHeightInRow,
                          child.getComMeasuredHeight() + lp.mLayoutMarginTop + lp.mLayoutMarginBottom);
  
                  if (isWrapRequired(widthMode, widthSize, flexLine.mMainSize,
                          child.getComMeasuredWidth() + lp.mLayoutMarginLeft + lp.mLayoutMarginRight, lp,
                          i, indexInFlexLine)) {
                      if (flexLine.mItemCount > 0) {
                          addFlexLine(flexLine);
                      }
  
                      flexLine = new FlexLine();
                      flexLine.mItemCount = 1;
                      flexLine.mMainSize = paddingStart + paddingEnd;
                      largestHeightInRow = child.getComMeasuredHeight() + lp.mLayoutMarginTop + lp.mLayoutMarginBottom;
                      indexInFlexLine = 0;
                  } else {
                      flexLine.mItemCount++;
                      indexInFlexLine++;
                  }
                  flexLine.mMainSize += child.getComMeasuredWidth() + lp.mLayoutMarginLeft + lp.mLayoutMarginRight;
                  flexLine.mTotalFlexGrow += lp.flexGrow;
                  flexLine.mTotalFlexShrink += lp.flexShrink;
                  // Temporarily set the cross axis length as the largest child in the row
                  // Expand along the cross axis depending on the mAlignContent property if needed
                  // later
                  flexLine.mCrossSize = Math.max(flexLine.mCrossSize, largestHeightInRow);
  
                  // Check if the beginning or middle divider is required for the flex item
                  if (hasDividerBeforeChildAtAlongMainAxis(i, indexInFlexLine)) {
                      flexLine.mMainSize += mDividerVerticalWidth;
                      flexLine.mDividerLengthInMainSize += mDividerVerticalWidth;
                  }
  
                  if (mFlexWrap != FLEX_WRAP_WRAP_REVERSE) {
                      flexLine.mMaxBaseline = Math
                              .max(flexLine.mMaxBaseline, child.getComBaseline() + lp.mLayoutMarginTop);
                  } else {
                      // if the flex wrap property is FLEX_WRAP_WRAP_REVERSE, calculate the
                      // baseline as the distance from the cross end and the baseline
                      // since the cross size calculation is based on the distance from the cross end
                      flexLine.mMaxBaseline = Math
                              .max(flexLine.mMaxBaseline,
                                      child.getComMeasuredHeight() - child.getComBaseline()
                                              + lp.mLayoutMarginBottom);
                  }
                  addFlexLineIfLastFlexItem(i, childCount, flexLine);
              }
          }
  
          determineMainSize(mFlexDirection, widthMeasureSpec, heightMeasureSpec);
  
          // TODO: Consider the case any individual child's alignSelf is set to ALIGN_SELF_BASELINE
          if (mAlignItems == ALIGN_ITEMS_BASELINE) {
              int viewIndex = 0;
              for (FlexLine flexLine : mFlexLines) {
                  // The largest height value that also take the baseline shift into account
                  int largestHeightInLine = Integer.MIN_VALUE;
                  for (int i = viewIndex; i < viewIndex + flexLine.mItemCount; i++) {
                      ViewBase child = getReorderedChildAt(i);
                      Params lp = (Params) child.getComLayoutParams();
                      if (mFlexWrap != FLEX_WRAP_WRAP_REVERSE) {
                          int marginTop = flexLine.mMaxBaseline - child.getComBaseline();
                          marginTop = Math.max(marginTop, lp.mLayoutMarginTop);
                          largestHeightInLine = Math.max(largestHeightInLine,
  //                                child.getHeight() + marginTop + lp.bottomMargin);
                                  child.getComMeasuredHeight() + marginTop + lp.mLayoutMarginBottom);
                      } else {
                          int marginBottom = flexLine.mMaxBaseline - child.getComMeasuredHeight() +
                                  child.getComBaseline();
                          marginBottom = Math.max(marginBottom, lp.mLayoutMarginBottom);
                          largestHeightInLine = Math.max(largestHeightInLine,
                                  child.getComMeasuredHeight() + lp.mLayoutMarginTop + marginBottom);
  //                                child.getHeight() + lp.topMargin + marginBottom);
                      }
                  }
                  flexLine.mCrossSize = largestHeightInLine;
                  viewIndex += flexLine.mItemCount;
              }
          }
  
          determineCrossSize(mFlexDirection, widthMeasureSpec, heightMeasureSpec,
                  getComPaddingTop() + getComPaddingBottom());
          // Now cross size for each flex line is determined.
          // Expand the views if alignItems (or alignSelf in each child view) is set to stretch
          stretchViews(mFlexDirection, mAlignItems);
          setMeasuredDimensionForFlex(mFlexDirection, widthMeasureSpec, heightMeasureSpec,
                  childState);
      }
  
      private void setMeasuredDimensionForFlex(@FlexDirection int flexDirection, int widthMeasureSpec,
                                               int heightMeasureSpec, int childState) {
          int widthMode = View.MeasureSpec.getMode(widthMeasureSpec);
          int widthSize = View.MeasureSpec.getSize(widthMeasureSpec);
          int heightMode = View.MeasureSpec.getMode(heightMeasureSpec);
          int heightSize = View.MeasureSpec.getSize(heightMeasureSpec);
          int calculatedMaxHeight;
          int calculatedMaxWidth;
          switch (flexDirection) {
              case FLEX_DIRECTION_ROW: // Intentional fall through
              case FLEX_DIRECTION_ROW_REVERSE:
                  calculatedMaxHeight = getSumOfCrossSize() + getComPaddingTop()
                          + getComPaddingBottom();
                  calculatedMaxWidth = getLargestMainSize();
                  break;
              case FLEX_DIRECTION_COLUMN: // Intentional fall through
              case FLEX_DIRECTION_COLUMN_REVERSE:
                  calculatedMaxHeight = getLargestMainSize();
                  calculatedMaxWidth = getSumOfCrossSize() + getComPaddingLeft() + getComPaddingRight();
                  break;
              default:
                  throw new IllegalArgumentException("Invalid flex direction: " + flexDirection);
          }
  
          int widthSizeAndState;
          switch (widthMode) {
              case View.MeasureSpec.EXACTLY:
                  if (widthSize < calculatedMaxWidth) {
                      childState = ViewCompat
                              .combineMeasuredStates(childState, ViewCompat.MEASURED_STATE_TOO_SMALL);
                  }
                  widthSizeAndState = ViewCompat.resolveSizeAndState(widthSize, widthMeasureSpec,
                          childState);
                  break;
              case View.MeasureSpec.AT_MOST: {
                  if (widthSize < calculatedMaxWidth) {
                      childState = ViewCompat
                              .combineMeasuredStates(childState, ViewCompat.MEASURED_STATE_TOO_SMALL);
                  } else {
                      widthSize = calculatedMaxWidth;
                  }
                  widthSizeAndState = ViewCompat.resolveSizeAndState(widthSize, widthMeasureSpec,
                          childState);
                  break;
              }
              case View.MeasureSpec.UNSPECIFIED: {
                  widthSizeAndState = ViewCompat
                          .resolveSizeAndState(calculatedMaxWidth, widthMeasureSpec, childState);
                  break;
              }
              default:
                  throw new IllegalStateException("Unknown width mode is set: " + widthMode);
          }
          int heightSizeAndState;
          switch (heightMode) {
              case View.MeasureSpec.EXACTLY:
                  if (heightSize < calculatedMaxHeight) {
                      childState = ViewCompat.combineMeasuredStates(childState,
                              ViewCompat.MEASURED_STATE_TOO_SMALL
                                      >> ViewCompat.MEASURED_HEIGHT_STATE_SHIFT);
                  }
                  heightSizeAndState = ViewCompat.resolveSizeAndState(heightSize, heightMeasureSpec,
                          childState);
                  break;
              case View.MeasureSpec.AT_MOST: {
                  if (heightSize < calculatedMaxHeight) {
                      childState = ViewCompat.combineMeasuredStates(childState,
                              ViewCompat.MEASURED_STATE_TOO_SMALL
                                      >> ViewCompat.MEASURED_HEIGHT_STATE_SHIFT);
                  } else {
                      heightSize = calculatedMaxHeight;
                  }
                  heightSizeAndState = ViewCompat.resolveSizeAndState(heightSize, heightMeasureSpec,
                          childState);
                  break;
              }
              case View.MeasureSpec.UNSPECIFIED: {
                  heightSizeAndState = ViewCompat.resolveSizeAndState(calculatedMaxHeight,
                          heightMeasureSpec, childState);
                  break;
              }
              default:
                  throw new IllegalStateException("Unknown height mode is set: " + heightMode);
          }
          setComMeasuredDimension(widthSizeAndState, heightSizeAndState);
      }
  
  
      private void stretchViews(int flexDirection, int alignItems) {
          if (alignItems == ALIGN_ITEMS_STRETCH) {
              int viewIndex = 0;
              for (FlexLine flexLine : mFlexLines) {
                  for (int i = 0; i < flexLine.mItemCount; i++, viewIndex++) {
                      ViewBase view = getReorderedChildAt(viewIndex);
                      Params lp = (Params) view.getComLayoutParams();
                      if (lp.alignSelf != Params.ALIGN_SELF_AUTO &&
                              lp.alignSelf != Params.ALIGN_SELF_STRETCH) {
                          continue;
                      }
                      switch (flexDirection) {
                          case FLEX_DIRECTION_ROW: // Intentional fall through
                          case FLEX_DIRECTION_ROW_REVERSE:
                              stretchViewVertically(view, flexLine.mCrossSize);
                              break;
                          case FLEX_DIRECTION_COLUMN:
                          case FLEX_DIRECTION_COLUMN_REVERSE:
                              stretchViewHorizontally(view, flexLine.mCrossSize);
                              break;
                          default:
                              throw new IllegalArgumentException(
                                      "Invalid flex direction: " + flexDirection);
                      }
                  }
              }
          } else {
              for (FlexLine flexLine : mFlexLines) {
                  for (Integer index : flexLine.mIndicesAlignSelfStretch) {
                      ViewBase view = getReorderedChildAt(index);
                      switch (flexDirection) {
                          case FLEX_DIRECTION_ROW: // Intentional fall through
                          case FLEX_DIRECTION_ROW_REVERSE:
                              stretchViewVertically(view, flexLine.mCrossSize);
                              break;
                          case FLEX_DIRECTION_COLUMN:
                          case FLEX_DIRECTION_COLUMN_REVERSE:
                              stretchViewHorizontally(view, flexLine.mCrossSize);
                              break;
                          default:
                              throw new IllegalArgumentException(
                                      "Invalid flex direction: " + flexDirection);
                      }
                  }
              }
          }
      }
  
      private void stretchViewHorizontally(ViewBase view, int crossSize) {
          Params lp = (Params) view.getComLayoutParams();
          int newWidth = crossSize - lp.mLayoutMarginLeft - lp.mLayoutMarginRight;
          newWidth = Math.max(newWidth, 0);
          view.measureComponent(View.MeasureSpec
                          .makeMeasureSpec(newWidth, View.MeasureSpec.EXACTLY),
                  View.MeasureSpec.makeMeasureSpec(view.getComMeasuredHeight(), View.MeasureSpec.EXACTLY));
      }
  
  
      private void stretchViewVertically(ViewBase view, int crossSize) {
          Params lp = (Params) view.getComLayoutParams();
          int newHeight = crossSize - lp.mLayoutMarginTop - lp.mLayoutMarginBottom;
          newHeight = Math.max(newHeight, 0);
          view.measureComponent(View.MeasureSpec
                          .makeMeasureSpec(view.getComMeasuredWidth(), View.MeasureSpec.EXACTLY),
                  View.MeasureSpec.makeMeasureSpec(newHeight, View.MeasureSpec.EXACTLY));
      }
  
      private void determineCrossSize(int flexDirection, int widthMeasureSpec,
                                      int heightMeasureSpec, int paddingAlongCrossAxis) {
          // The MeasureSpec mode along the cross axis
          int mode;
          // The MeasureSpec size along the cross axis
          int size;
          switch (flexDirection) {
              case FLEX_DIRECTION_ROW: // Intentional fall through
              case FLEX_DIRECTION_ROW_REVERSE:
                  mode = View.MeasureSpec.getMode(heightMeasureSpec);
                  size = View.MeasureSpec.getSize(heightMeasureSpec);
                  break;
              case FLEX_DIRECTION_COLUMN: // Intentional fall through
              case FLEX_DIRECTION_COLUMN_REVERSE:
                  mode = View.MeasureSpec.getMode(widthMeasureSpec);
                  size = View.MeasureSpec.getSize(widthMeasureSpec);
                  break;
              default:
                  throw new IllegalArgumentException("Invalid flex direction: " + flexDirection);
          }
          if (mode == View.MeasureSpec.EXACTLY) {
              int totalCrossSize = getSumOfCrossSize() + paddingAlongCrossAxis;
              if (mFlexLines.size() == 1) {
                  mFlexLines.get(0).mCrossSize = size - paddingAlongCrossAxis;
                  // alignContent property is valid only if the Flexbox has at least two lines
              } else if (mFlexLines.size() >= 2 && totalCrossSize < size) {
                  switch (mAlignContent) {
                      case ALIGN_CONTENT_STRETCH: {
                          float freeSpaceUnit = (size - totalCrossSize) / (float) mFlexLines.size();
                          float accumulatedError = 0;
                          for (int i = 0, flexLinesSize = mFlexLines.size(); i < flexLinesSize; i++) {
                              FlexLine flexLine = mFlexLines.get(i);
                              float newCrossSizeAsFloat = flexLine.mCrossSize + freeSpaceUnit;
                              if (i == mFlexLines.size() - 1) {
                                  newCrossSizeAsFloat += accumulatedError;
                                  accumulatedError = 0;
                              }
                              int newCrossSize = Math.round(newCrossSizeAsFloat);
                              accumulatedError += (newCrossSizeAsFloat - newCrossSize);
                              if (accumulatedError > 1) {
                                  newCrossSize += 1;
                                  accumulatedError -= 1;
                              } else if (accumulatedError < -1) {
                                  newCrossSize -= 1;
                                  accumulatedError += 1;
                              }
                              flexLine.mCrossSize = newCrossSize;
                          }
                          break;
                      }
                      case ALIGN_CONTENT_SPACE_AROUND: {
                          // The value of free space along the cross axis which needs to be put on top
                          // and below the bottom of each flex line.
                          int spaceTopAndBottom = size - totalCrossSize;
                          // The number of spaces along the cross axis
                          int numberOfSpaces = mFlexLines.size() * 2;
                          spaceTopAndBottom = spaceTopAndBottom / numberOfSpaces;
                          List<FlexLine> newFlexLines = new ArrayList<>();
                          FlexLine dummySpaceFlexLine = new FlexLine();
                          dummySpaceFlexLine.mCrossSize = spaceTopAndBottom;
                          for (FlexLine flexLine : mFlexLines) {
                              newFlexLines.add(dummySpaceFlexLine);
                              newFlexLines.add(flexLine);
                              newFlexLines.add(dummySpaceFlexLine);
                          }
                          mFlexLines = newFlexLines;
                          break;
                      }
                      case ALIGN_CONTENT_SPACE_BETWEEN: {
                          // The value of free space along the cross axis between each flex line.
                          float spaceBetweenFlexLine = size - totalCrossSize;
                          int numberOfSpaces = mFlexLines.size() - 1;
                          spaceBetweenFlexLine = spaceBetweenFlexLine / (float) numberOfSpaces;
                          float accumulatedError = 0;
                          List<FlexLine> newFlexLines = new ArrayList<>();
                          for (int i = 0, flexLineSize = mFlexLines.size(); i < flexLineSize; i++) {
                              FlexLine flexLine = mFlexLines.get(i);
                              newFlexLines.add(flexLine);
  
                              if (i != mFlexLines.size() - 1) {
                                  FlexLine dummySpaceFlexLine = new FlexLine();
                                  if (i == mFlexLines.size() - 2) {
                                      // The last dummy space block in the flex container.
                                      // Adjust the cross size by the accumulated error.
                                      dummySpaceFlexLine.mCrossSize = Math
                                              .round(spaceBetweenFlexLine + accumulatedError);
                                      accumulatedError = 0;
                                  } else {
                                      dummySpaceFlexLine.mCrossSize = Math
                                              .round(spaceBetweenFlexLine);
                                  }
                                  accumulatedError += (spaceBetweenFlexLine
                                          - dummySpaceFlexLine.mCrossSize);
                                  if (accumulatedError > 1) {
                                      dummySpaceFlexLine.mCrossSize += 1;
                                      accumulatedError -= 1;
                                  } else if (accumulatedError < -1) {
                                      dummySpaceFlexLine.mCrossSize -= 1;
                                      accumulatedError += 1;
                                  }
                                  newFlexLines.add(dummySpaceFlexLine);
                              }
                          }
                          mFlexLines = newFlexLines;
                          break;
                      }
                      case ALIGN_CONTENT_CENTER: {
                          int spaceAboveAndBottom = size - totalCrossSize;
                          spaceAboveAndBottom = spaceAboveAndBottom / 2;
                          List<FlexLine> newFlexLines = new ArrayList<>();
                          FlexLine dummySpaceFlexLine = new FlexLine();
                          dummySpaceFlexLine.mCrossSize = spaceAboveAndBottom;
                          for (int i = 0, flexLineSize = mFlexLines.size(); i < flexLineSize; i++) {
                              if (i == 0) {
                                  newFlexLines.add(dummySpaceFlexLine);
                              }
                              FlexLine flexLine = mFlexLines.get(i);
                              newFlexLines.add(flexLine);
                              if (i == mFlexLines.size() - 1) {
                                  newFlexLines.add(dummySpaceFlexLine);
                              }
                          }
                          mFlexLines = newFlexLines;
                          break;
                      }
                      case ALIGN_CONTENT_FLEX_END: {
                          int spaceTop = size - totalCrossSize;
                          FlexLine dummySpaceFlexLine = new FlexLine();
                          dummySpaceFlexLine.mCrossSize = spaceTop;
                          mFlexLines.add(0, dummySpaceFlexLine);
                          break;
                      }
                  }
              }
          }
      }
  
      private int getSumOfCrossSize() {
          int sum = 0;
          for (int i = 0, size = mFlexLines.size(); i < size; i++) {
              FlexLine flexLine = mFlexLines.get(i);
  
              // Judge if the beginning or middle dividers are required
              if (hasDividerBeforeFlexLine(i)) {
                  if (isMainAxisDirectionHorizontal(mFlexDirection)) {
                      sum += mDividerHorizontalHeight;
                  } else {
                      sum += mDividerVerticalWidth;
                  }
              }
  
              // Judge if the end divider is required
              if (hasEndDividerAfterFlexLine(i)) {
                  if (isMainAxisDirectionHorizontal(mFlexDirection)) {
                      sum += mDividerHorizontalHeight;
                  } else {
                      sum += mDividerVerticalWidth;
                  }
              }
              sum += flexLine.mCrossSize;
          }
          return sum;
      }
  
      private boolean hasEndDividerAfterFlexLine(int flexLineIndex) {
          if (flexLineIndex < 0 || flexLineIndex >= mFlexLines.size()) {
              return false;
          }
  
          for (int i = flexLineIndex + 1; i < mFlexLines.size(); i++) {
              if (mFlexLines.get(i).mItemCount > 0) {
                  return false;
              }
          }
          if (isMainAxisDirectionHorizontal(mFlexDirection)) {
              return (mShowDividerHorizontal & SHOW_DIVIDER_END) != 0;
          } else {
              return (mShowDividerVertical & SHOW_DIVIDER_END) != 0;
          }
  
      }
  
      private boolean hasDividerBeforeFlexLine(int flexLineIndex) {
          if (flexLineIndex < 0 || flexLineIndex >= mFlexLines.size()) {
              return false;
          }
          if (allFlexLinesAreDummyBefore(flexLineIndex)) {
              if (isMainAxisDirectionHorizontal(mFlexDirection)) {
                  return (mShowDividerHorizontal & SHOW_DIVIDER_BEGINNING) != 0;
              } else {
                  return (mShowDividerVertical & SHOW_DIVIDER_BEGINNING) != 0;
              }
          } else {
              if (isMainAxisDirectionHorizontal(mFlexDirection)) {
                  return (mShowDividerHorizontal & SHOW_DIVIDER_MIDDLE) != 0;
              } else {
                  return (mShowDividerVertical & SHOW_DIVIDER_MIDDLE) != 0;
              }
          }
      }
  
      private boolean allFlexLinesAreDummyBefore(int flexLineIndex) {
          for (int i = 0; i < flexLineIndex; i++) {
              if (mFlexLines.get(i).mItemCount > 0) {
                  return false;
              }
          }
          return true;
      }
  
      private void determineMainSize(@FlexDirection int flexDirection, int widthMeasureSpec,
                                     int heightMeasureSpec) {
          int mainSize;
          int paddingAlongMainAxis;
          switch (flexDirection) {
              case FLEX_DIRECTION_ROW: // Intentional fall through
              case FLEX_DIRECTION_ROW_REVERSE:
                  int widthMode = View.MeasureSpec.getMode(widthMeasureSpec);
                  int widthSize = View.MeasureSpec.getSize(widthMeasureSpec);
                  if (widthMode == View.MeasureSpec.EXACTLY) {
                      mainSize = widthSize;
                  } else {
                      mainSize = getLargestMainSize();
                  }
                  paddingAlongMainAxis = getComPaddingLeft() + getComPaddingRight();
                  break;
              case FLEX_DIRECTION_COLUMN: // Intentional fall through
              case FLEX_DIRECTION_COLUMN_REVERSE:
                  int heightMode = View.MeasureSpec.getMode(heightMeasureSpec);
                  int heightSize = View.MeasureSpec.getSize(heightMeasureSpec);
                  if (heightMode == View.MeasureSpec.EXACTLY) {
                      mainSize = heightSize;
                  } else {
                      mainSize = getLargestMainSize();
                  }
                  paddingAlongMainAxis = getComPaddingTop() + getComPaddingBottom();
                  break;
              default:
                  throw new IllegalArgumentException("Invalid flex direction: " + flexDirection);
          }
  
          int childIndex = 0;
          for (FlexLine flexLine : mFlexLines) {
              if (flexLine.mMainSize < mainSize) {
                  childIndex = expandFlexItems(flexLine, flexDirection, mainSize,
                          paddingAlongMainAxis, childIndex);
              } else {
                  childIndex = shrinkFlexItems(flexLine, flexDirection, mainSize,
                          paddingAlongMainAxis, childIndex);
              }
          }
      }
  
      private int shrinkFlexItems(FlexLine flexLine, @FlexDirection int flexDirection,
                                  int maxMainSize, int paddingAlongMainAxis, int startIndex) {
          int childIndex = startIndex;
          int sizeBeforeShrink = flexLine.mMainSize;
          if (flexLine.mTotalFlexShrink <= 0 || maxMainSize > flexLine.mMainSize) {
              childIndex += flexLine.mItemCount;
              return childIndex;
          }
          boolean needsReshrink = false;
          float unitShrink = (flexLine.mMainSize - maxMainSize) / flexLine.mTotalFlexShrink;
          float accumulatedRoundError = 0;
          flexLine.mMainSize = paddingAlongMainAxis + flexLine.mDividerLengthInMainSize;
          for (int i = 0; i < flexLine.mItemCount; i++) {
              ViewBase child = getReorderedChildAt(childIndex);
              if (child == null) {
                  continue;
  //            } else if (child.getVisibility() == View.GONE) {
  //            } else if (!child.isVisible()) {
              } else if (child.getVisibility() == ViewBaseCommon.GONE) {
                  childIndex++;
                  continue;
              }
              Params lp = (Params) child.getComLayoutParams();
              if (isMainAxisDirectionHorizontal(flexDirection)) {
                  // The direction of main axis is horizontal
                  if (!mChildrenFrozen[childIndex]) {
                      float rawCalculatedWidth = child.getComMeasuredWidth()
                              - unitShrink * lp.flexShrink;
                      if (i == flexLine.mItemCount - 1) {
                          rawCalculatedWidth += accumulatedRoundError;
                          accumulatedRoundError = 0;
                      }
                      int newWidth = Math.round(rawCalculatedWidth);
                      if (newWidth < lp.minWidth) {
                          // This means the child doesn't have enough space to distribute the negative
                          // free space. To adjust the flex line length down to the maxMainSize, remaining
                          // negative free space needs to be re-distributed to other flex items
                          // (children views). In that case, invoke this method again with the same
                          // startIndex.
                          needsReshrink = true;
                          newWidth = lp.minWidth;
                          mChildrenFrozen[childIndex] = true;
                          flexLine.mTotalFlexShrink -= lp.flexShrink;
                      } else {
                          accumulatedRoundError += (rawCalculatedWidth - newWidth);
                          if (accumulatedRoundError > 1.0) {
                              newWidth += 1;
                              accumulatedRoundError -= 1;
                          } else if (accumulatedRoundError < -1.0) {
                              newWidth -= 1;
                              accumulatedRoundError += 1;
                          }
                      }
                      child.measureComponent(View.MeasureSpec.makeMeasureSpec(newWidth, View.MeasureSpec.EXACTLY),
                              View.MeasureSpec.makeMeasureSpec(child.getComMeasuredHeight(),
                                      View.MeasureSpec.EXACTLY));
                  }
                  flexLine.mMainSize += child.getComMeasuredWidth() + lp.mLayoutMarginLeft + lp.mLayoutMarginRight;
              } else {
                  // The direction of main axis is vertical
                  if (!mChildrenFrozen[childIndex]) {
                      float rawCalculatedHeight = child.getComMeasuredHeight()
                              - unitShrink * lp.flexShrink;
                      if (i == flexLine.mItemCount - 1) {
                          rawCalculatedHeight += accumulatedRoundError;
                          accumulatedRoundError = 0;
                      }
                      int newHeight = Math.round(rawCalculatedHeight);
                      if (newHeight < lp.minHeight) {
                          // Need to invoke this method again like the case flex direction is vertical
                          needsReshrink = true;
                          newHeight = lp.minHeight;
                          mChildrenFrozen[childIndex] = true;
                          flexLine.mTotalFlexShrink -= lp.flexShrink;
                      } else {
                          accumulatedRoundError += (rawCalculatedHeight - newHeight);
                          if (accumulatedRoundError > 1.0) {
                              newHeight += 1;
                              accumulatedRoundError -= 1;
                          } else if (accumulatedRoundError < -1.0) {
                              newHeight -= 1;
                              accumulatedRoundError += 1;
                          }
                      }
                      child.measureComponent(View.MeasureSpec.makeMeasureSpec(child.getComMeasuredWidth(),
                              View.MeasureSpec.EXACTLY),
                              View.MeasureSpec.makeMeasureSpec(newHeight, View.MeasureSpec.EXACTLY));
                  }
                  flexLine.mMainSize += child.getComMeasuredHeight() + lp.mLayoutMarginTop + lp.mLayoutMarginBottom;
              }
              childIndex++;
          }
  
          if (needsReshrink && sizeBeforeShrink != flexLine.mMainSize) {
              // Re-invoke the method with the same startIndex to distribute the negative free space
              // that wasn't fully distributed (because some views length were not enough)
              shrinkFlexItems(flexLine, flexDirection, maxMainSize, paddingAlongMainAxis, startIndex);
          }
          return childIndex;
      }
  
      private int expandFlexItems(FlexLine flexLine, @FlexDirection int flexDirection,
                                  int maxMainSize, int paddingAlongMainAxis, int startIndex) {
          int childIndex = startIndex;
          if (flexLine.mTotalFlexGrow <= 0 || maxMainSize < flexLine.mMainSize) {
              childIndex += flexLine.mItemCount;
              return childIndex;
          }
          int sizeBeforeExpand = flexLine.mMainSize;
          boolean needsReexpand = false;
          float unitSpace = (maxMainSize - flexLine.mMainSize) / flexLine.mTotalFlexGrow;
          flexLine.mMainSize = paddingAlongMainAxis + flexLine.mDividerLengthInMainSize;
          float accumulatedRoundError = 0;
          for (int i = 0; i < flexLine.mItemCount; i++) {
              ViewBase child = getReorderedChildAt(childIndex);
              if (child == null) {
                  continue;
  //            } else if (child.getVisibility() == View.GONE) {
  //            } else if (!child.isVisible()) {
              } else if (child.getVisibility() == ViewBaseCommon.GONE) {
                  childIndex++;
                  continue;
              }
              Params lp = (Params) child.getComLayoutParams();
              if (isMainAxisDirectionHorizontal(flexDirection)) {
                  // The direction of the main axis is horizontal
                  if (!mChildrenFrozen[childIndex]) {
                      float rawCalculatedWidth = child.getComMeasuredWidth() + unitSpace * lp.flexGrow;
                      if (i == flexLine.mItemCount - 1) {
                          rawCalculatedWidth += accumulatedRoundError;
                          accumulatedRoundError = 0;
                      }
                      int newWidth = Math.round(rawCalculatedWidth);
                      if (newWidth > lp.maxWidth) {
                          // This means the child can't expand beyond the value of the maxWidth attribute.
                          // To adjust the flex line length to the size of maxMainSize, remaining
                          // positive free space needs to be re-distributed to other flex items
                          // (children views). In that case, invoke this method again with the same
                          // startIndex.
                          needsReexpand = true;
                          newWidth = lp.maxWidth;
                          mChildrenFrozen[childIndex] = true;
                          flexLine.mTotalFlexGrow -= lp.flexGrow;
                      } else {
                          accumulatedRoundError += (rawCalculatedWidth - newWidth);
                          if (accumulatedRoundError > 1.0) {
                              newWidth += 1;
                              accumulatedRoundError -= 1.0;
                          } else if (accumulatedRoundError < -1.0) {
                              newWidth -= 1;
                              accumulatedRoundError += 1.0;
                          }
                      }
                      child.measureComponent(View.MeasureSpec.makeMeasureSpec(newWidth, View.MeasureSpec.EXACTLY),
                              View.MeasureSpec
                                      .makeMeasureSpec(child.getComMeasuredHeight(),
                                              View.MeasureSpec.EXACTLY));
                  }
                  flexLine.mMainSize += child.getComMeasuredWidth() + lp.mLayoutMarginLeft + lp.mLayoutMarginRight;
              } else {
                  // The direction of the main axis is vertical
                  if (!mChildrenFrozen[childIndex]) {
                      float rawCalculatedHeight = child.getComMeasuredHeight() + unitSpace * lp.flexGrow;
                      if (i == flexLine.mItemCount - 1) {
                          rawCalculatedHeight += accumulatedRoundError;
                          accumulatedRoundError = 0;
                      }
                      int newHeight = Math.round(rawCalculatedHeight);
                      if (newHeight > lp.maxHeight) {
                          // This means the child can't expand beyond the value of the maxHeight
                          // attribute.
                          // To adjust the flex line length to the size of maxMainSize, remaining
                          // positive free space needs to be re-distributed to other flex items
                          // (children views). In that case, invoke this method again with the same
                          // startIndex.
                          needsReexpand = true;
                          newHeight = lp.maxHeight;
                          mChildrenFrozen[childIndex] = true;
                          flexLine.mTotalFlexGrow -= lp.flexGrow;
                      } else {
                          accumulatedRoundError += (rawCalculatedHeight - newHeight);
                          if (accumulatedRoundError > 1.0) {
                              newHeight += 1;
                              accumulatedRoundError -= 1.0;
                          } else if (accumulatedRoundError < -1.0) {
                              newHeight -= 1;
                              accumulatedRoundError += 1.0;
                          }
                      }
                      child.measureComponent(View.MeasureSpec.makeMeasureSpec(child.getComMeasuredWidth(),
                              View.MeasureSpec.EXACTLY),
                              View.MeasureSpec.makeMeasureSpec(newHeight, View.MeasureSpec.EXACTLY));
                  }
                  flexLine.mMainSize += child.getComMeasuredHeight() + lp.mLayoutMarginTop + lp.mLayoutMarginBottom;
              }
              childIndex++;
          }
  
          if (needsReexpand && sizeBeforeExpand != flexLine.mMainSize) {
              // Re-invoke the method with the same startIndex to distribute the positive free space
              // that wasn't fully distributed (because of maximum length constraint)
              expandFlexItems(flexLine, flexDirection, maxMainSize, paddingAlongMainAxis, startIndex);
          }
          return childIndex;
      }
  
  
      private int getLargestMainSize() {
          int largestSize = Integer.MIN_VALUE;
          for (FlexLine flexLine : mFlexLines) {
              largestSize = Math.max(largestSize, flexLine.mMainSize);
          }
          return largestSize;
      }
  
      private boolean isWrapRequired(int mode, int maxSize, int currentLength, int childLength,
                                     Params lp, int childAbsoluteIndex, int childRelativeIndexInFlexLine) {
          if (mFlexWrap == FLEX_WRAP_NOWRAP) {
              return false;
          }
          if (lp.wrapBefore) {
              return true;
          }
          if (mode == View.MeasureSpec.UNSPECIFIED) {
              return false;
          }
          if (isMainAxisDirectionHorizontal(mFlexDirection)) {
              if (hasDividerBeforeChildAtAlongMainAxis(childAbsoluteIndex,
                      childRelativeIndexInFlexLine)) {
                  childLength += mDividerVerticalWidth;
              }
              if ((mShowDividerVertical & SHOW_DIVIDER_END) > 0) {
                  childLength += mDividerVerticalWidth;
              }
          } else {
              if (hasDividerBeforeChildAtAlongMainAxis(childAbsoluteIndex,
                      childRelativeIndexInFlexLine)) {
                  childLength += mDividerHorizontalHeight;
              }
              if ((mShowDividerHorizontal & SHOW_DIVIDER_END) > 0) {
                  childLength += mDividerHorizontalHeight;
              }
          }
          return maxSize < currentLength + childLength;
      }
  
      private boolean hasDividerBeforeChildAtAlongMainAxis(int childAbsoluteIndex,
                                                           int childRelativeIndexInFlexLine) {
          if (allViewsAreGoneBefore(childAbsoluteIndex, childRelativeIndexInFlexLine)) {
              if (isMainAxisDirectionHorizontal(mFlexDirection)) {
                  return (mShowDividerVertical & SHOW_DIVIDER_BEGINNING) != 0;
              } else {
                  return (mShowDividerHorizontal & SHOW_DIVIDER_BEGINNING) != 0;
              }
          } else {
              if (isMainAxisDirectionHorizontal(mFlexDirection)) {
                  return (mShowDividerVertical & SHOW_DIVIDER_MIDDLE) != 0;
              } else {
                  return (mShowDividerHorizontal & SHOW_DIVIDER_MIDDLE) != 0;
              }
          }
      }
  
      private boolean allViewsAreGoneBefore(int childAbsoluteIndex,
                                            int childRelativeIndexInFlexLine) {
          for (int i = 1; i <= childRelativeIndexInFlexLine; i++) {
              ViewBase view = getReorderedChildAt(childAbsoluteIndex - i);
  //            if (view != null && view.getVisibility() != View.GONE) {
  //            if (view != null && view.isVisible()) {
              if (view != null && view.getVisibility() != ViewBaseCommon.GONE) {
                  return false;
              }
          }
          return true;
      }
  
      private void checkSizeConstraints(ViewBase view) {
          boolean needsMeasure = false;
          Params lp = (Params) view.getComLayoutParams();
          int childWidth = view.getComMeasuredWidth();
          int childHeight = view.getComMeasuredHeight();
  
          if (view.getComMeasuredWidth() < lp.minWidth) {
              needsMeasure = true;
              childWidth = lp.minWidth;
          } else if (view.getComMeasuredWidth() > lp.maxWidth) {
              needsMeasure = true;
              childWidth = lp.maxWidth;
          }
  
          if (childHeight < lp.minHeight) {
              needsMeasure = true;
              childHeight = lp.minHeight;
          } else if (childHeight > lp.maxHeight) {
              needsMeasure = true;
              childHeight = lp.maxHeight;
          }
          if (needsMeasure) {
              view.measureComponent(View.MeasureSpec.makeMeasureSpec(childWidth, View.MeasureSpec.EXACTLY),
                      View.MeasureSpec.makeMeasureSpec(childHeight, View.MeasureSpec.EXACTLY));
          }
      }
  
      private void addFlexLineIfLastFlexItem(int childIndex, int childCount, FlexLine flexLine) {
          if (childIndex == childCount - 1 && flexLine.mItemCount != 0) {
              // Add the flex line if this item is the last item
              addFlexLine(flexLine);
          }
      }
  
      private void addFlexLine(FlexLine flexLine) {
          // The size of the end divider isn't added until the flexLine is added to the flex container
          // take the divider width (or height) into account when adding the flex line.
          if (isMainAxisDirectionHorizontal(mFlexDirection)) {
              if ((mShowDividerVertical & SHOW_DIVIDER_END) > 0) {
                  flexLine.mMainSize += mDividerVerticalWidth;
                  flexLine.mDividerLengthInMainSize += mDividerVerticalWidth;
              }
          } else {
              if ((mShowDividerHorizontal & SHOW_DIVIDER_END) > 0) {
                  flexLine.mMainSize += mDividerHorizontalHeight;
                  flexLine.mDividerLengthInMainSize += mDividerHorizontalHeight;
              }
          }
          mFlexLines.add(flexLine);
      }
  
      private boolean isMainAxisDirectionHorizontal(@FlexDirection int flexDirection) {
          return flexDirection == FLEX_DIRECTION_ROW
                  || flexDirection == FLEX_DIRECTION_ROW_REVERSE;
      }
  
      public ViewBase getReorderedChildAt(int index) {
          if (index < 0 || index >= mReorderedIndices.length) {
              return null;
          }
  //        return getChildAt(mReorderedIndices[index]);
          return mSubViews.get(mReorderedIndices[index]);
      }
  
      private int[] createReorderedIndices() {
  //        int childCount = getChildCount();
  //        List<Order> orders = createOrders(childCount);
          int childCount = mSubViews.size();
          List<Order> orders = createOrders(childCount);
          return sortOrdersIntoReorderedIndices(childCount, orders);
      }
  
      private int[] sortOrdersIntoReorderedIndices(int childCount, List<Order> orders) {
          Collections.sort(orders);
          if (mOrderCache == null) {
              mOrderCache = new SparseIntArray(childCount);
          }
          mOrderCache.clear();
          int[] reorderedIndices = new int[childCount];
          int i = 0;
          for (Order order : orders) {
              reorderedIndices[i] = order.index;
              mOrderCache.append(i, order.order);
              i++;
          }
          return reorderedIndices;
      }
  
      @NonNull
      private List<Order> createOrders(int childCount) {
          List<Order> orders = new ArrayList<>();
          for (int i = 0; i < childCount; i++) {
              ViewBase child = mSubViews.get(i);
              Params params = (Params) child.getComLayoutParams();
              Order order = new Order();
              order.order = params.order;
              order.index = i;
              orders.add(order);
          }
          return orders;
      }
  
      private boolean isOrderChangedFromLastMeasurement() {
  //        int childCount = getChildCount();
          int childCount = mSubViews.size();
          if (mOrderCache == null) {
              mOrderCache = new SparseIntArray(childCount);
          }
          if (mOrderCache.size() != childCount) {
              return true;
          }
          for (int i = 0; i < childCount; i++) {
  //            View view = getChildAt(i);
              ViewBase view = mSubViews.get(i);
              if (view == null) {
                  continue;
              }
              Params lp = (Params) view.getComLayoutParams();
              if (lp.order != mOrderCache.get(i)) {
                  return true;
              }
          }
          return false;
      }
  
      @Override
      public void onComLayout(boolean changed, int left, int top, int right, int bottom) {
  //        Log.d(TAG, "onComLayout: changed:" + changed + "  left:" + left + "  top:" + top + "  right:" + right + "  bottom:" + bottom);
  //        int layoutDirection = ViewCompat.getLayoutDirection(this);
          boolean isRtl = false;
          switch (mFlexDirection) {
              case FLEX_DIRECTION_ROW:
  //                isRtl = layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL;
                  layoutHorizontal(isRtl, left, top, right, bottom);
                  break;
              case FLEX_DIRECTION_ROW_REVERSE:
  //                isRtl = layoutDirection != ViewCompat.LAYOUT_DIRECTION_RTL;
                  isRtl = true;
                  layoutHorizontal(isRtl, left, top, right, bottom);
                  break;
              case FLEX_DIRECTION_COLUMN:
  //                isRtl = layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL;
                  if (mFlexWrap == FLEX_WRAP_WRAP_REVERSE) {
                      isRtl = !isRtl;
                  }
                  layoutVertical(isRtl, false, left, top, right, bottom);
                  break;
              case FLEX_DIRECTION_COLUMN_REVERSE:
  //                isRtl = layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL;
                  if (mFlexWrap == FLEX_WRAP_WRAP_REVERSE) {
                      isRtl = !isRtl;
                  }
                  layoutVertical(isRtl, true, left, top, right, bottom);
                  break;
              default:
                  throw new IllegalStateException("Invalid flex direction is set: " + mFlexDirection);
          }
      }
  
      private void layoutVertical(boolean isRtl, boolean fromBottomToTop, int left, int top,
                                  int right, int bottom) {
  //        Log.d(TAG, "layoutVertical id:" + this.getId());
          int paddingTop = getComPaddingTop();
          int paddingBottom = getComPaddingBottom();
  
          int paddingRight = getComPaddingRight();
          int childLeft = left + getComPaddingLeft();
  //        int childLeft = getComPaddingLeft();
          int currentViewIndex = 0;
  
          int width = right - left;
          int height = bottom - top;
          // childRight is used if the mFlexWrap is FLEX_WRAP_WRAP_REVERSE otherwise
          // childLeft is used to align the horizontal position of the children views.
          int childRight = left + width - paddingRight;
  //        int childRight = width - paddingRight;
  
          // Use float to reduce the round error that may happen in when justifyContent ==
          // SPACE_BETWEEN or SPACE_AROUND
          float childTop;
  
          // Used only for if the direction is from bottom to top
          float childBottom;
  
          for (int i = 0, size = mFlexLines.size(); i < size; i++) {
              FlexLine flexLine = mFlexLines.get(i);
              if (hasDividerBeforeFlexLine(i)) {
                  childLeft += mDividerVerticalWidth;
                  childRight -= mDividerVerticalWidth;
              }
              float spaceBetweenItem = 0f;
              switch (mJustifyContent) {
                  case JUSTIFY_CONTENT_FLEX_START:
                      childTop = top + paddingTop;
                      childBottom = top + height - paddingBottom;
  //                    childTop = paddingTop;
  //                    childBottom = height - paddingBottom;
                      break;
                  case JUSTIFY_CONTENT_FLEX_END:
                      childTop = top + height - flexLine.mMainSize + paddingBottom;
                      childBottom = top + flexLine.mMainSize - paddingTop;
  //                    childTop = height - flexLine.mMainSize + paddingBottom;
  //                    childBottom = flexLine.mMainSize - paddingTop;
                      break;
                  case JUSTIFY_CONTENT_CENTER:
                      childTop = top + paddingTop + (height - flexLine.mMainSize) / 2f;
                      childBottom = top + height - paddingBottom - (height - flexLine.mMainSize) / 2f;
  //                    childTop = paddingTop + (height - flexLine.mMainSize) / 2f;
  //                    childBottom = height - paddingBottom - (height - flexLine.mMainSize) / 2f;
                      break;
                  case JUSTIFY_CONTENT_SPACE_AROUND:
                      if (flexLine.mItemCount != 0) {
                          spaceBetweenItem = (height - flexLine.mMainSize)
                                  / (float) flexLine.mItemCount;
                      }
                      childTop = top + paddingTop + spaceBetweenItem / 2f;
                      childBottom = top + height - paddingBottom - spaceBetweenItem / 2f;
  //                    childTop = paddingTop + spaceBetweenItem / 2f;
  //                    childBottom = height - paddingBottom - spaceBetweenItem / 2f;
                      break;
                  case JUSTIFY_CONTENT_SPACE_BETWEEN:
                      childTop = top + paddingTop;
  //                    childTop = paddingTop;
                      float denominator = flexLine.mItemCount != 1 ? flexLine.mItemCount - 1 : 1f;
                      spaceBetweenItem = (height - flexLine.mMainSize) / denominator;
                      childBottom = top + height - paddingBottom;
  //                    childBottom = height - paddingBottom;
                      break;
                  default:
                      throw new IllegalStateException(
                              "Invalid justifyContent is set: " + mJustifyContent);
              }
              spaceBetweenItem = Math.max(spaceBetweenItem, 0);
  
              for (int j = 0; j < flexLine.mItemCount; j++) {
                  ViewBase child = getReorderedChildAt(currentViewIndex);
                  if (child == null) {
                      continue;
  //                } else if (child.getVisibility() == View.GONE) {
  //                } else if (!child.isVisible()) {
                  } else if (child.getVisibility() == ViewBaseCommon.GONE) {
                      currentViewIndex++;
                      continue;
                  }
                  Params lp = ((Params) child.getComLayoutParams());
                  childTop += lp.mLayoutMarginTop;
                  childBottom -= lp.mLayoutMarginBottom;
                  if (hasDividerBeforeChildAtAlongMainAxis(currentViewIndex, j)) {
                      childTop += mDividerHorizontalHeight;
                      childBottom -= mDividerHorizontalHeight;
                  }
                  if (isRtl) {
                      if (fromBottomToTop) {
                          layoutSingleChildVertical(child, flexLine, true, mAlignItems,
                                  childRight - child.getComMeasuredWidth(),
                                  Math.round(childBottom) - child.getComMeasuredHeight(), childRight,
                                  Math.round(childBottom));
                      } else {
                          layoutSingleChildVertical(child, flexLine, true, mAlignItems,
                                  childRight - child.getComMeasuredWidth(), Math.round(childTop),
                                  childRight, Math.round(childTop) + child.getComMeasuredHeight());
                      }
                  } else {
                      if (fromBottomToTop) {
                          layoutSingleChildVertical(child, flexLine, false, mAlignItems,
                                  childLeft, Math.round(childBottom) - child.getComMeasuredHeight(),
                                  childLeft + child.getComMeasuredWidth(), Math.round(childBottom));
                      } else {
  //                        Log.d(TAG, "layout vertical child:" + child.getId());
                          layoutSingleChildVertical(child, flexLine, false, mAlignItems,
                                  childLeft, Math.round(childTop),
                                  childLeft + child.getComMeasuredWidth(),
                                  Math.round(childTop) + child.getComMeasuredHeight());
                      }
                  }
                  childTop += child.getComMeasuredHeight() + spaceBetweenItem + lp.mLayoutMarginBottom;
                  childBottom -= child.getComMeasuredHeight() + spaceBetweenItem + lp.mLayoutMarginTop;
                  currentViewIndex++;
  
  //                flexLine.mLeft = Math.min(flexLine.mLeft, child.getLeft() - lp.mLayoutMarginLeft);
  //                flexLine.mTop = Math.min(flexLine.mTop, child.getTop() - lp.mLayoutMarginTop);
  //                flexLine.mRight = Math.max(flexLine.mRight, child.getRight() + lp.mLayoutMarginRight);
  //                flexLine.mBottom = Math.max(flexLine.mBottom, child.getBottom() + lp.mLayoutMarginBottom);
              }
              childLeft += flexLine.mCrossSize;
              childRight -= flexLine.mCrossSize;
          }
      }
  
      private void layoutSingleChildVertical(ViewBase view, FlexLine flexLine, boolean isRtl,
                                             int alignItems, int left, int top, int right, int bottom) {
  //        Log.d(TAG, "layoutSingleChildVertical left:" + left + "  top:" + top);
          Params lp = (Params) view.getComLayoutParams();
          if (lp.alignSelf != Params.ALIGN_SELF_AUTO) {
              // Expecting the values for alignItems and alignSelf match except for ALIGN_SELF_AUTO.
              // Assigning the alignSelf value as alignItems should work.
              alignItems = lp.alignSelf;
          }
          int crossSize = flexLine.mCrossSize;
          switch (alignItems) {
              case ALIGN_ITEMS_FLEX_START: // Intentional fall through
              case ALIGN_ITEMS_STRETCH: // Intentional fall through
              case ALIGN_ITEMS_BASELINE:
                  if (!isRtl) {
                      view.comLayout(left + lp.mLayoutMarginLeft, top, right + lp.mLayoutMarginLeft, bottom);
                  } else {
                      view.comLayout(left - lp.mLayoutMarginRight, top, right - lp.mLayoutMarginRight, bottom);
                  }
                  break;
              case ALIGN_ITEMS_FLEX_END:
                  if (!isRtl) {
                      view.comLayout(left + crossSize - view.getComMeasuredWidth() - lp.mLayoutMarginRight,
                              top, right + crossSize - view.getComMeasuredWidth() - lp.mLayoutMarginRight,
                              bottom);
                  } else {
                      // If the flexWrap == FLEX_WRAP_WRAP_REVERSE, the direction of the
                      // flexEnd is flipped (from left to right).
                      view.comLayout(left - crossSize + view.getComMeasuredWidth() + lp.mLayoutMarginLeft, top,
                              right - crossSize + view.getComMeasuredWidth() + lp.mLayoutMarginLeft,
                              bottom);
                  }
                  break;
              case ALIGN_ITEMS_CENTER:
                  int leftFromCrossAxis = (crossSize - view.getComMeasuredWidth()) / 2;
                  if (!isRtl) {
                      view.comLayout(left + leftFromCrossAxis + lp.mLayoutMarginLeft - lp.mLayoutMarginRight,
                              top, right + leftFromCrossAxis + lp.mLayoutMarginLeft - lp.mLayoutMarginRight,
                              bottom);
                  } else {
                      view.comLayout(left - leftFromCrossAxis + lp.mLayoutMarginLeft - lp.mLayoutMarginRight,
                              top, right - leftFromCrossAxis + lp.mLayoutMarginLeft - lp.mLayoutMarginRight,
                              bottom);
                  }
                  break;
          }
      }
  
      private void layoutHorizontal(boolean isRtl, int left, int top, int right, int bottom) {
          int paddingLeft = getComPaddingLeft();
          int paddingRight = getComPaddingRight();
          // Use float to reduce the round error that may happen in when justifyContent ==
          // SPACE_BETWEEN or SPACE_AROUND
          float childLeft;
          int currentViewIndex = 0;
  
          int height = bottom - top;
          int width = right - left;
          // childBottom is used if the mFlexWrap is FLEX_WRAP_WRAP_REVERSE otherwise
          // childTop is used to align the vertical position of the children views.
  //        int childBottom = height - getComPaddingBottom();
          int childBottom = bottom - getComPaddingBottom();
  //        int childTop = getComPaddingTop();
          int childTop = top + getComPaddingTop();
  
          // Used only for RTL layout
          // Use float to reduce the round error that may happen in when justifyContent ==
          // SPACE_BETWEEN or SPACE_AROUND
          float childRight;
          for (int i = 0, size = mFlexLines.size(); i < size; i++) {
              FlexLine flexLine = mFlexLines.get(i);
              if (hasDividerBeforeFlexLine(i)) {
                  childBottom -= mDividerHorizontalHeight;
                  childTop += mDividerHorizontalHeight;
              }
              float spaceBetweenItem = 0f;
              switch (mJustifyContent) {
                  case JUSTIFY_CONTENT_FLEX_START:
                      childLeft = left + paddingLeft;
                      childRight = right - paddingRight;
  //                    childLeft = paddingLeft;
  //                    childRight = width - paddingRight;
                      break;
                  case JUSTIFY_CONTENT_FLEX_END:
                      childLeft = left + width - flexLine.mMainSize + paddingRight;
                      childRight = left + flexLine.mMainSize - paddingLeft;
  //                    childLeft = width - flexLine.mMainSize + paddingRight;
  //                    childRight = flexLine.mMainSize - paddingLeft;
                      break;
                  case JUSTIFY_CONTENT_CENTER:
                      childLeft = left + paddingLeft + (width - flexLine.mMainSize) / 2f;
                      childRight = left + width - paddingRight - (width - flexLine.mMainSize) / 2f;
  
  //                    childLeft = paddingLeft + (width - flexLine.mMainSize) / 2f;
  //                    childRight = width - paddingRight - (width - flexLine.mMainSize) / 2f;
                      break;
                  case JUSTIFY_CONTENT_SPACE_AROUND:
                      if (flexLine.mItemCount != 0) {
                          spaceBetweenItem = (width - flexLine.mMainSize)
                                  / (float) flexLine.mItemCount;
                      }
                      childLeft = left + paddingLeft + spaceBetweenItem / 2f;
                      childRight = left + width - paddingRight - spaceBetweenItem / 2f;
  //                    childLeft = paddingLeft + spaceBetweenItem / 2f;
  //                    childRight = width - paddingRight - spaceBetweenItem / 2f;
                      break;
                  case JUSTIFY_CONTENT_SPACE_BETWEEN:
                      childLeft = left + paddingLeft;
  //                    childLeft = paddingLeft;
                      float denominator = flexLine.mItemCount != 1 ? flexLine.mItemCount - 1 : 1f;
                      spaceBetweenItem = (width - flexLine.mMainSize) / denominator;
  //                    childRight = width - paddingRight;
                      childRight = left + width - paddingRight;
                      break;
                  default:
                      throw new IllegalStateException(
                              "Invalid justifyContent is set: " + mJustifyContent);
              }
              spaceBetweenItem = Math.max(spaceBetweenItem, 0);
  
              for (int j = 0; j < flexLine.mItemCount; j++) {
                  ViewBase child = getReorderedChildAt(currentViewIndex);
                  if (child == null) {
                      continue;
  //                } else if (child.getVisibility() == View.GONE) {
  //                } else if (!child.isVisible()) {
                  } else if (child.getVisibility() == ViewBaseCommon.GONE) {
                      currentViewIndex++;
                      continue;
                  }
                  Params lp = ((Params) child.getComLayoutParams());
                  childLeft += lp.mLayoutMarginLeft;
                  childRight -= lp.mLayoutMarginRight;
                  if (hasDividerBeforeChildAtAlongMainAxis(currentViewIndex, j)) {
                      childLeft += mDividerVerticalWidth;
                      childRight -= mDividerVerticalWidth;
                  }
  
                  if (mFlexWrap == FLEX_WRAP_WRAP_REVERSE) {
                      if (isRtl) {
                          layoutSingleChildHorizontal(child, flexLine, mFlexWrap, mAlignItems,
                                  Math.round(childRight) - child.getComMeasuredWidth(),
                                  childBottom - child.getComMeasuredHeight(), Math.round(childRight),
                                  childBottom);
                      } else {
                          layoutSingleChildHorizontal(child, flexLine, mFlexWrap, mAlignItems,
                                  Math.round(childLeft), childBottom - child.getComMeasuredHeight(),
                                  Math.round(childLeft) + child.getComMeasuredWidth(),
                                  childBottom);
                      }
                  } else {
                      if (isRtl) {
                          layoutSingleChildHorizontal(child, flexLine, mFlexWrap, mAlignItems,
                                  Math.round(childRight) - child.getComMeasuredWidth(), childTop,
                                  Math.round(childRight), childTop + child.getComMeasuredHeight());
                      } else {
  //                        Log.d(TAG, "layout child id:" + child.getId());
                          layoutSingleChildHorizontal(child, flexLine, mFlexWrap, mAlignItems,
                                  Math.round(childLeft), childTop,
                                  Math.round(childLeft) + child.getComMeasuredWidth(),
                                  childTop + child.getComMeasuredHeight());
                      }
                  }
                  childLeft += child.getComMeasuredWidth() + spaceBetweenItem + lp.mLayoutMarginRight;
                  childRight -= child.getComMeasuredWidth() + spaceBetweenItem + lp.mLayoutMarginLeft;
                  currentViewIndex++;
  
  //                flexLine.mLeft = Math.min(flexLine.mLeft, child.getLeft() - lp.mLayoutMarginLeft);
  //                flexLine.mTop = Math.min(flexLine.mTop, child.getTop() - lp.mLayoutMarginTop);
  //                flexLine.mRight = Math.max(flexLine.mRight, child.getRight() + lp.mLayoutMarginRight);
  //                flexLine.mBottom = Math.max(flexLine.mBottom, child.getBottom() + lp.mLayoutMarginBottom);
              }
              childTop += flexLine.mCrossSize;
              childBottom -= flexLine.mCrossSize;
          }
      }
  
      private void layoutSingleChildHorizontal(ViewBase view, FlexLine flexLine, @FlexWrap int flexWrap,
                                               int alignItems, int left, int top, int right, int bottom) {
          Params lp = (Params) view.getComLayoutParams();
          if (lp.alignSelf != Params.ALIGN_SELF_AUTO) {
              // Expecting the values for alignItems and alignSelf match except for ALIGN_SELF_AUTO.
              // Assigning the alignSelf value as alignItems should work.
              alignItems = lp.alignSelf;
          }
          int crossSize = flexLine.mCrossSize;
          switch (alignItems) {
              case ALIGN_ITEMS_FLEX_START: // Intentional fall through
              case ALIGN_ITEMS_STRETCH:
                  if (flexWrap != FLEX_WRAP_WRAP_REVERSE) {
  //                    Log.d(TAG, "layoutSingleChildHorizontal  left:" + left + "  top:" + top + lp.mLayoutMarginTop + "  right:" + right + "  bottom:" + bottom + lp.mLayoutMarginTop) ;
                      view.comLayout(left, top + lp.mLayoutMarginTop, right, bottom + lp.mLayoutMarginTop);
                  } else {
                      view.comLayout(left, top - lp.mLayoutMarginBottom, right, bottom - lp.mLayoutMarginBottom);
                  }
                  break;
              case ALIGN_ITEMS_BASELINE:
                  if (flexWrap != FLEX_WRAP_WRAP_REVERSE) {
                      int marginTop = flexLine.mMaxBaseline - view.getComBaseline();
                      marginTop = Math.max(marginTop, lp.mLayoutMarginTop);
                      view.comLayout(left, top + marginTop, right, bottom + marginTop);
                  } else {
                      int marginBottom = flexLine.mMaxBaseline - view.getComMeasuredHeight() + view
                              .getComBaseline();
                      marginBottom = Math.max(marginBottom, lp.mLayoutMarginBottom);
                      view.comLayout(left, top - marginBottom, right, bottom - marginBottom);
                  }
                  break;
              case ALIGN_ITEMS_FLEX_END:
                  if (flexWrap != FLEX_WRAP_WRAP_REVERSE) {
                      view.comLayout(left,
                              top + crossSize - view.getComMeasuredHeight() - lp.mLayoutMarginBottom,
                              right, top + crossSize - lp.mLayoutMarginBottom);
                  } else {
                      // If the flexWrap == FLEX_WRAP_WRAP_REVERSE, the direction of the
                      // flexEnd is flipped (from top to bottom).
                      view.comLayout(left, top - crossSize + view.getComMeasuredHeight() + lp.mLayoutMarginTop,
                              right, bottom - crossSize + view.getComMeasuredHeight() + lp.mLayoutMarginTop);
                  }
                  break;
              case ALIGN_ITEMS_CENTER:
                  int topFromCrossAxis = (crossSize - view.getComMeasuredHeight()) / 2;
                  if (flexWrap != FLEX_WRAP_WRAP_REVERSE) {
                      view.comLayout(left, top + topFromCrossAxis + lp.mLayoutMarginTop - lp.mLayoutMarginBottom,
                              right, top + topFromCrossAxis + view.getComMeasuredHeight() + lp.mLayoutMarginTop
                                      - lp.mLayoutMarginBottom);
                  } else {
                      view.comLayout(left, top - topFromCrossAxis + lp.mLayoutMarginTop - lp.mLayoutMarginBottom,
                              right, top - topFromCrossAxis + view.getComMeasuredHeight() + lp.mLayoutMarginTop
                                      - lp.mLayoutMarginBottom);
                  }
                  break;
          }
      }
  
      @Override
      protected boolean setAttribute(int key, int value) {
          boolean ret = super.setAttribute(key, value);
  
          if (!ret) {
              ret = true;
              switch (key) {
                  case StringBase.STR_ID_flexDirection:
                      mFlexDirection = value;
                      break;
  
                  case StringBase.STR_ID_flexWrap:
                      mFlexWrap = value;
                      break;
  
                  case StringBase.STR_ID_justifyContent:
                      mJustifyContent = value;
                      break;
  
                  case StringBase.STR_ID_alignItems:
                      mAlignItems = value;
                      break;
  
                  case StringBase.STR_ID_alignContent:
                      mAlignContent = value;
                      break;
  
                  default:
                      ret = false;
                      break;
              }
          }
  
          return ret;
      }
  
      public static class Params extends Layout.Params {
  
          private static final int ORDER_DEFAULT = 1;
  
          private static final float FLEX_GROW_DEFAULT = 0f;
  
          private static final float FLEX_SHRINK_DEFAULT = 1f;
  
          public static final float FLEX_BASIS_PERCENT_DEFAULT = -1f;
  
          public static final int ALIGN_SELF_AUTO = -1;
  
          public static final int ALIGN_SELF_FLEX_START = ALIGN_ITEMS_FLEX_START;
  
          public static final int ALIGN_SELF_FLEX_END = ALIGN_ITEMS_FLEX_END;
  
          public static final int ALIGN_SELF_CENTER = ALIGN_ITEMS_CENTER;
  
          public static final int ALIGN_SELF_BASELINE = ALIGN_ITEMS_BASELINE;
  
          public static final int ALIGN_SELF_STRETCH = ALIGN_ITEMS_STRETCH;
  
          private static final int MAX_SIZE = Integer.MAX_VALUE & ViewCompat.MEASURED_SIZE_MASK;
  
          /**
           * This attribute can change the ordering of the children views are laid out.
           * By default, children are displayed and laid out in the same order as they appear in the
           * layout XML. If not specified, {@link #ORDER_DEFAULT} is set as a default value.
           */
          public int order = ORDER_DEFAULT;
  
          /**
           * This attribute determines how much this child will grow if positive free space is
           * distributed relative to the rest of other flex items included in the same flex line.
           * If not specified, {@link #FLEX_GROW_DEFAULT} is set as a default value.
           */
          public float flexGrow = FLEX_GROW_DEFAULT;
  
          /**
           * This attributes determines how much this child will shrink is negative free space is
           * distributed relative to the rest of other flex items included in the same flex line.
           * If not specified, {@link #FLEX_SHRINK_DEFAULT} is set as a default value.
           */
          public float flexShrink = FLEX_SHRINK_DEFAULT;
  
          /**
           * This attributes determines the alignment along the cross axis (perpendicular to the
           * main axis). The alignment in the same direction can be determined by the
           * {@link #mAlignItems} in the parent, but if this is set to other than
           * {@link #ALIGN_SELF_AUTO}, the cross axis alignment is overridden for this child.
           * The value needs to be one of the values in ({@link #ALIGN_SELF_AUTO},
           * {@link #ALIGN_SELF_STRETCH}, {@link #ALIGN_SELF_FLEX_START}, {@link
           * #ALIGN_SELF_FLEX_END}, {@link #ALIGN_SELF_CENTER}, or {@link #ALIGN_SELF_BASELINE}).
           * If not specified, {@link #ALIGN_SELF_AUTO} is set as a default value.
           */
          public int alignSelf = ALIGN_SELF_AUTO;
  
          /**
           * The initial flex item length in a fraction format relative to its parent.
           * The initial main size of this child View is trying to be expanded as the specified
           * fraction against the parent main size.
           * If this value is set, the length specified from layout_width
           * (or layout_height) is overridden by the calculated value from this attribute.
           * This attribute is only effective when the parent's MeasureSpec mode is
           * MeasureSpec.EXACTLY. The default value is {@link #FLEX_BASIS_PERCENT_DEFAULT}, which
           * means not set.
           */
          public float flexBasisPercent = FLEX_BASIS_PERCENT_DEFAULT;
  
          /**
           * This attribute determines the minimum width the child can shrink to.
           */
          public int minWidth;
  
          /**
           * This attribute determines the minimum height the child can shrink to.
           */
          public int minHeight;
  
          /**
           * This attribute determines the maximum width the child can expand to.
           */
          public int maxWidth = MAX_SIZE;
  
          /**
           * This attribute determines the maximum height the child can expand to.
           */
          public int maxHeight = MAX_SIZE;
  
          /**
           * This attribute forces a flex line wrapping. i.e. if this is set to {@code true} for a
           * flex item, the item will become the first item of the new flex line. (A wrapping happens
           * regardless of the flex items being processed in the the previous flex line)
           * This attribute is ignored if the flex_wrap attribute is set as nowrap.
           * The equivalent attribute isn't defined in the original CSS Flexible Box Module
           * specification, but having this attribute is useful for Android developers to flatten
           * the layouts when building a grid like layout or for a situation where developers want
           * to put a new flex line to make a semantic difference from the previous one, etc.
           */
          public boolean wrapBefore;
  
          public Params() {
              order = ORDER_DEFAULT;
              flexGrow = FLEX_GROW_DEFAULT;
              flexShrink = FLEX_SHRINK_DEFAULT;
              alignSelf = ALIGN_SELF_AUTO;
              flexBasisPercent = FLEX_BASIS_PERCENT_DEFAULT;
              minWidth = 0;
              minHeight = 0;
              maxWidth = MAX_SIZE;
              maxHeight = MAX_SIZE;
              wrapBefore = false;
          }
  
          public Params(Params source) {
              order = source.order;
              flexGrow = source.flexGrow;
              flexShrink = source.flexShrink;
              alignSelf = source.alignSelf;
              flexBasisPercent = source.flexBasisPercent;
              minWidth = source.minWidth;
              minHeight = source.minHeight;
              maxWidth = source.maxWidth;
              maxHeight = source.maxHeight;
              wrapBefore = source.wrapBefore;
          }
  
          @Override
          public boolean setAttribute(int key, int value) {
              boolean ret = super.setAttribute(key, value);
  
              if (!ret) {
                  ret = true;
                  switch (key) {
                      case StringBase.STR_ID_flexGrow:
  //                        Log.d(TAG, "flexgrow:" + value);
                          flexGrow = value;
                          break;
  
                      default:
                          ret = false;
                  }
              }
  
              return ret;
          }
      }
  
      public static class Builder implements ViewBase.IBuilder {
          @Override
          public ViewBase build(VafContext context, ViewCache viewCache) {
              return new FlexLayout(context, viewCache);
          }
      }
  
      private static class Order implements Comparable<Order> {
  
          /** {@link View}'s index */
          int index;
  
          /** order property in the Flexbox */
          int order;
  
          @Override
          public int compareTo(@NonNull Order another) {
              if (order != another.order) {
                  return order - another.order;
              }
              return index - another.index;
          }
  
          @Override
          public String toString() {
              return "Order{" +
                      "order=" + order +
                      ", index=" + index +
                      '}';
          }
      }
  
  }
  