/*
 * MIT License
 *
 * Copyright (c) 2016 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.alibaba.android.vlayout.layout;

import java.util.Arrays;

import com.alibaba.android.vlayout.LayoutManagerHelper;
import com.alibaba.android.vlayout.OrientationHelperEx;
import com.alibaba.android.vlayout.VirtualLayoutManager;
import com.alibaba.android.vlayout.VirtualLayoutManager.AnchorInfoWrapper;
import com.alibaba.android.vlayout.VirtualLayoutManager.LayoutParams;
import com.alibaba.android.vlayout.VirtualLayoutManager.LayoutStateWrapper;

import android.graphics.Rect;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.State;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;

import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static com.alibaba.android.vlayout.VirtualLayoutManager.VERTICAL;

/**
 * <pre>
 * Currently support 1+3(max) layout
 * 1 + 0
 * -------------------------
 * |                       |
 * |                       |
 * |           1           |
 * |                       |
 * |                       |
 * |                       |
 * -------------------------
 *
 * 1 + 1
 * -------------------------
 * |           |           |
 * |           |           |
 * |           |           |
 * |     1     |     2     |
 * |           |           |
 * |           |           |
 * |           |           |
 * -------------------------
 *
 * 1 + 2
 * -------------------------
 * |           |           |
 * |           |     2     |
 * |           |           |
 * |     1     |-----------|
 * |           |           |
 * |           |     3     |
 * |           |           |
 * -------------------------
 *
 * 1 + 3
 * -------------------------
 * |           |           |
 * |           |     2     |
 * |           |           |
 * |     1     |-----------|
 * |           |     |     |
 * |           |  3  |  4  |
 * |           |     |     |
 * -------------------------
 *  1 + 4
 * -------------------------
 * |           |           |
 * |           |     2     |
 * |           |           |
 * |     1     |-----------|
 * |           |   |   |   |
 * |           | 3 | 4 | 5 |
 * |           |   |   |   |
 * -------------------------
 * </pre>
 *
 * @author villadora
 * @since 1.0.0
 */
public class OnePlusNLayoutHelper extends AbstractFullFillLayoutHelper {

    private static final String TAG = "OnePlusNLayoutHelper";


    private Rect mAreaRect = new Rect();

    private View[] mChildrenViews;

    private float[] mColWeights = new float[0];

    private float mRowWeight = Float.NaN;

    public OnePlusNLayoutHelper() {
        setItemCount(0);
    }

    public OnePlusNLayoutHelper(int itemCount) {
        this(itemCount, 0, 0, 0, 0);
    }

    public OnePlusNLayoutHelper(int itemCount, int leftMargin, int topMargin, int rightMargin,
            int bottomMargin) {
        setItemCount(itemCount);
    }

    /**
     * {@inheritDoc}
     * <p/>
     * Currently, this layout supports maximum children up to 5, otherwise {@link
     * IllegalArgumentException}
     * will be thrown
     *
     * @param start start position of items handled by this layoutHelper
     * @param end   end position of items handled by this layoutHelper, if end &lt; start or end -
     *              start &gt 6, it will throw {@link IllegalArgumentException}
     */
    @Override
    public void onRangeChange(int start, int end) {
        if (end - start > 6) {
            throw new IllegalArgumentException(
                    "OnePlusNLayoutHelper only supports maximum 7 children now");
        }
    }



    public void setColWeights(float[] weights) {
        if (weights != null) {
            this.mColWeights = Arrays.copyOf(weights, weights.length);
        } else {
            this.mColWeights = new float[0];
        }
    }

    public void setRowWeight(float weight) {
        this.mRowWeight = weight;
    }

