import { FlexDirection, FlexWrap, JustifyContent, AlignItems, AlignContent, FlexboxLayoutBase, FlexBasisPercent, orderProperty, flexGrowProperty, flexShrinkProperty, flexWrapBeforeProperty, alignSelfProperty } from './flexbox-layout-common';
import { View } from '../../core/view';
import { layout } from '../../../utils';
export * from './flexbox-layout-common';
var EXACTLY = layout.EXACTLY;
var AT_MOST = layout.AT_MOST;
var UNSPECIFIED = layout.UNSPECIFIED;
var MEASURED_SIZE_MASK = layout.MEASURED_SIZE_MASK;
var MEASURED_STATE_TOO_SMALL = layout.MEASURED_STATE_TOO_SMALL;
function requestFlexboxLayout(value) {
    const flexbox = this.parent;
    if (flexbox instanceof FlexboxLayoutBase) {
        flexbox.requestLayout();
    }
}
View.prototype[orderProperty.setNative] = requestFlexboxLayout;
View.prototype[flexGrowProperty.setNative] = requestFlexboxLayout;
View.prototype[flexShrinkProperty.setNative] = requestFlexboxLayout;
View.prototype[flexWrapBeforeProperty.setNative] = requestFlexboxLayout;
View.prototype[alignSelfProperty.setNative] = requestFlexboxLayout;
const MATCH_PARENT = -1;
const WRAP_CONTENT = -2;
const View_sUseZeroUnspecifiedMeasureSpec = true; // NOTE: android version < M
// Long ints may not be safe in JavaScript
const MAX_SIZE = 0x00ffffff & MEASURED_SIZE_MASK;
var makeMeasureSpec = layout.makeMeasureSpec;
var getMeasureSpecMode = layout.getMeasureSpecMode;
var getMeasureSpecSize = layout.getMeasureSpecSize;
import { CoreTypes } from '../../enums';
// `eachLayoutChild` iterates over children, and we need more - indexed access.
// This class tries to accomodate that by collecting all children in an
// array no more than once per measure.
class MeasureContext {
    constructor(owner) {
        this.owner = owner;
        this.children = [];
        this.owner.eachLayoutChild((child) => {
            this.children.push(child);
        });
    }
    get childrenCount() {
        return this.children.length;
    }
    childAt(index) {
        return this.children[index];
    }
}
class FlexLine {
    constructor() {
        this._left = Number.MAX_VALUE;
        this._top = Number.MAX_VALUE;
        this._right = Number.MAX_VALUE;
        this._bottom = Number.MAX_VALUE;
        this._mainSize = 0;
        this._dividerLengthInMainSize = 0;
        this._crossSize = 0;
        this._itemCount = 0;
        this._goneItemCount = 0;
        this._totalFlexGrow = 0;
        this._totalFlexShrink = 0;
        this._maxBaseline = 0;
        this._indicesAlignSelfStretch = [];
    }
    get left() {
        return this._left;
    }
    get top() {
        return this._top;
    }
    get right() {
        return this._right;
    }
    get bottom() {
        return this._bottom;
    }
    get mainSize() {
        return this._mainSize;
    }
    get crossSize() {
        return this._crossSize;
    }
    get itemCount() {
        return this._itemCount;
    }
    get layoutVisibleItemCount() {
        return this._itemCount - this._goneItemCount;
    }
    get totalFlexGrow() {
        return this._totalFlexGrow;
    }
    get totalFlexShrink() {
        return this._totalFlexShrink;
    }
}
class Order {
    compareTo(another) {
        if (this.order !== another.order) {
            return this.order - another.order;
        }
        return this.index - another.index;
    }
}
export class FlexboxLayout extends FlexboxLayoutBase {
    constructor() {
        // Omit divider
        super(...arguments);
        this._flexLines = [];
    }
    onMeasure(widthMeasureSpec, heightMeasureSpec) {
        this.measureContext = new MeasureContext(this);
        // Omit: super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (this._isOrderChangedFromLastMeasurement) {
            this._reorderedIndices = this._createReorderedIndices();
        }
        if (!this._childrenFrozen || this._childrenFrozen.length < this.measureContext.childrenCount) {
            this._childrenFrozen = new Array(this.measureContext.childrenCount);
        }
        switch (this.flexDirection) {
            case FlexDirection.ROW:
            case FlexDirection.ROW_REVERSE:
                this._measureHorizontal(widthMeasureSpec, heightMeasureSpec);
                break;
            case FlexDirection.COLUMN:
            case FlexDirection.COLUMN_REVERSE:
                this._measureVertical(widthMeasureSpec, heightMeasureSpec);
                break;
            default:
                throw new Error('Invalid value for the flex direction is set: ' + this.flexDirection);
        }
        this._childrenFrozen.length = 0;
    }
    _getReorderedChildAt(index) {
        let child;
        if (index < 0 || index >= this._reorderedIndices.length) {
            child = null;
        }
        else {
            const reorderedIndex = this._reorderedIndices[index];
            child = this.measureContext.childAt(reorderedIndex);
        }
        return child;
    }
    _createReorderedIndices() {
        const childCount = this.measureContext.childrenCount;
        const orders = this._createOrders(childCount);
        return this._sortOrdersIntoReorderedIndices(childCount, orders);
    }
    _sortOrdersIntoReorderedIndices(childCount, orders) {
        orders.sort((a, b) => a.compareTo(b));
        if (!this._orderCache) {
            this._orderCache = [];
        }
        this._orderCache.length = 0;
        const reorderedIndices = [];
        orders.forEach((order, i) => {
            reorderedIndices[i] = order.index;
            this._orderCache[i] = order.order;
        });
        return reorderedIndices;
    }
    _createOrders(childCount) {
        const orders = [];
        for (let i = 0; i < childCount; i++) {
            const child = this.measureContext.childAt(i);
            const order = new Order();
            order.order = FlexboxLayout.getOrder(child);
            order.index = i;
            orders.push(order);
        }
        return orders;
    }
    get _isOrderChangedFromLastMeasurement() {
        const childCount = this.measureContext.childrenCount;
        if (!this._orderCache) {
            this._orderCache = [];
        }
        if (this._orderCache.length !== childCount) {
            return true;
        }
        for (let i = 0; i < childCount; i++) {
            const view = this.measureContext.childAt(i);
            if (view === null) {
                continue;
            }
            if (FlexboxLayout.getOrder(view) !== this._orderCache[i]) {
                return true;
            }
        }
        return false;
    }
    _measureHorizontal(widthMeasureSpec, heightMeasureSpec) {
        const widthSize = getMeasureSpecSize(widthMeasureSpec);
        const widthMode = getMeasureSpecMode(widthMeasureSpec);
        const heightSize = getMeasureSpecSize(heightMeasureSpec);
        const heightMode = getMeasureSpecMode(heightMeasureSpec);
        let childState = 0;
        this._flexLines.length = 0;
        (() => {
            const childCount = this.measureContext.childrenCount;
            const paddingStart = FlexboxLayout.getPaddingStart(this);
            const paddingEnd = FlexboxLayout.getPaddingEnd(this);
            let largestHeightInRow = Number.MIN_VALUE;
            let flexLine = new FlexLine();
            let indexInFlexLine = 0;
            flexLine._mainSize = paddingStart + paddingEnd;
            for (let i = 0; i < childCount; i++) {
                const child = this._getReorderedChildAt(i);
                if (child === null) {
                    this._addFlexLineIfLastFlexItem(i, childCount, flexLine);
                    continue;
                }
                else if (child.isCollapsed) {
                    flexLine._itemCount++;
                    flexLine._goneItemCount++;
                    this._addFlexLineIfLastFlexItem(i, childCount, flexLine);
                    continue;
                }
                child._updateEffectiveLayoutValues(widthSize, widthMode, heightSize, heightMode);
                const lp = child; // child.style;
                if (FlexboxLayout.getAlignSelf(child) === 'stretch') {
                    flexLine._indicesAlignSelfStretch.push(i);
                }
                let childWidth = lp.effectiveWidth;
                if (FlexBasisPercent.DEFAULT /*lp.flexBasisPercent*/ !== FlexBasisPercent.DEFAULT && widthMode === EXACTLY) {
                    childWidth = Math.round(widthSize * FlexBasisPercent.DEFAULT /*lp.flexBasisPercent*/);
                }
                const childWidthMeasureSpec = FlexboxLayout.getChildMeasureSpec(widthMeasureSpec, lp.effectivePaddingLeft + lp.effectivePaddingRight + lp.effectiveMarginLeft + lp.effectiveMarginRight, childWidth < 0 ? WRAP_CONTENT : childWidth);
                const childHeightMeasureSpec = FlexboxLayout.getChildMeasureSpec(heightMeasureSpec, lp.effectivePaddingTop + lp.effectivePaddingBottom + lp.effectiveMarginTop + lp.effectiveMarginBottom, lp.effectiveHeight < 0 ? WRAP_CONTENT : lp.effectiveHeight);
                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
                this._checkSizeConstraints(child);
                childState = View.combineMeasuredStates(childState, child.getMeasuredState());
                largestHeightInRow = Math.max(largestHeightInRow, child.getMeasuredHeight() + lp.effectiveMarginTop + lp.effectiveMarginBottom);
                if (this._isWrapRequired(child, widthMode, widthSize, flexLine._mainSize, child.getMeasuredWidth() + lp.effectiveMarginLeft + lp.effectiveMarginRight, i, indexInFlexLine)) {
                    if (flexLine.layoutVisibleItemCount > 0) {
                        this._addFlexLine(flexLine);
                    }
                    flexLine = new FlexLine();
                    flexLine._itemCount = 1;
                    flexLine._mainSize = paddingStart + paddingEnd;
                    largestHeightInRow = child.getMeasuredHeight() + lp.effectiveMarginTop + lp.effectiveMarginBottom;
                    indexInFlexLine = 0;
                }
                else {
                    flexLine._itemCount++;
                    indexInFlexLine++;
                }
                flexLine._mainSize += child.getMeasuredWidth() + lp.effectiveMarginLeft + lp.effectiveMarginRight;
                flexLine._totalFlexGrow += FlexboxLayout.getFlexGrow(child);
                flexLine._totalFlexShrink += FlexboxLayout.getFlexShrink(child);
                flexLine._crossSize = Math.max(flexLine._crossSize, largestHeightInRow);
                // Omit divider
                if (this.flexWrap !== FlexWrap.WRAP_REVERSE) {
                    flexLine._maxBaseline = Math.max(flexLine._maxBaseline, FlexboxLayout.getBaseline(child) + lp.effectiveMarginTop);
                }
                else {
                    flexLine._maxBaseline = Math.max(flexLine._maxBaseline, child.getMeasuredHeight() - FlexboxLayout.getBaseline(child) + lp.effectiveMarginBottom);
                }
                this._addFlexLineIfLastFlexItem(i, childCount, flexLine);
            }
        })();
        this._determineMainSize(this.flexDirection, widthMeasureSpec, heightMeasureSpec);
        if (this.alignItems === AlignItems.BASELINE) {
            let viewIndex = 0;
            this._flexLines.forEach((flexLine) => {
                let largestHeightInLine = Number.MIN_VALUE;
                for (let i = viewIndex; i < viewIndex + flexLine._itemCount; i++) {
                    const child = this._getReorderedChildAt(i);
                    const lp = child; // .style;
                    if (this.flexWrap !== FlexWrap.WRAP_REVERSE) {
                        let marginTop = flexLine._maxBaseline - FlexboxLayout.getBaseline(child);
                        marginTop = Math.max(marginTop, lp.effectiveMarginTop);
                        largestHeightInLine = Math.max(largestHeightInLine, child.getMeasuredHeight() + marginTop + lp.effectiveMarginBottom);
                    }
                    else {
                        let marginBottom = flexLine._maxBaseline - child.getMeasuredHeight() + FlexboxLayout.getBaseline(child);
                        marginBottom = Math.max(marginBottom, lp.effectiveMarginBottom);
                        largestHeightInLine = Math.max(largestHeightInLine, child.getMeasuredHeight() + lp.effectiveMarginTop + marginBottom);
                    }
                }
                flexLine._crossSize = largestHeightInLine;
                viewIndex += flexLine.itemCount;
            });
        }
        this._determineCrossSize(this.flexDirection, widthMeasureSpec, heightMeasureSpec, this.effectivePaddingTop + this.effectivePaddingBottom);
        this._stretchViews(this.flexDirection, this.alignItems);
        this._setMeasuredDimensionForFlex(this.flexDirection, widthMeasureSpec, heightMeasureSpec, childState);
    }
    _measureVertical(widthMeasureSpec, heightMeasureSpec) {
        const widthSize = getMeasureSpecSize(widthMeasureSpec);
        const widthMode = getMeasureSpecMode(widthMeasureSpec);
        const heightSize = getMeasureSpecSize(heightMeasureSpec);
        const heightMode = getMeasureSpecMode(heightMeasureSpec);
        let childState = 0;
        this._flexLines.length = 0;
        const childCount = this.measureContext.childrenCount;
        const paddingTop = this.effectivePaddingTop;
        const paddingBottom = this.effectivePaddingBottom;
        let largestWidthInColumn = Number.MIN_VALUE;
        let flexLine = new FlexLine();
        flexLine._mainSize = paddingTop + paddingBottom;
        let indexInFlexLine = 0;
        for (let i = 0; i < childCount; i++) {
            const child = this._getReorderedChildAt(i);
            if (child === null) {
                this._addFlexLineIfLastFlexItem(i, childCount, flexLine);
                continue;
            }
            else if (child.isCollapsed) {
                flexLine._itemCount++;
                flexLine._goneItemCount++;
                this._addFlexLineIfLastFlexItem(i, childCount, flexLine);
                continue;
            }
            child._updateEffectiveLayoutValues(widthSize, widthMode, heightSize, heightMode);
            const lp = child; // .style;
            if (FlexboxLayout.getAlignSelf(child) === 'stretch') {
                flexLine._indicesAlignSelfStretch.push(i);
            }
            let childHeight = lp.effectiveHeight;
            // TODO: This should always be false
            if (FlexBasisPercent.DEFAULT /* lp.flexBasisPercent */ !== FlexBasisPercent.DEFAULT && heightMode === EXACTLY) {
                childHeight = Math.round(heightSize * FlexBasisPercent.DEFAULT /* lp.flexBasisPercent */);
            }
            const childWidthMeasureSpec = FlexboxLayout.getChildMeasureSpec(widthMeasureSpec, this.effectivePaddingLeft + this.effectivePaddingRight + lp.effectiveMarginLeft + lp.effectiveMarginRight, lp.effectiveWidth < 0 ? WRAP_CONTENT : lp.effectiveWidth);
            const childHeightMeasureSpec = FlexboxLayout.getChildMeasureSpec(heightMeasureSpec, this.effectivePaddingTop + this.effectivePaddingBottom + lp.effectiveMarginTop + lp.effectiveMarginBottom, childHeight < 0 ? WRAP_CONTENT : childHeight);
            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
            this._checkSizeConstraints(child);
            childState = View.combineMeasuredStates(childState, child.getMeasuredState());
            largestWidthInColumn = Math.max(largestWidthInColumn, child.getMeasuredWidth() + lp.effectiveMarginLeft + lp.effectiveMarginRight);
            if (this._isWrapRequired(child, heightMode, heightSize, flexLine.mainSize, child.getMeasuredHeight() + lp.effectiveMarginTop + lp.effectiveMarginBottom, i, indexInFlexLine)) {
                if (flexLine.layoutVisibleItemCount > 0) {
                    this._addFlexLine(flexLine);
                }
                flexLine = new FlexLine();
                flexLine._itemCount = 1;
                flexLine._mainSize = paddingTop + paddingBottom;
                largestWidthInColumn = child.getMeasuredWidth() + lp.effectiveMarginLeft + lp.effectiveMarginRight;
                indexInFlexLine = 0;
            }
            else {
                flexLine._itemCount++;
                indexInFlexLine++;
            }
            flexLine._mainSize += child.getMeasuredHeight() + lp.effectiveMarginTop + lp.effectiveMarginBottom;
            flexLine._totalFlexGrow += FlexboxLayout.getFlexGrow(child);
            flexLine._totalFlexShrink += FlexboxLayout.getFlexShrink(child);
            flexLine._crossSize = Math.max(flexLine._crossSize, largestWidthInColumn);
            // Omit divider
            this._addFlexLineIfLastFlexItem(i, childCount, flexLine);
        }
        this._determineMainSize(this.flexDirection, widthMeasureSpec, heightMeasureSpec);
        this._determineCrossSize(this.flexDirection, widthMeasureSpec, heightMeasureSpec, this.effectivePaddingLeft + this.effectivePaddingRight);
        this._stretchViews(this.flexDirection, this.alignItems);
        this._setMeasuredDimensionForFlex(this.flexDirection, widthMeasureSpec, heightMeasureSpec, childState);
    }
    _checkSizeConstraints(view) {
        let needsMeasure = false;
        let childWidth = view.getMeasuredWidth();
        let childHeight = view.getMeasuredHeight();
        const minWidth = view.effectiveMinWidth;
        view.effectiveMinWidth = 0;
        if (view.getMeasuredWidth() < minWidth) {
            needsMeasure = true;
            childWidth = minWidth;
        }
        else if (view.getMeasuredWidth() > MAX_SIZE /*lp.maxWidth*/) {
            needsMeasure = true;
            childWidth = MAX_SIZE /*lp.maxWidth*/;
        }
        const minHeight = view.effectiveMinHeight;
        view.effectiveMinHeight = 0;
        if (childHeight < minHeight) {
            needsMeasure = true;
            childHeight = minHeight;
        }
        else if (childHeight > MAX_SIZE /*lp.maxWidth*/) {
            needsMeasure = true;
            childHeight = MAX_SIZE /*lp.maxWidth*/;
        }
        if (needsMeasure) {
            view.measure(makeMeasureSpec(childWidth, EXACTLY), makeMeasureSpec(childHeight, EXACTLY));
        }
        view.effectiveMinWidth = minWidth;
        view.effectiveMinHeight = minHeight;
    }
    _addFlexLineIfLastFlexItem(childIndex, childCount, flexLine) {
        if (childIndex === childCount - 1 && flexLine.layoutVisibleItemCount !== 0) {
            this._addFlexLine(flexLine);
        }
    }
    _addFlexLine(flexLine) {
        // Omit divider
        this._flexLines.push(flexLine);
    }
    _determineMainSize(flexDirection, widthMeasureSpec, heightMeasureSpec) {
        let mainSize;
        let paddingAlongMainAxis;
        switch (flexDirection) {
            case FlexDirection.ROW:
            case FlexDirection.ROW_REVERSE: {
                const widthMode = getMeasureSpecMode(widthMeasureSpec);
                const widthSize = getMeasureSpecSize(widthMeasureSpec);
                if (widthMode === EXACTLY) {
                    mainSize = widthSize;
                }
                else {
                    mainSize = this._getLargestMainSize();
                }
                paddingAlongMainAxis = this.effectivePaddingLeft + this.effectivePaddingRight;
                break;
            }
            case FlexDirection.COLUMN:
            case FlexDirection.COLUMN_REVERSE: {
                const heightMode = getMeasureSpecMode(heightMeasureSpec);
                const heightSize = getMeasureSpecSize(heightMeasureSpec);
                if (heightMode === EXACTLY) {
                    mainSize = heightSize;
                }
                else {
                    mainSize = this._getLargestMainSize();
                }
                paddingAlongMainAxis = this.effectivePaddingTop + this.effectivePaddingBottom;
                break;
            }
            default:
                throw new Error('Invalid flex direction: ' + flexDirection);
        }
        let childIndex = 0;
        this._flexLines.forEach((flexLine) => {
            if (flexLine.mainSize < mainSize) {
                childIndex = this._expandFlexItems(flexLine, flexDirection, mainSize, paddingAlongMainAxis, childIndex);
            }
            else {
                childIndex = this._shrinkFlexItems(flexLine, flexDirection, mainSize, paddingAlongMainAxis, childIndex);
            }
        });
    }
    _expandFlexItems(flexLine, flexDirection, maxMainSize, paddingAlongMainAxis, startIndex) {
        let childIndex = startIndex;
        if (flexLine._totalFlexGrow <= 0 || maxMainSize < flexLine._mainSize) {
            childIndex += flexLine._itemCount;
            return childIndex;
        }
        const sizeBeforeExpand = flexLine._mainSize;
        let needsReexpand = false;
        const pendingSpace = maxMainSize - flexLine._mainSize;
        const unitSpace = pendingSpace / flexLine._totalFlexGrow;
        flexLine._mainSize = paddingAlongMainAxis + flexLine._dividerLengthInMainSize;
        let accumulatedRoundError = 0;
        for (let i = 0; i < flexLine.itemCount; i++) {
            const child = this._getReorderedChildAt(childIndex);
            if (child === null) {
                continue;
            }
            else if (child.isCollapsed) {
                childIndex++;
                continue;
            }
            const lp = child; // .style;
            if (this._isMainAxisDirectionHorizontal(flexDirection)) {
                if (!this._childrenFrozen[childIndex]) {
                    const flexGrow = FlexboxLayout.getFlexGrow(child);
                    const rawCalculatedWidth = child.getMeasuredWidth() + unitSpace * flexGrow + accumulatedRoundError;
                    let roundedCalculatedWidth = Math.round(rawCalculatedWidth);
                    // TODO: MAX_SIZE is so big, this is always false:
                    if (roundedCalculatedWidth > MAX_SIZE /* lp.maxWidth */) {
                        needsReexpand = true;
                        roundedCalculatedWidth = MAX_SIZE /* lp.maxWidth */;
                        this._childrenFrozen[childIndex] = true;
                        flexLine._totalFlexGrow -= flexGrow;
                    }
                    else {
                        accumulatedRoundError = rawCalculatedWidth - roundedCalculatedWidth;
                    }
                    child.measure(makeMeasureSpec(roundedCalculatedWidth, EXACTLY), makeMeasureSpec(child.getMeasuredHeight(), EXACTLY));
                }
                flexLine._mainSize += child.getMeasuredWidth() + lp.effectiveMarginLeft + lp.effectiveMarginRight;
            }
            else {
                if (!this._childrenFrozen[childIndex]) {
                    const flexGrow = FlexboxLayout.getFlexGrow(child);
                    const rawCalculatedHeight = child.getMeasuredHeight() + unitSpace * flexGrow + accumulatedRoundError;
                    let roundedCalculatedHeight = Math.round(rawCalculatedHeight);
                    // TODO: MAX_SIZE is so big this is always false:
                    if (roundedCalculatedHeight > MAX_SIZE /*lp.maxHeight*/) {
                        needsReexpand = true;
                        roundedCalculatedHeight = MAX_SIZE /*lp.maxHeight*/;
                        this._childrenFrozen[childIndex] = true;
                        flexLine._totalFlexGrow -= flexGrow;
                    }
                    else {
                        accumulatedRoundError = rawCalculatedHeight - roundedCalculatedHeight;
                    }
                    child.measure(makeMeasureSpec(child.getMeasuredWidth(), EXACTLY), makeMeasureSpec(roundedCalculatedHeight, EXACTLY));
                }
                flexLine._mainSize += child.getMeasuredHeight() + lp.effectiveMarginTop + lp.effectiveMarginBottom;
            }
            childIndex++;
        }
        if (needsReexpand && sizeBeforeExpand !== flexLine._mainSize) {
            this._expandFlexItems(flexLine, flexDirection, maxMainSize, paddingAlongMainAxis, startIndex);
        }
        return childIndex;
    }
    _shrinkFlexItems(flexLine, flexDirection, maxMainSize, paddingAlongMainAxis, startIndex) {
        let childIndex = startIndex;
        const sizeBeforeShrink = flexLine._mainSize;
        if (flexLine._totalFlexShrink <= 0 || maxMainSize > flexLine._mainSize) {
            childIndex += flexLine.itemCount;
            return childIndex;
        }
        let needsReshrink = false;
        const unitShrink = (flexLine._mainSize - maxMainSize) / flexLine._totalFlexShrink;
        let accumulatedRoundError = 0;
        flexLine._mainSize = paddingAlongMainAxis + flexLine._dividerLengthInMainSize;
        for (let i = 0; i < flexLine.itemCount; i++) {
            const child = this._getReorderedChildAt(childIndex);
            if (child === null) {
                continue;
            }
            else if (child.isCollapsed) {
                childIndex++;
                continue;
            }
            const lp = child; // .style;
            if (this._isMainAxisDirectionHorizontal(flexDirection)) {
                // The direction of main axis is horizontal
                if (!this._childrenFrozen[childIndex]) {
                    const flexShrink = FlexboxLayout.getFlexShrink(child);
                    const rawCalculatedWidth = child.getMeasuredWidth() - unitShrink * flexShrink + accumulatedRoundError;
                    let roundedCalculatedWidth = Math.round(rawCalculatedWidth);
                    const minWidth = child.effectiveMinWidth;
                    child.effectiveMinWidth = 0;
                    if (roundedCalculatedWidth < minWidth) {
                        needsReshrink = true;
                        roundedCalculatedWidth = minWidth;
                        this._childrenFrozen[childIndex] = true;
                        flexLine._totalFlexShrink -= flexShrink;
                    }
                    else {
                        accumulatedRoundError = rawCalculatedWidth - roundedCalculatedWidth;
                    }
                    const childWidthMeasureSpec = makeMeasureSpec(roundedCalculatedWidth, EXACTLY);
                    // NOTE: for controls that support internal content wrapping (e.g. UILabel) reducing the width
                    // might result in increased height e.g. text that could be shown on one line for larger
                    // width needs to be wrapped in two when width is reduced.
                    // As a result we cannot unconditionally measure with EXACTLY the current measured height
                    const childHeightMeasureSpec = FlexboxLayout.getChildMeasureSpec(this._currentHeightMeasureSpec, lp.effectivePaddingTop + lp.effectivePaddingBottom + lp.effectiveMarginTop + lp.effectiveMarginBottom, lp.effectiveHeight < 0 ? WRAP_CONTENT : lp.effectiveHeight);
                    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
                    child.effectiveMinWidth = minWidth;
                    // make sure crossSize is up-to-date as child calculated height might have increased
                    flexLine._crossSize = Math.max(flexLine._crossSize, child.getMeasuredHeight() + lp.effectiveMarginTop + lp.effectiveMarginBottom);
                }
                flexLine._mainSize += child.getMeasuredWidth() + lp.effectiveMarginLeft + lp.effectiveMarginRight;
            }
            else {
                if (!this._childrenFrozen[childIndex]) {
                    const flexShrink = FlexboxLayout.getFlexShrink(child);
                    const rawCalculatedHeight = child.getMeasuredHeight() - unitShrink * flexShrink + accumulatedRoundError;
                    let roundedCalculatedHeight = Math.round(rawCalculatedHeight);
                    const minHeight = child.effectiveMinHeight;
                    child.effectiveMinHeight = 0;
                    if (roundedCalculatedHeight < minHeight) {
                        needsReshrink = true;
                        roundedCalculatedHeight = minHeight;
                        this._childrenFrozen[childIndex] = true;
                        flexLine._totalFlexShrink -= flexShrink;
                    }
                    else {
                        accumulatedRoundError = rawCalculatedHeight - roundedCalculatedHeight;
                    }
                    child.measure(makeMeasureSpec(child.getMeasuredWidth(), EXACTLY), makeMeasureSpec(roundedCalculatedHeight, EXACTLY));
                    child.effectiveMinHeight = minHeight;
                }
                flexLine._mainSize += child.getMeasuredHeight() + lp.effectiveMarginTop + lp.effectiveMarginBottom;
            }
            childIndex++;
        }
        if (needsReshrink && sizeBeforeShrink !== flexLine._mainSize) {
            this._shrinkFlexItems(flexLine, flexDirection, maxMainSize, paddingAlongMainAxis, startIndex);
        }
        return childIndex;
    }
    _determineCrossSize(flexDirection, widthMeasureSpec, heightMeasureSpec, paddingAlongCrossAxis) {
        let mode;
        let size;
        switch (flexDirection) {
            case FlexDirection.ROW:
            case FlexDirection.ROW_REVERSE:
                mode = getMeasureSpecMode(heightMeasureSpec);
                size = getMeasureSpecSize(heightMeasureSpec);
                break;
            case FlexDirection.COLUMN:
            case FlexDirection.COLUMN_REVERSE:
                mode = getMeasureSpecMode(widthMeasureSpec);
                size = getMeasureSpecSize(widthMeasureSpec);
                break;
            default:
                throw new Error('Invalid flex direction: ' + flexDirection);
        }
        if (mode === EXACTLY) {
            const totalCrossSize = this._getSumOfCrossSize() + paddingAlongCrossAxis;
            if (this._flexLines.length === 1) {
                this._flexLines[0]._crossSize = size - paddingAlongCrossAxis;
            }
            else if (this._flexLines.length >= 2 && totalCrossSize < size) {
                switch (this.alignContent) {
                    case AlignContent.STRETCH:
                        (() => {
                            const freeSpaceUnit = (size - totalCrossSize) / this._flexLines.length;
                            let accumulatedError = 0;
                            for (let i = 0, flexLinesSize = this._flexLines.length; i < flexLinesSize; i++) {
                                const flexLine = this._flexLines[i];
                                let newCrossSizeAsFloat = flexLine._crossSize + freeSpaceUnit;
                                if (i === this._flexLines.length - 1) {
                                    newCrossSizeAsFloat += accumulatedError;
                                    accumulatedError = 0;
                                }
                                let newCrossSize = Math.round(newCrossSizeAsFloat);
                                accumulatedError += newCrossSizeAsFloat - newCrossSize;
                                if (accumulatedError > 1) {
                                    newCrossSize += 1;
                                    accumulatedError -= 1;
                                }
                                else if (accumulatedError < -1) {
                                    newCrossSize -= 1;
                                    accumulatedError += 1;
                                }
                                flexLine._crossSize = newCrossSize;
                            }
                        })();
                        break;
                    case AlignContent.SPACE_AROUND:
                        (() => {
                            let spaceTopAndBottom = size - totalCrossSize;
                            const numberOfSpaces = this._flexLines.length * 2;
                            spaceTopAndBottom = spaceTopAndBottom / numberOfSpaces;
                            const newFlexLines = [];
                            const dummySpaceFlexLine = new FlexLine();
                            dummySpaceFlexLine._crossSize = spaceTopAndBottom;
                            this._flexLines.forEach((flexLine) => {
                                newFlexLines.push(dummySpaceFlexLine);
                                newFlexLines.push(flexLine);
                                newFlexLines.push(dummySpaceFlexLine);
                            });
                            this._flexLines = newFlexLines;
                        })();
                        break;
                    case AlignContent.SPACE_BETWEEN:
                        (() => {
                            let spaceBetweenFlexLine = size - totalCrossSize;
                            const numberOfSpaces = this._flexLines.length - 1;
                            spaceBetweenFlexLine = spaceBetweenFlexLine / numberOfSpaces;
                            let accumulatedError = 0;
                            const newFlexLines = [];
                            for (let i = 0, flexLineSize = this._flexLines.length; i < flexLineSize; i++) {
                                const flexLine = this._flexLines[i];
                                newFlexLines.push(flexLine);
                                if (i !== this._flexLines.length - 1) {
                                    const dummySpaceFlexLine = new FlexLine();
                                    if (i === this._flexLines.length - 2) {
                                        dummySpaceFlexLine._crossSize = Math.round(spaceBetweenFlexLine + accumulatedError);
                                        accumulatedError = 0;
                                    }
                                    else {
                                        dummySpaceFlexLine._crossSize = Math.round(spaceBetweenFlexLine);
                                    }
                                    accumulatedError += spaceBetweenFlexLine - dummySpaceFlexLine._crossSize;
                                    if (accumulatedError > 1) {
                                        dummySpaceFlexLine._crossSize += 1;
                                        accumulatedError -= 1;
                                    }
                                    else if (accumulatedError < -1) {
                                        dummySpaceFlexLine._crossSize -= 1;
                                        accumulatedError += 1;
                                    }
                                    newFlexLines.push(dummySpaceFlexLine);
                                }
                            }
                            this._flexLines = newFlexLines;
                        })();
                        break;
                    case AlignContent.CENTER: {
                        let spaceAboveAndBottom = size - totalCrossSize;
                        spaceAboveAndBottom = spaceAboveAndBottom / 2;
                        const newFlexLines = [];
                        const dummySpaceFlexLine = new FlexLine();
                        dummySpaceFlexLine._crossSize = spaceAboveAndBottom;
                        for (let i = 0, flexLineSize = this._flexLines.length; i < flexLineSize; i++) {
                            if (i === 0) {
                                newFlexLines.push(dummySpaceFlexLine);
                            }
                            const flexLine = this._flexLines[i];
                            newFlexLines.push(flexLine);
                            if (i === this._flexLines.length - 1) {
                                newFlexLines.push(dummySpaceFlexLine);
                            }
                        }
                        this._flexLines = newFlexLines;
                        break;
                    }
                    case AlignContent.FLEX_END: {
                        const spaceTop = size - totalCrossSize;
                        const dummySpaceFlexLine = new FlexLine();
                        dummySpaceFlexLine._crossSize = spaceTop;
                        this._flexLines.unshift(dummySpaceFlexLine);
                        break;
                    }
                }
            }
        }
    }
    _stretchViews(flexDirection, alignItems) {
        if (alignItems === AlignItems.STRETCH) {
            let viewIndex = 0;
            this._flexLines.forEach((flexLine) => {
                for (let i = 0; i < flexLine.itemCount; i++, viewIndex++) {
                    const view = this._getReorderedChildAt(viewIndex);
                    const alignSelf = FlexboxLayout.getAlignSelf(view);
                    if (alignSelf !== 'auto' && alignSelf !== 'stretch') {
                        continue;
                    }
                    switch (flexDirection) {
                        case FlexDirection.ROW:
                        case FlexDirection.ROW_REVERSE:
                            this._stretchViewVertically(view, flexLine._crossSize);
                            break;
                        case FlexDirection.COLUMN:
                        case FlexDirection.COLUMN_REVERSE:
                            this._stretchViewHorizontally(view, flexLine._crossSize);
                            break;
                        default:
                            throw new Error('Invalid flex direction: ' + flexDirection);
                    }
                }
            });
        }
        else {
            this._flexLines.forEach((flexLine) => {
                flexLine._indicesAlignSelfStretch.forEach((index) => {
                    const view = this._getReorderedChildAt(index);
                    switch (flexDirection) {
                        case FlexDirection.ROW:
                        case FlexDirection.ROW_REVERSE:
                            this._stretchViewVertically(view, flexLine._crossSize);
                            break;
                        case FlexDirection.COLUMN:
                        case FlexDirection.COLUMN_REVERSE:
                            this._stretchViewHorizontally(view, flexLine._crossSize);
                            break;
                        default:
                            throw new Error('Invalid flex direction: ' + flexDirection);
                    }
                });
            });
        }
    }
    _stretchViewVertically(view, crossSize) {
        let newHeight = crossSize - view.effectiveMarginTop - view.effectiveMarginBottom;
        newHeight = Math.max(newHeight, 0);
        const originalMeasuredWidth = view.getMeasuredWidth();
        let childWidthMeasureSpec = FlexboxLayout.getChildMeasureSpec(this._currentWidthMeasureSpec, view.effectivePaddingLeft + view.effectivePaddingRight + view.effectiveMarginLeft + view.effectiveMarginRight, view.effectiveWidth < 0 ? WRAP_CONTENT : Math.min(view.effectiveWidth, originalMeasuredWidth));
        view.measure(childWidthMeasureSpec, makeMeasureSpec(newHeight, EXACTLY));
        if (originalMeasuredWidth > view.getMeasuredWidth()) {
            childWidthMeasureSpec = makeMeasureSpec(originalMeasuredWidth, EXACTLY);
            view.measure(childWidthMeasureSpec, makeMeasureSpec(newHeight, EXACTLY));
        }
    }
    _stretchViewHorizontally(view, crossSize) {
        let newWidth = crossSize - view.effectiveMarginLeft - view.effectiveMarginRight;
        newWidth = Math.max(newWidth, 0);
        view.measure(makeMeasureSpec(newWidth, EXACTLY), makeMeasureSpec(view.getMeasuredHeight(), EXACTLY));
    }
    _setMeasuredDimensionForFlex(flexDirection, widthMeasureSpec, heightMeasureSpec, childState) {
        const widthMode = getMeasureSpecMode(widthMeasureSpec);
        let widthSize = getMeasureSpecSize(widthMeasureSpec);
        const heightMode = getMeasureSpecMode(heightMeasureSpec);
        let heightSize = getMeasureSpecSize(heightMeasureSpec);
        let calculatedMaxHeight;
        let calculatedMaxWidth;
        switch (flexDirection) {
            case FlexDirection.ROW:
            case FlexDirection.ROW_REVERSE:
                calculatedMaxHeight = this._getSumOfCrossSize() + this.effectivePaddingTop + this.effectivePaddingBottom;
                calculatedMaxWidth = this._getLargestMainSize();
                break;
            case FlexDirection.COLUMN:
            case FlexDirection.COLUMN_REVERSE:
                calculatedMaxHeight = this._getLargestMainSize();
                calculatedMaxWidth = this._getSumOfCrossSize() + this.effectivePaddingLeft + this.effectivePaddingRight;
                break;
            default:
                throw new Error('Invalid flex direction: ' + flexDirection);
        }
        let widthSizeAndState;
        switch (widthMode) {
            case EXACTLY:
                if (widthSize < calculatedMaxWidth) {
                    childState = View.combineMeasuredStates(childState, MEASURED_STATE_TOO_SMALL);
                }
                widthSizeAndState = View.resolveSizeAndState(widthSize, widthSize, widthMode, childState);
                break;
            case AT_MOST: {
                if (widthSize < calculatedMaxWidth) {
                    childState = View.combineMeasuredStates(childState, MEASURED_STATE_TOO_SMALL);
                }
                else {
                    widthSize = calculatedMaxWidth;
                }
                widthSizeAndState = View.resolveSizeAndState(widthSize, widthSize, widthMode, childState);
                break;
            }
            case UNSPECIFIED: {
                widthSizeAndState = View.resolveSizeAndState(calculatedMaxWidth, widthSize, widthMode, childState);
                break;
            }
            default:
                throw new Error('Unknown width mode is set: ' + widthMode);
        }
        let heightSizeAndState;
        switch (heightMode) {
            case EXACTLY:
                if (heightSize < calculatedMaxHeight) {
                    childState = View.combineMeasuredStates(childState, MEASURED_STATE_TOO_SMALL >> layout.MEASURED_HEIGHT_STATE_SHIFT);
                }
                heightSizeAndState = View.resolveSizeAndState(heightSize, heightSize, heightMode, childState);
                break;
            case AT_MOST: {
                if (heightSize < calculatedMaxHeight) {
                    childState = View.combineMeasuredStates(childState, MEASURED_STATE_TOO_SMALL >> layout.MEASURED_HEIGHT_STATE_SHIFT);
                }
                else {
                    heightSize = calculatedMaxHeight;
                }
                heightSizeAndState = View.resolveSizeAndState(heightSize, heightSize, heightMode, childState);
                break;
            }
            case UNSPECIFIED: {
                heightSizeAndState = View.resolveSizeAndState(calculatedMaxHeight, heightSize, heightMode, childState);
                break;
            }
            default:
                throw new Error('Unknown height mode is set: ' + heightMode);
        }
        this.setMeasuredDimension(widthSizeAndState, heightSizeAndState);
    }
    _isWrapRequired(child, mode, maxSize, currentLength, childLength, childAbsoluteIndex, childRelativeIndexInFlexLine) {
        if (this.flexWrap === FlexWrap.NOWRAP) {
            return false;
        }
        if (FlexboxLayout.getFlexWrapBefore(child)) {
            return true;
        }
        if (mode === UNSPECIFIED) {
            return false;
        }
        // Omit divider
        return maxSize < currentLength + childLength;
    }
    _getLargestMainSize() {
        return this._flexLines.reduce((max, flexLine) => Math.max(max, flexLine.mainSize), Number.MIN_VALUE);
    }
    _getSumOfCrossSize() {
        // Omit divider
        return this._flexLines.reduce((sum, flexLine) => sum + flexLine._crossSize, 0);
    }
    _isMainAxisDirectionHorizontal(flexDirection) {
        return flexDirection === FlexDirection.ROW || flexDirection === FlexDirection.ROW_REVERSE;
    }
    onLayout(left, top, right, bottom) {
        const insets = this.getSafeAreaInsets();
        let isRtl = this.direction === CoreTypes.LayoutDirection.rtl;
        switch (this.flexDirection) {
            case FlexDirection.ROW:
                this._layoutHorizontal(isRtl, left, top, right, bottom, insets);
                break;
            case FlexDirection.ROW_REVERSE:
                this._layoutHorizontal(!isRtl, left, top, right, bottom, insets);
                break;
            case FlexDirection.COLUMN:
                if (this.flexWrap === FlexWrap.WRAP_REVERSE) {
                    isRtl = !isRtl;
                }
                this._layoutVertical(isRtl, false, left, top, right, bottom, insets);
                break;
            case FlexDirection.COLUMN_REVERSE:
                if (this.flexWrap === FlexWrap.WRAP_REVERSE) {
                    isRtl = !isRtl;
                }
                this._layoutVertical(isRtl, true, left, top, right, bottom, insets);
                break;
            default:
                throw new Error('Invalid flex direction is set: ' + this.flexDirection);
        }
    }
    _layoutHorizontal(isRtl, left, top, right, bottom, insets) {
        // include insets
        const paddingLeft = this.effectivePaddingLeft + insets.left;
        const paddingTop = this.effectivePaddingTop + insets.top;
        const paddingRight = this.effectivePaddingRight + insets.right;
        const paddingBottom = this.effectivePaddingBottom + insets.bottom;
        let childLeft;
        let currentViewIndex = 0;
        const height = bottom - top;
        const width = right - left;
        // include insets
        let childBottom = height - paddingBottom;
        let childTop = paddingTop;
        let childRight;
        this._flexLines.forEach((flexLine, i) => {
            // Omit divider
            let spaceBetweenItem = 0.0;
            switch (this.justifyContent) {
                case JustifyContent.FLEX_START:
                    childLeft = paddingLeft;
                    childRight = width - paddingRight;
                    break;
                case JustifyContent.FLEX_END:
                    childLeft = width - flexLine._mainSize + paddingRight;
                    childRight = flexLine._mainSize - paddingLeft;
                    break;
                case JustifyContent.CENTER:
                    childLeft = paddingLeft + (width - insets.left - insets.right - flexLine._mainSize) / 2.0;
                    childRight = width - paddingRight - (width - insets.left - insets.right - flexLine._mainSize) / 2.0;
                    break;
                case JustifyContent.SPACE_AROUND: {
                    const visibleCount = flexLine.layoutVisibleItemCount;
                    if (visibleCount !== 0) {
                        spaceBetweenItem = (width - insets.left - insets.right - flexLine.mainSize) / visibleCount;
                    }
                    childLeft = paddingLeft + spaceBetweenItem / 2.0;
                    childRight = width - paddingRight - spaceBetweenItem / 2.0;
                    break;
                }
                case JustifyContent.SPACE_BETWEEN: {
                    const visibleCount = flexLine.layoutVisibleItemCount;
                    const denominator = visibleCount !== 1 ? visibleCount - 1 : 1.0;
                    childLeft = paddingLeft;
                    spaceBetweenItem = (width - insets.left - insets.right - flexLine.mainSize) / denominator;
                    childRight = width - paddingRight;
                    break;
                }
                default:
                    throw new Error('Invalid justifyContent is set: ' + this.justifyContent);
            }
            spaceBetweenItem = Math.max(spaceBetweenItem, 0);
            for (let j = 0; j < flexLine.itemCount; j++) {
                const child = this._getReorderedChildAt(currentViewIndex);
                if (child === null) {
                    continue;
                }
                else if (child.isCollapsed) {
                    currentViewIndex++;
                    continue;
                }
                const lp = child; // .style;
                childLeft += lp.effectiveMarginLeft;
                childRight -= lp.effectiveMarginRight;
                // Omit divider
                if (this.flexWrap === FlexWrap.WRAP_REVERSE) {
                    if (isRtl) {
                        this._layoutSingleChildHorizontal(child, flexLine, this.flexWrap, this.alignItems, Math.round(childRight) - child.getMeasuredWidth(), childBottom - child.getMeasuredHeight(), Math.round(childRight), childBottom);
                    }
                    else {
                        this._layoutSingleChildHorizontal(child, flexLine, this.flexWrap, this.alignItems, Math.round(childLeft), childBottom - child.getMeasuredHeight(), Math.round(childLeft) + child.getMeasuredWidth(), childBottom);
                    }
                }
                else {
                    if (isRtl) {
                        this._layoutSingleChildHorizontal(child, flexLine, this.flexWrap, this.alignItems, Math.round(childRight) - child.getMeasuredWidth(), childTop, Math.round(childRight), childTop + child.getMeasuredHeight());
                    }
                    else {
                        this._layoutSingleChildHorizontal(child, flexLine, this.flexWrap, this.alignItems, Math.round(childLeft), childTop, Math.round(childLeft) + child.getMeasuredWidth(), childTop + child.getMeasuredHeight());
                    }
                }
                childLeft += child.getMeasuredWidth() + spaceBetweenItem + lp.effectiveMarginRight;
                childRight -= child.getMeasuredWidth() + spaceBetweenItem + lp.effectiveMarginLeft;
                currentViewIndex++;
                const bounds = child._getCurrentLayoutBounds();
                flexLine._left = Math.min(flexLine._left, bounds.left - lp.effectiveMarginLeft);
                flexLine._top = Math.min(flexLine._top, bounds.top - lp.effectiveMarginTop);
                flexLine._right = Math.max(flexLine._right, bounds.right + lp.effectiveMarginRight);
                flexLine._bottom = Math.max(flexLine._bottom, bounds.bottom + lp.effectiveMarginBottom);
            }
            childTop += flexLine._crossSize;
            childBottom -= flexLine._crossSize;
        });
    }
    _layoutSingleChildHorizontal(view, flexLine, flexWrap, alignItems, left, top, right, bottom) {
        const lp = view; // .style;
        const alignSelf = FlexboxLayout.getAlignSelf(view);
        if (alignSelf !== 'auto') {
            alignItems = alignSelf;
        }
        const crossSize = flexLine._crossSize;
        switch (alignItems) {
            case AlignItems.FLEX_START:
            case AlignItems.STRETCH:
                if (flexWrap !== FlexWrap.WRAP_REVERSE) {
                    view.layout(left, top + lp.effectiveMarginTop, right, bottom + lp.effectiveMarginTop);
                }
                else {
                    view.layout(left, top - lp.effectiveMarginBottom, right, bottom - lp.effectiveMarginBottom);
                }
                break;
            case AlignItems.BASELINE:
                if (flexWrap !== FlexWrap.WRAP_REVERSE) {
                    let marginTop = flexLine._maxBaseline - FlexboxLayout.getBaseline(view);
                    marginTop = Math.max(marginTop, lp.effectiveMarginTop);
                    view.layout(left, top + marginTop, right, bottom + marginTop);
                }
                else {
                    let marginBottom = flexLine._maxBaseline - view.getMeasuredHeight() + FlexboxLayout.getBaseline(view);
                    marginBottom = Math.max(marginBottom, lp.effectiveMarginBottom);
                    view.layout(left, top - marginBottom, right, bottom - marginBottom);
                }
                break;
            case AlignItems.FLEX_END:
                if (flexWrap !== FlexWrap.WRAP_REVERSE) {
                    view.layout(left, top + crossSize - view.getMeasuredHeight() - lp.effectiveMarginBottom, right, top + crossSize - lp.effectiveMarginBottom);
                }
                else {
                    view.layout(left, top - crossSize + view.getMeasuredHeight() + lp.effectiveMarginTop, right, bottom - crossSize + view.getMeasuredHeight() + lp.effectiveMarginTop);
                }
                break;
            case AlignItems.CENTER: {
                const topFromCrossAxis = (crossSize - view.getMeasuredHeight()) / 2;
                if (flexWrap !== FlexWrap.WRAP_REVERSE) {
                    view.layout(left, top + topFromCrossAxis + lp.effectiveMarginTop - lp.effectiveMarginBottom, right, top + topFromCrossAxis + view.getMeasuredHeight() + lp.effectiveMarginTop - lp.effectiveMarginBottom);
                }
                else {
                    view.layout(left, top - topFromCrossAxis + lp.effectiveMarginTop - lp.effectiveMarginBottom, right, top - topFromCrossAxis + view.getMeasuredHeight() + lp.effectiveMarginTop - lp.effectiveMarginBottom);
                }
                break;
            }
        }
    }
    _layoutVertical(isRtl, fromBottomToTop, left, top, right, bottom, insets) {
        const paddingLeft = this.effectivePaddingLeft + insets.left;
        const paddingTop = this.effectivePaddingTop + insets.top;
        const paddingRight = this.effectivePaddingRight + insets.right;
        const paddingBottom = this.effectivePaddingBottom + insets.bottom;
        let childLeft = paddingLeft;
        let currentViewIndex = 0;
        const width = right - left;
        const height = bottom - top;
        let childRight = width - paddingRight;
        let childTop;
        let childBottom;
        this._flexLines.forEach((flexLine) => {
            // Omit divider.
            let spaceBetweenItem = 0.0;
            switch (this.justifyContent) {
                case JustifyContent.FLEX_START:
                    childTop = paddingTop;
                    childBottom = height - paddingBottom;
                    break;
                case JustifyContent.FLEX_END:
                    childTop = height - flexLine._mainSize + paddingBottom;
                    childBottom = flexLine._mainSize - paddingTop;
                    break;
                case JustifyContent.CENTER:
                    childTop = paddingTop + (height - insets.top - insets.bottom - flexLine._mainSize) / 2.0;
                    childBottom = height - paddingBottom - (height - insets.top - insets.bottom - flexLine._mainSize) / 2.0;
                    break;
                case JustifyContent.SPACE_AROUND: {
                    const visibleCount = flexLine.layoutVisibleItemCount;
                    if (visibleCount !== 0) {
                        spaceBetweenItem = (height - insets.top - insets.bottom - flexLine._mainSize) / visibleCount;
                    }
                    childTop = paddingTop + spaceBetweenItem / 2.0;
                    childBottom = height - paddingBottom - spaceBetweenItem / 2.0;
                    break;
                }
                case JustifyContent.SPACE_BETWEEN: {
                    const visibleCount = flexLine.layoutVisibleItemCount;
                    const denominator = visibleCount !== 1 ? visibleCount - 1 : 1.0;
                    childTop = paddingTop;
                    spaceBetweenItem = (height - insets.top - insets.bottom - flexLine.mainSize) / denominator;
                    childBottom = height - paddingBottom;
                    break;
                }
                default:
                    throw new Error('Invalid justifyContent is set: ' + this.justifyContent);
            }
            spaceBetweenItem = Math.max(spaceBetweenItem, 0);
            for (let j = 0; j < flexLine.itemCount; j++) {
                const child = this._getReorderedChildAt(currentViewIndex);
                if (child === null) {
                    continue;
                }
                else if (child.isCollapsed) {
                    currentViewIndex++;
                    continue;
                }
                const lp = child; // .style;
                childTop += lp.effectiveMarginTop;
                childBottom -= lp.effectiveMarginBottom;
                // Omit divider.
                if (isRtl) {
                    if (fromBottomToTop) {
                        this._layoutSingleChildVertical(child, flexLine, true, this.alignItems, childRight - child.getMeasuredWidth(), Math.round(childBottom) - child.getMeasuredHeight(), childRight, Math.round(childBottom));
                    }
                    else {
                        this._layoutSingleChildVertical(child, flexLine, true, this.alignItems, childRight - child.getMeasuredWidth(), Math.round(childTop), childRight, Math.round(childTop) + child.getMeasuredHeight());
                    }
                }
                else {
                    if (fromBottomToTop) {
                        this._layoutSingleChildVertical(child, flexLine, false, this.alignItems, childLeft, Math.round(childBottom) - child.getMeasuredHeight(), childLeft + child.getMeasuredWidth(), Math.round(childBottom));
                    }
                    else {
                        this._layoutSingleChildVertical(child, flexLine, false, this.alignItems, childLeft, Math.round(childTop), childLeft + child.getMeasuredWidth(), Math.round(childTop) + child.getMeasuredHeight());
                    }
                }
                childTop += child.getMeasuredHeight() + spaceBetweenItem + lp.effectiveMarginBottom;
                childBottom -= child.getMeasuredHeight() + spaceBetweenItem + lp.effectiveMarginTop;
                currentViewIndex++;
                const bounds = child._getCurrentLayoutBounds();
                flexLine._left = Math.min(flexLine._left, bounds.left - lp.effectiveMarginLeft);
                flexLine._top = Math.min(flexLine._top, bounds.top - lp.effectiveMarginTop);
                flexLine._right = Math.max(flexLine._right, bounds.right + lp.effectiveMarginRight);
                flexLine._bottom = Math.max(flexLine._bottom, bounds.bottom + lp.effectiveMarginBottom);
            }
            childLeft += flexLine.crossSize;
            childRight -= flexLine.crossSize;
        });
    }
    _layoutSingleChildVertical(view, flexLine, isRtl, alignItems, left, top, right, bottom) {
        const lp = view; // .style;
        const alignSelf = FlexboxLayout.getAlignSelf(view);
        if (alignSelf !== 'auto') {
            alignItems = alignSelf;
        }
        const crossSize = flexLine.crossSize;
        switch (alignItems) {
            case AlignItems.FLEX_START:
            case AlignItems.STRETCH:
            case AlignItems.BASELINE:
                if (!isRtl) {
                    view.layout(left + lp.effectiveMarginLeft, top, right + lp.effectiveMarginLeft, bottom);
                }
                else {
                    view.layout(left - lp.effectiveMarginRight, top, right - lp.effectiveMarginRight, bottom);
                }
                break;
            case AlignItems.FLEX_END:
                if (!isRtl) {
                    view.layout(left + crossSize - view.getMeasuredWidth() - lp.effectiveMarginRight, top, right + crossSize - view.getMeasuredWidth() - lp.effectiveMarginRight, bottom);
                }
                else {
                    // If the flexWrap === FLEX_WRAP_WRAP_REVERSE, the direction of the
                    // flexEnd is flipped (from left to right).
                    view.layout(left - crossSize + view.getMeasuredWidth() + lp.effectiveMarginLeft, top, right - crossSize + view.getMeasuredWidth() + lp.effectiveMarginLeft, bottom);
                }
                break;
            case AlignItems.CENTER: {
                const leftFromCrossAxis = (crossSize - view.getMeasuredWidth()) / 2;
                if (!isRtl) {
                    view.layout(left + leftFromCrossAxis + lp.effectiveMarginLeft - lp.effectiveMarginRight, top, right + leftFromCrossAxis + lp.effectiveMarginLeft - lp.effectiveMarginRight, bottom);
                }
                else {
                    view.layout(left - leftFromCrossAxis + lp.effectiveMarginLeft - lp.effectiveMarginRight, top, right - leftFromCrossAxis + lp.effectiveMarginLeft - lp.effectiveMarginRight, bottom);
                }
                break;
            }
        }
    }
    // Omit divider in onDraw(), drawDividersHorizontal, drawDividersVertical, drawVerticalDivider
    // requestLayout on set flexDirection, set flexWrap, set justifyContent, set alignItems, set alignContent
    // NOTE Consider moving to View if frequently used
    static getChildMeasureSpec(spec, padding, childDimension) {
        const specMode = layout.getMeasureSpecMode(spec);
        const specSize = layout.getMeasureSpecSize(spec);
        const size = Math.max(0, specSize - padding);
        let resultSize = 0;
        let resultMode = 0;
        switch (specMode) {
            // Parent has imposed an exact size on us
            case EXACTLY:
                if (childDimension >= 0) {
                    resultSize = childDimension;
                    resultMode = EXACTLY;
                }
                else if (childDimension === MATCH_PARENT) {
                    resultSize = size;
                    resultMode = EXACTLY;
                }
                else if (childDimension === WRAP_CONTENT) {
                    resultSize = size;
                    resultMode = AT_MOST;
                }
                break;
            case AT_MOST:
                if (childDimension >= 0) {
                    resultSize = childDimension;
                    resultMode = EXACTLY;
                }
                else if (childDimension === MATCH_PARENT) {
                    resultSize = size;
                    resultMode = AT_MOST;
                }
                else if (childDimension === WRAP_CONTENT) {
                    resultSize = size;
                    resultMode = AT_MOST;
                }
                break;
            case UNSPECIFIED:
                if (childDimension >= 0) {
                    resultSize = childDimension;
                    resultMode = EXACTLY;
                }
                else if (childDimension === MATCH_PARENT) {
                    resultSize = View_sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                    resultMode = UNSPECIFIED;
                }
                else if (childDimension === WRAP_CONTENT) {
                    resultSize = View_sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                    resultMode = UNSPECIFIED;
                }
                break;
        }
        return layout.makeMeasureSpec(resultSize, resultMode);
    }
}
(function (FlexboxLayout) {
    function getBaseline(child) {
        // TODO: Check if we support baseline for iOS.
        return 0;
    }
    FlexboxLayout.getBaseline = getBaseline;
    function getPaddingStart(child) {
        return child.effectivePaddingLeft;
    }
    FlexboxLayout.getPaddingStart = getPaddingStart;
    function getPaddingEnd(child) {
        return child.effectivePaddingRight;
    }
    FlexboxLayout.getPaddingEnd = getPaddingEnd;
})(FlexboxLayout || (FlexboxLayout = {}));
//# sourceMappingURL=index.ios.js.map