/*
 * 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 +
                    '}';
        }
    }

}