    @Override
    public void layoutViews(RecyclerView.Recycler recycler, RecyclerView.State state,
            LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper) {
        // reach the end of this layout
        if (isOutOfRange(layoutState.getCurrentPosition())) {
            return;
        }
        final OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();
        final boolean layoutInVertical = helper.getOrientation() == VERTICAL;
        final boolean layoutStart = layoutState.getLayoutDirection() == LayoutStateWrapper.LAYOUT_START;
        final int parentWidth = helper.getContentWidth();
        final int parentHeight = helper.getContentHeight();
        final int parentHPadding = helper.getPaddingLeft() + helper.getPaddingRight()
            + getHorizontalMargin() + getHorizontalPadding();
        final int parentVPadding = helper.getPaddingTop() + helper.getPaddingBottom()
            + getVerticalMargin() + getVerticalPadding();

        final int currentPosition = layoutState.getCurrentPosition();
        if (hasHeader && currentPosition == getRange().getLower()) {
            View header = nextView(recycler, layoutState, helper, result);
            int headerConsumed = handleHeader(header, layoutState, result, helper, layoutInVertical, parentWidth, parentHeight,
                parentHPadding, parentVPadding);
            if (header != null) {
                int left = 0, right = 0, top = 0, bottom = 0;
                if (layoutInVertical) {
                    if (layoutStart) {
                        bottom = layoutState.getOffset();
                        top = bottom - headerConsumed;
                    } else {
                        top = layoutState.getOffset() + (mLayoutWithAnchor ? 0 : mMarginTop + mPaddingTop);
                        bottom = top + headerConsumed;
                    }
                    left = helper.getPaddingLeft() + mMarginLeft + mPaddingLeft;
                    right = left + orientationHelper.getDecoratedMeasurementInOther(header);
                } else {
                    if (layoutStart) {
                        right = layoutState.getOffset();
                        left = right - headerConsumed;
                    } else {
                        left = layoutState.getOffset() + (mLayoutWithAnchor ? 0 : mMarginLeft + mPaddingLeft);
                        right = left + headerConsumed;
                    }
                    top = helper.getPaddingTop() + mMarginTop + mPaddingTop;
                    bottom = top + orientationHelper.getDecoratedMeasurementInOther(header);
                }
                layoutChildWithMargin(header, left, top, right, bottom, helper);
            }
            result.mConsumed = headerConsumed;
            handleStateOnResult(result, header);
        } else if (hasFooter && currentPosition == getRange().getUpper()) {
            View footer = nextView(recycler, layoutState, helper, result);
            int footerConsumed = handleFooter(footer, layoutState, result, helper, layoutInVertical, parentWidth, parentHeight,
                parentHPadding, parentVPadding);
            if (footer != null) {
                int left = 0, right = 0, top = 0, bottom = 0;
                if (layoutInVertical) {
                    if (layoutStart) {
                        bottom = layoutState.getOffset() - (mLayoutWithAnchor ? 0 : mMarginBottom + mPaddingBottom); //TODO margin overlap
                        top = bottom - footerConsumed;
                    } else {
                        top = layoutState.getOffset();
                        bottom = top + footerConsumed;
                    }
                    left = helper.getPaddingLeft() + mMarginLeft + mPaddingLeft;
                    right = left + orientationHelper.getDecoratedMeasurementInOther(footer);
                } else {
                    if (layoutStart) {
                        right = layoutState.getOffset() - (mLayoutWithAnchor ? 0 : mMarginRight + mPaddingRight); //TODO margin overlap
                        left = right - footerConsumed;
                    } else {
                        left = layoutState.getOffset();
                        right = left + footerConsumed;
                    }
                    top = helper.getPaddingTop() + mMarginTop + mPaddingTop;
                    bottom = top + orientationHelper.getDecoratedMeasurementInOther(footer);
                }
                layoutChildWithMargin(footer, left, top, right, bottom, helper);
            }
            result.mConsumed = footerConsumed;
            handleStateOnResult(result, footer);
        } else {
            int contentCount = getItemCount() - (hasHeader ? 1 : 0) - (hasFooter ? 1 : 0);
            if (mChildrenViews == null || mChildrenViews.length != contentCount) {
                mChildrenViews = new View[contentCount];
            }
            int count = getAllChildren(mChildrenViews, recycler, layoutState, result, helper);
            if (count == 0 || count < contentCount) {
                return;
            }
            int mainConsumed = 0;
            if (contentCount == 1) {
                mainConsumed = handleOne(layoutState, result, helper, layoutInVertical, parentWidth, parentHeight, parentHPadding, parentVPadding);
            } else if (contentCount == 2) {
                mainConsumed = handleTwo(layoutState, result, helper, layoutInVertical, parentWidth, parentHeight, parentHPadding, parentVPadding);
            } else if (contentCount == 3) {
                mainConsumed = handleThree(layoutState, result, helper, layoutInVertical, parentWidth, parentHeight, parentHPadding, parentVPadding);
            } else if (contentCount == 4) {
                mainConsumed = handleFour(layoutState, result, helper, layoutInVertical, parentWidth, parentHeight, parentHPadding, parentVPadding);
            } else if (contentCount == 5) {
                mainConsumed = handleFive(layoutState, result, helper, layoutInVertical, parentWidth, parentHeight, parentHPadding, parentVPadding);
            }
            result.mConsumed = mainConsumed;
            Arrays.fill(mChildrenViews, null);
        }
    }

    private float getViewMainWeight(int index) {
        if (mColWeights.length > index) {
            return mColWeights[index];
        }

        return Float.NaN;
    }

    @Override
    protected void onClear(LayoutManagerHelper helper) {
        super.onClear(helper);
    }

    @Override
    public void checkAnchorInfo(State state, AnchorInfoWrapper anchorInfo, LayoutManagerHelper helper) {
        super.checkAnchorInfo(state, anchorInfo, helper);
        mLayoutWithAnchor = true;
    }

    @Override
    public int computeAlignOffset(int offset, boolean isLayoutEnd, boolean useAnchor,
            LayoutManagerHelper helper) {
        //Log.d(TAG,
        //    "range " + getRange() + " offset " + offset + " isLayoutEnd " + isLayoutEnd + " useAnchor " + useAnchor
        //        + " helper " + this.hashCode());
        final boolean layoutInVertical = helper.getOrientation() == LinearLayoutManager.VERTICAL;

        if (useAnchor) {
            return 0;
        }
        if (isLayoutEnd) {
            if (offset == getItemCount() - 1) {
                return layoutInVertical ? mMarginBottom + mPaddingBottom : mMarginRight + mPaddingRight;
            }
        } else {
            if (offset == 0) {
                return layoutInVertical ? -mMarginTop - mPaddingTop : -mMarginLeft - mPaddingLeft;
            }
        }
        return 0;
    }

    private int handleHeader(View header, LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper,
        boolean layoutInVertical, int parentWidth, int parentHeight, int parentHPadding, int parentVPadding) {
        if (header == null) {
            return 0;
        }
        OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();

        final VirtualLayoutManager.LayoutParams lp = (LayoutParams) header.getLayoutParams();


        // fill width
        int widthSpec = helper.getChildMeasureSpec(parentWidth - parentHPadding,
            layoutInVertical ? MATCH_PARENT : lp.width, !layoutInVertical);
        int heightSpec = helper.getChildMeasureSpec(parentHeight - parentVPadding,
            layoutInVertical ? lp.height : MeasureSpec.EXACTLY, layoutInVertical);
        helper.measureChildWithMargins(header, widthSpec, heightSpec);
        return orientationHelper.getDecoratedMeasurement(header);
    }

    private int handleFooter(View footer, LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper,
        boolean layoutInVertical, int parentWidth, int parentHeight, int parentHPadding, int parentVPadding) {
        if (footer == null) {
            return 0;
        }

        OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();

        final VirtualLayoutManager.LayoutParams lp = (LayoutParams) footer.getLayoutParams();

        // fill width
        int widthSpec = helper.getChildMeasureSpec(parentWidth - parentHPadding,
            layoutInVertical ? MATCH_PARENT : lp.width, !layoutInVertical);
        int heightSpec = helper.getChildMeasureSpec(parentHeight - parentVPadding,
            layoutInVertical ? lp.height : MeasureSpec.EXACTLY, layoutInVertical);
        helper.measureChildWithMargins(footer, widthSpec, heightSpec);
        return orientationHelper.getDecoratedMeasurement(footer);
    }

    private int handleOne(LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper,
        boolean layoutInVertical, int parentWidth, int parentHeight, int parentHPadding, int parentVPadding) {
        int mainConsumed = 0;
        OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();

        View view = mChildrenViews[0];
        final VirtualLayoutManager.LayoutParams lp = (LayoutParams) view.getLayoutParams();

        if (!Float.isNaN(mAspectRatio)) {
            if (layoutInVertical) {
                lp.height = (int) ((parentWidth - parentHPadding) / mAspectRatio);
            } else {
                lp.width = (int) ((parentHeight - parentVPadding) * mAspectRatio);
            }
        }

        final float weight = getViewMainWeight(0);

        // fill width
        int widthSpec = helper.getChildMeasureSpec(
            Float.isNaN(weight) ? (parentWidth - parentHPadding)
                : (int) ((parentWidth - parentHPadding) * weight),
            layoutInVertical ? MATCH_PARENT : lp.width, !layoutInVertical);
        int heightSpec = helper.getChildMeasureSpec(parentHeight - parentVPadding,
            layoutInVertical ? lp.height : MeasureSpec.EXACTLY, layoutInVertical);

        helper.measureChildWithMargins(view, widthSpec, heightSpec);

        mainConsumed += orientationHelper.getDecoratedMeasurement(view);

        calculateRect(mainConsumed, mAreaRect, layoutState, helper);

        layoutChildWithMargin(view, mAreaRect.left, mAreaRect.top, mAreaRect.right, mAreaRect.bottom,
            helper);
        handleStateOnResult(result, view);
        mainConsumed = mAreaRect.bottom - mAreaRect.top + (hasHeader ? 0 : mMarginTop + mPaddingTop) + (hasFooter ? 0 : mMarginBottom + mPaddingBottom);
        return mainConsumed;
    }

    private int handleTwo(LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper,
        boolean layoutInVertical, int parentWidth, int parentHeight, int parentHPadding, int parentVPadding) {
        int mainConsumed = 0;
        OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();

        final View child1 = mChildrenViews[0];
        final VirtualLayoutManager.LayoutParams lp1 = (VirtualLayoutManager.LayoutParams) child1.getLayoutParams();
        final View child2 = mChildrenViews[1];
        final VirtualLayoutManager.LayoutParams lp2 = (VirtualLayoutManager.LayoutParams) child2.getLayoutParams();
        final float weight1 = getViewMainWeight(0);
        final float weight2 = getViewMainWeight(1);


        if (layoutInVertical) {

            if (!Float.isNaN(mAspectRatio)) {
                lp1.height = lp2.height = (int) ((parentWidth - parentHPadding) / mAspectRatio);
            }

            lp2.topMargin = lp1.topMargin;
            lp2.bottomMargin = lp1.bottomMargin;

            int availableSpace = parentWidth - parentHPadding - lp1.leftMargin - lp1.rightMargin
                - lp2.leftMargin - lp2.rightMargin;
            int width1 = Float.isNaN(weight1) ? (int) (availableSpace / 2.0f + 0.5f)
                : (int) (availableSpace * weight1 / 100 + 0.5f);
            int width2 = Float.isNaN(weight2) ? (availableSpace - width1)
                : (int) (availableSpace * weight2 / 100 + 0.5f);

            helper.measureChildWithMargins(child1,
                MeasureSpec.makeMeasureSpec(width1 + lp1.leftMargin + lp1.rightMargin,
                    MeasureSpec.EXACTLY),
                helper.getChildMeasureSpec(helper.getContentHeight(), lp1.height, true));

            helper.measureChildWithMargins(child2,
                MeasureSpec.makeMeasureSpec(width2 + lp2.leftMargin + lp2.rightMargin,
                    MeasureSpec.EXACTLY),
                helper.getChildMeasureSpec(helper.getContentHeight(), lp2.height, true));

            mainConsumed += Math.max(orientationHelper.getDecoratedMeasurement(child1),
                orientationHelper.getDecoratedMeasurement(child2));

            calculateRect(mainConsumed, mAreaRect, layoutState, helper);

            int right1 = mAreaRect.left + orientationHelper
                .getDecoratedMeasurementInOther(child1);

            layoutChildWithMargin(child1, mAreaRect.left, mAreaRect.top, right1, mAreaRect.bottom, helper);

            layoutChildWithMargin(child2, right1, mAreaRect.top,
                right1 + orientationHelper.getDecoratedMeasurementInOther(child2), mAreaRect.bottom, helper);

            mainConsumed = mAreaRect.bottom - mAreaRect.top + (hasHeader ? 0 : mMarginTop + mPaddingTop) + (hasFooter ? 0 : mMarginBottom + mPaddingBottom);
        } else {

            if (!Float.isNaN(mAspectRatio)) {
                lp1.width = lp2.width = (int) ((parentHeight - parentVPadding) * mAspectRatio);
            }

            int availableSpace = parentHeight - parentVPadding - lp1.topMargin
                - lp1.bottomMargin
                - lp2.topMargin - lp2.bottomMargin;
            int height1 = Float.isNaN(weight1) ? (int) (availableSpace / 2.0f + 0.5f)
                : (int) (availableSpace * weight1 / 100 + 0.5f);
            int height2 = Float.isNaN(weight2) ? (int) (availableSpace - height1)
                : (int) (availableSpace * weight2 / 100 + 0.5f);

            helper.measureChildWithMargins(child1,
                helper.getChildMeasureSpec(helper.getContentWidth(), lp1.width, true),
                MeasureSpec.makeMeasureSpec(height1 + lp1.topMargin + lp1.bottomMargin,
                    MeasureSpec.EXACTLY));

            int width = child1.getMeasuredWidth();

            helper.measureChildWithMargins(child2,
                MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(height2 + lp2.topMargin + lp2.bottomMargin,
                    MeasureSpec.EXACTLY));

            mainConsumed += Math.max(orientationHelper.getDecoratedMeasurement(child1),
                orientationHelper.getDecoratedMeasurement(child2));

            calculateRect(mainConsumed, mAreaRect, layoutState, helper);

            int bottom1 = mAreaRect.top + orientationHelper
                .getDecoratedMeasurementInOther(child1);
            layoutChildWithMargin(child1, mAreaRect.left, mAreaRect.top,
                mAreaRect.right, bottom1, helper);

            layoutChildWithMargin(child2, mAreaRect.left,
                bottom1, mAreaRect.right,
                bottom1 + orientationHelper.getDecoratedMeasurementInOther(child2), helper);
            mainConsumed = mAreaRect.right - mAreaRect.left + (hasHeader ? 0 : mMarginLeft + mPaddingRight) + (hasFooter ? 0 : mMarginRight + mPaddingRight);
        }

        handleStateOnResult(result, mChildrenViews);
        return mainConsumed;
    }

    private int handleThree(LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper,
        boolean layoutInVertical, int parentWidth, int parentHeight, int parentHPadding, int parentVPadding) {
        int mainConsumed = 0;
        OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();

        final View child1 = mChildrenViews[0];
        final VirtualLayoutManager.LayoutParams lp1 = (VirtualLayoutManager.LayoutParams) child1.getLayoutParams();
        final View child2 = helper.getReverseLayout() ? mChildrenViews[2] : mChildrenViews[1];
        final View child3 = helper.getReverseLayout() ? mChildrenViews[1] : mChildrenViews[2];

        final VirtualLayoutManager.LayoutParams lp2 = (VirtualLayoutManager.LayoutParams) child2.getLayoutParams();
        final VirtualLayoutManager.LayoutParams lp3 = (VirtualLayoutManager.LayoutParams) child3.getLayoutParams();

        final float weight1 = getViewMainWeight(0);
        final float weight2 = getViewMainWeight(1);
        final float weight3 = getViewMainWeight(2);

        if (layoutInVertical) {

            if (!Float.isNaN(mAspectRatio)) {
                lp1.height = (int) ((parentWidth - parentHPadding) / mAspectRatio);
            }

            // make border consistent
            lp2.topMargin = lp1.topMargin;
            lp3.bottomMargin = lp1.bottomMargin;

            lp3.leftMargin = lp2.leftMargin;
            lp3.rightMargin = lp2.rightMargin;

            int availableSpace = parentWidth - parentHPadding - lp1.leftMargin - lp1.rightMargin
                - lp2.leftMargin - lp2.rightMargin;
            int width1 = Float.isNaN(weight1) ? (int) (availableSpace / 2.0f + 0.5f)
                : (int) (availableSpace * weight1 / 100 + 0.5f);
            int width2 = Float.isNaN(weight2) ? (int) (availableSpace - width1)
                : (int) (availableSpace * weight2 / 100 + 0.5);
            int width3 = Float.isNaN(weight3) ? (int) (width2)
                : (int) (availableSpace * weight3 / 100 + 0.5);

            helper.measureChildWithMargins(child1,
                MeasureSpec.makeMeasureSpec(width1 + lp1.leftMargin + lp1.rightMargin,
                    MeasureSpec.EXACTLY),
                helper.getChildMeasureSpec(helper.getContentHeight(), lp1.height, true));

            int height1 = child1.getMeasuredHeight();
            int height2 =
                Float.isNaN(mRowWeight) ?
                    (int) ((height1 - lp2.bottomMargin - lp3.topMargin) / 2.0f + 0.5f)
                    : (int) ((height1 - lp2.bottomMargin - lp3.topMargin) * mRowWeight
                        / 100 + 0.5f);

            int height3 = height1 - lp2.bottomMargin - lp3.topMargin - height2;

            helper.measureChildWithMargins(child2,
                MeasureSpec.makeMeasureSpec(width2 + lp2.leftMargin + lp2.rightMargin,
                    MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(height2 + lp2.topMargin + lp2.bottomMargin,
                    MeasureSpec.EXACTLY));

            helper.measureChildWithMargins(child3,
                MeasureSpec.makeMeasureSpec(width3 + lp3.leftMargin + lp3.rightMargin,
                    MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(height3 + lp3.topMargin + lp3.bottomMargin,
                    MeasureSpec.EXACTLY));

            mainConsumed += Math.max(height1 + lp1.topMargin + lp1.bottomMargin,
                height2 + lp2.topMargin + lp2.bottomMargin + height3 + lp3.topMargin
                    + lp3.bottomMargin);

            calculateRect(mainConsumed, mAreaRect, layoutState, helper);

            int right1 = mAreaRect.left + orientationHelper
                .getDecoratedMeasurementInOther(child1);
            layoutChildWithMargin(child1, mAreaRect.left, mAreaRect.top, right1, mAreaRect.bottom, helper);

            int right2 = right1 + orientationHelper.getDecoratedMeasurementInOther(child2);
            layoutChildWithMargin(child2, right1, mAreaRect.top, right2,
                mAreaRect.top + child2.getMeasuredHeight() + lp2.topMargin + lp2.bottomMargin, helper);

            layoutChildWithMargin(child3, right1, mAreaRect.bottom - orientationHelper.getDecoratedMeasurement(child3),
                right1 + orientationHelper.getDecoratedMeasurementInOther(child3), mAreaRect.bottom, helper);
            mainConsumed = mAreaRect.bottom - mAreaRect.top + (hasHeader ? 0 : mMarginTop + mPaddingTop) + (hasFooter ? 0 : mMarginBottom + mPaddingBottom);
        } else {
            // TODO: horizontal support
        }

        handleStateOnResult(result, mChildrenViews);
        return mainConsumed;
    }

    private int handleFour(LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper,
        boolean layoutInVertical, int parentWidth, int parentHeight, int parentHPadding, int parentVPadding) {

        int mainConsumed = 0;
        OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();

        final View child1 = mChildrenViews[0];
        final VirtualLayoutManager.LayoutParams lp1 = (VirtualLayoutManager.LayoutParams) child1.getLayoutParams();
        final View child2 = helper.getReverseLayout() ? mChildrenViews[3] : mChildrenViews[1];
        final VirtualLayoutManager.LayoutParams lp2 = (VirtualLayoutManager.LayoutParams) child2.getLayoutParams();
        final View child3 = mChildrenViews[2];
        final VirtualLayoutManager.LayoutParams lp3 = (VirtualLayoutManager.LayoutParams) child3.getLayoutParams();
        final View child4 = helper.getReverseLayout() ? mChildrenViews[1] : mChildrenViews[3];
        final VirtualLayoutManager.LayoutParams lp4 = (VirtualLayoutManager.LayoutParams) child4.getLayoutParams();

        final float weight1 = getViewMainWeight(0);
        final float weight2 = getViewMainWeight(1);
        final float weight3 = getViewMainWeight(2);
        final float weight4 = getViewMainWeight(3);

        if (layoutInVertical) {

            lp2.topMargin = lp1.topMargin;
            lp3.bottomMargin = lp4.bottomMargin = lp1.bottomMargin;
            lp3.leftMargin = lp2.leftMargin;
            lp4.rightMargin = lp2.rightMargin;

            if (!Float.isNaN(mAspectRatio)) {
                lp1.height = (int) ((parentWidth - parentHPadding) / mAspectRatio);
            }

            int availableSpace = parentWidth - parentHPadding - lp1.leftMargin - lp1.rightMargin
                - lp2.leftMargin
                - lp2.rightMargin;

            int width1 = Float.isNaN(weight1) ?
                (int) (availableSpace / 2.0f + 0.5f)
                : (int) (availableSpace * weight1 / 100 + 0.5f);
            int width2 = Float.isNaN(weight2) ? (int) (availableSpace - width1) :
                (int) (availableSpace * weight2 / 100 + 0.5f);

            int width3 = Float.isNaN(weight3) ? (int) (
                (width2 - lp3.rightMargin - lp4.leftMargin) / 2.0f + 0.5f)
                : (int) (availableSpace * weight3 / 100 + 0.5f);
            int width4 = Float.isNaN(weight4) ? (int) ((width2 - lp3.rightMargin
                - lp4.leftMargin - width3))
                : (int) (availableSpace * weight4 / 100 + 0.5f);

            helper.measureChildWithMargins(child1,
                MeasureSpec.makeMeasureSpec(width1 + lp1.leftMargin + lp1.rightMargin,
                    MeasureSpec.EXACTLY),
                helper.getChildMeasureSpec(helper.getContentHeight(), lp1.height, true));

            int height1 = child1.getMeasuredHeight();
            int height2 = Float.isNaN(mRowWeight) ?
                (int) ((height1 - lp2.bottomMargin - lp3.topMargin) / 2.0f + 0.5f)
                : (int) ((height1 - lp2.bottomMargin - lp3.topMargin) * mRowWeight / 100
                    + 0.5f);
            int height3 = (int) ((height1 - lp2.bottomMargin - lp3.topMargin) - height2);

            helper.measureChildWithMargins(child2,
                MeasureSpec.makeMeasureSpec(width2 + lp2.leftMargin + lp2.rightMargin,
                    MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(height2 + lp2.topMargin + lp2.bottomMargin,
                    MeasureSpec.EXACTLY));

            helper.measureChildWithMargins(child3,
                MeasureSpec.makeMeasureSpec(width3 + lp3.leftMargin + lp3.rightMargin,
                    MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(height3 + lp3.topMargin + lp3.bottomMargin,
                    MeasureSpec.EXACTLY));

            helper.measureChildWithMargins(child4,
                MeasureSpec.makeMeasureSpec(width4 + lp4.leftMargin + lp4.rightMargin,
                    MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(height3 + lp4.topMargin + lp4.bottomMargin,
                    MeasureSpec.EXACTLY));

            mainConsumed += Math.max(height1 + lp1.topMargin + lp1.bottomMargin,
                height2 + lp2.topMargin + lp2.bottomMargin + Math
                    .max(height3 + lp3.topMargin + lp3.bottomMargin,
                        height3 + lp4.topMargin + lp4.bottomMargin));

            calculateRect(mainConsumed, mAreaRect, layoutState, helper);

            int right1 = mAreaRect.left + orientationHelper
                .getDecoratedMeasurementInOther(child1);
            layoutChildWithMargin(child1, mAreaRect.left, mAreaRect.top, right1, mAreaRect.bottom, helper);

            int right2 = right1 + orientationHelper.getDecoratedMeasurementInOther(child2);
            layoutChildWithMargin(child2, right1, mAreaRect.top, right2,
                mAreaRect.top + orientationHelper.getDecoratedMeasurement(child2), helper);

            int right3 = right1 + orientationHelper.getDecoratedMeasurementInOther(child3);
            layoutChildWithMargin(child3, right1, mAreaRect.bottom - orientationHelper.getDecoratedMeasurement(child3),
                right3, mAreaRect.bottom, helper);

            layoutChildWithMargin(child4, right3, mAreaRect.bottom - orientationHelper.getDecoratedMeasurement(child4),
                right3 + orientationHelper.getDecoratedMeasurementInOther(child4), mAreaRect.bottom, helper);
            mainConsumed = mAreaRect.bottom - mAreaRect.top + (hasHeader ? 0 : mMarginTop + mPaddingTop) + (hasFooter ? 0 : mMarginBottom + mPaddingBottom);
        } else {
            // TODO: horizontal support
        }

        handleStateOnResult(result, mChildrenViews);
        return mainConsumed;
    }

    private int handleFive(LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper,
        boolean layoutInVertical, int parentWidth, int parentHeight, int parentHPadding, int parentVPadding) {
        int mainConsumed = 0;
        OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();

        final View child1 = mChildrenViews[0];
        final VirtualLayoutManager.LayoutParams lp1 = (VirtualLayoutManager.LayoutParams) child1.getLayoutParams();
        final View child2 = helper.getReverseLayout() ? mChildrenViews[4] : mChildrenViews[1];
        final VirtualLayoutManager.LayoutParams lp2 = (VirtualLayoutManager.LayoutParams) child2.getLayoutParams();
        final View child3 = helper.getReverseLayout() ? mChildrenViews[3] : mChildrenViews[2];
        final VirtualLayoutManager.LayoutParams lp3 = (VirtualLayoutManager.LayoutParams) child3.getLayoutParams();
        final View child4 = helper.getReverseLayout() ? mChildrenViews[2] : mChildrenViews[3];
        final VirtualLayoutManager.LayoutParams lp4 = (VirtualLayoutManager.LayoutParams) child4.getLayoutParams();
        final View child5 = helper.getReverseLayout() ? mChildrenViews[1] : mChildrenViews[4];
        final VirtualLayoutManager.LayoutParams lp5 = (VirtualLayoutManager.LayoutParams) child5.getLayoutParams();

        final float weight1 = getViewMainWeight(0);
        final float weight2 = getViewMainWeight(1);
        final float weight3 = getViewMainWeight(2);
        final float weight4 = getViewMainWeight(3);
        final float weight5 = getViewMainWeight(4);

        if (layoutInVertical) {

            lp2.topMargin = lp1.topMargin;
            lp3.bottomMargin = lp4.bottomMargin = lp1.bottomMargin;
            lp3.leftMargin = lp2.leftMargin;
            lp4.rightMargin = lp2.rightMargin;
            lp5.rightMargin = lp2.rightMargin;

            if (!Float.isNaN(mAspectRatio)) {
                lp1.height = (int) ((parentWidth - parentHPadding) / mAspectRatio);
            }

            int availableSpace = parentWidth - parentHPadding - lp1.leftMargin - lp1.rightMargin
                - lp2.leftMargin
                - lp2.rightMargin;

            int width1 = Float.isNaN(weight1) ?
                (int) (availableSpace / 2.0f + 0.5f)
                : (int) (availableSpace * weight1 / 100 + 0.5f);
            int width2 = Float.isNaN(weight2) ? (int) (availableSpace - width1) :
                (int) (availableSpace * weight2 / 100 + 0.5f);

            int width3 = Float.isNaN(weight3) ? (int) (
                (width2 - lp3.rightMargin - lp4.leftMargin) / 3.0f + 0.5f)
                : (int) (availableSpace * weight3 / 100 + 0.5f);
            int width4 = Float.isNaN(weight4) ? (int) (
                (width2 - lp3.rightMargin - lp4.leftMargin) / 3.0f + 0.5f)
                : (int) (availableSpace * weight4 / 100 + 0.5f);
            int width5 = Float.isNaN(weight5) ? (int) ((width2 - lp3.rightMargin
                - lp4.leftMargin - width3 - width4))
                : (int) (availableSpace * weight5 / 100 + 0.5f);

            helper.measureChildWithMargins(child1,
                MeasureSpec.makeMeasureSpec(width1 + lp1.leftMargin + lp1.rightMargin,
                    MeasureSpec.EXACTLY),
                helper.getChildMeasureSpec(helper.getContentHeight(), lp1.height, true));

            int height1 = child1.getMeasuredHeight();
            int height2 = Float.isNaN(mRowWeight) ?
                (int) ((height1 - lp2.bottomMargin - lp3.topMargin) / 2.0f + 0.5f)
                : (int) ((height1 - lp2.bottomMargin - lp3.topMargin) * mRowWeight / 100
                    + 0.5f);
            int height3 = (int) ((height1 - lp2.bottomMargin - lp3.topMargin) - height2);

            helper.measureChildWithMargins(child2,
                MeasureSpec.makeMeasureSpec(width2 + lp2.leftMargin + lp2.rightMargin,
                    MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(height2 + lp2.topMargin + lp2.bottomMargin,
                    MeasureSpec.EXACTLY));

            helper.measureChildWithMargins(child3,
                MeasureSpec.makeMeasureSpec(width3 + lp3.leftMargin + lp3.rightMargin,
                    MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(height3 + lp3.topMargin + lp3.bottomMargin,
                    MeasureSpec.EXACTLY));

            helper.measureChildWithMargins(child4,
                MeasureSpec.makeMeasureSpec(width4 + lp4.leftMargin + lp4.rightMargin,
                    MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(height3 + lp4.topMargin + lp4.bottomMargin,
                    MeasureSpec.EXACTLY));

            helper.measureChildWithMargins(child5,
                MeasureSpec.makeMeasureSpec(width5 + lp5.leftMargin + lp5.rightMargin,
                    MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(height3 + lp5.topMargin + lp5.bottomMargin,
                    MeasureSpec.EXACTLY));

            mainConsumed += Math.max(height1 + lp1.topMargin + lp1.bottomMargin,
                height2 + lp2.topMargin + lp2.bottomMargin + Math
                    .max(height3 + lp3.topMargin + lp3.bottomMargin,
                        height3 + lp4.topMargin + lp4.bottomMargin));

            calculateRect(mainConsumed, mAreaRect, layoutState, helper);

            int right1 = mAreaRect.left + orientationHelper.getDecoratedMeasurementInOther(child1);
            layoutChildWithMargin(child1, mAreaRect.left, mAreaRect.top, right1, mAreaRect.bottom, helper);

            int right2 = right1 + orientationHelper.getDecoratedMeasurementInOther(child2);
            layoutChildWithMargin(child2, right1, mAreaRect.top, right2,
                mAreaRect.top + orientationHelper.getDecoratedMeasurement(child2), helper);

            int right3 = right1 + orientationHelper.getDecoratedMeasurementInOther(child3);
            layoutChildWithMargin(child3, right1, mAreaRect.bottom - orientationHelper.getDecoratedMeasurement(child3),
                right3, mAreaRect.bottom, helper);

            int right4 = right3 + orientationHelper.getDecoratedMeasurementInOther(child4);
            layoutChildWithMargin(child4, right3, mAreaRect.bottom - orientationHelper.getDecoratedMeasurement(child4),
                right3 + orientationHelper.getDecoratedMeasurementInOther(child4), mAreaRect.bottom, helper);

            layoutChildWithMargin(child5, right4, mAreaRect.bottom - orientationHelper.getDecoratedMeasurement(child5),
                right4 + orientationHelper.getDecoratedMeasurementInOther(child5), mAreaRect.bottom, helper);
            mainConsumed = mAreaRect.bottom - mAreaRect.top + (hasHeader ? 0 : mMarginTop + mPaddingTop) + (hasFooter ? 0 : mMarginBottom + mPaddingBottom);
        } else {
            // TODO: horizontal support
        }

        handleStateOnResult(result, mChildrenViews);
        return mainConsumed;
    }

}