ZF_NAMESPACE_GLOBAL_BEGIN

void ZFUIViewLayout::layoutOnMeasure(ZF_OUT ZFUISize &ret,
                                     ZF_IN const ZFUISize &sizeHint,
                                     ZF_IN const ZFUISizeParam &sizeParam)
{
    ZFUIViewLayoutParam::sizeHintApply(ret, this->viewSizeMin(), sizeHint, sizeParam);
    for(zfindex i = 0; i < this->childCount(); ++i)
    {
        ZFUIView *child = this->childAtIndex(i);
        ZFUISize sizeHintTmp = sizeHint;
        if(sizeHintTmp.width >= 0)
        {
            sizeHintTmp.width -= ZFUIMarginGetX(child->layoutParam()->layoutMargin());
            if(sizeHintTmp.width < 0)
            {
                sizeHintTmp.width = 0;
            }
        }
        if(sizeHintTmp.height >= 0)
        {
            sizeHintTmp.height -= ZFUIMarginGetY(child->layoutParam()->layoutMargin());
            if(sizeHintTmp.height < 0)
            {
                sizeHintTmp.height = 0;
            }
        }
        child->layoutMeasure(sizeHintTmp, child->layoutParam()->sizeParam());

        ret.width = zfmMax(ret.width, child->layoutMeasuredSize().width + ZFUIMarginGetX(child->layoutParam()->layoutMargin()));
        ret.height = zfmMax(ret.height, child->layoutMeasuredSize().height + ZFUIMarginGetY(child->layoutParam()->layoutMargin()));
    }
}
static ZFUISize _ZFP_ZFUILinearLayout_measureVertical(ZF_IN ZFUILinearLayout *parent,
                                                      ZF_IN const ZFUISize &sizeHint,
                                                      ZF_IN const ZFUISizeParam &sizeParam)
{
    ZFUISize ret = ZFUISizeZero;
    zfbool hasLayoutedChild = zffalse;
    zfint parentMarginX = ZFUIMarginGetX(parent->layoutChildMargin());
    zfint parentMarginY = ZFUIMarginGetY(parent->layoutChildMargin());
    zfint sizeHintLast = -1;
    do
    {
        ret.height = 0;
        sizeHintLast = -1;
        for(zfindex i = 0; i < parent->childCount(); ++i)
        {
            ZFUIView *child = parent->childAtIndex(i);
            ZFUILinearLayoutParam *layoutParam = child->layoutParamT();
            if(!child->viewVisible() && !layoutParam->layoutReserveSpaceWhenNotVisible())
            {
                continue ;
            }
            zfint prevSpace = (hasLayoutedChild ? parent->layoutChildSpace() : 0);
            hasLayoutedChild = zftrue;

            zfint marginX = parentMarginX + ZFUIMarginGetX(layoutParam->layoutMargin());
            zfint marginY = parentMarginY + ZFUIMarginGetY(layoutParam->layoutMargin());
            zfint sizeHintTmp = ZFUIViewLayoutParam::sizeHintMerge(
                layoutParam->sizeHint().width,
                ZFUIViewLayoutParam::sizeHintOffset(sizeHint.width, -marginX));
            if(sizeParam.width == ZFUISizeType::e_Wrap && layoutParam->sizeParam().width == ZFUISizeType::e_Fill)
            {
                child->layoutMeasure(
                    ZFUISizeMake(sizeHintTmp, layoutParam->sizeHint().height),
                    ZFUISizeParamWrapWidthWrapHeight);
                sizeHintTmp = child->layoutMeasuredSize().width;
                if(sizeHintTmp < ret.width)
                {
                    sizeHintTmp = ret.width;
                }
                if(sizeHintLast == -1)
                {
                    sizeHintLast = sizeHintTmp;
                }
            }
            child->layoutMeasure(
                ZFUISizeMake(sizeHintTmp, layoutParam->sizeHint().height),
                layoutParam->sizeParam());
            ret.width = zfmMax(ret.width, child->layoutMeasuredSize().width + marginX);
            ret.height += prevSpace + child->layoutMeasuredSize().height + marginY;
        }
    } while(sizeHintLast != -1 && sizeHintLast != ret.width);
    return ZFUIViewLayoutParam::sizeHintApply(ret, sizeHint, sizeParam);
}
static void _ZFP_ZFUILinearLayout_layoutVertical(ZF_IN ZFUILinearLayout *parent,
                                                 ZF_IN const ZFUISize &size,
                                                 ZF_IN zfbool positiveDirection)
{
    zfint requiredSize = 0;
    zfint totalWeight = 0;
    zfbool hasLayoutedChild = zffalse;
    zfint parentMarginX = ZFUIMarginGetX(parent->layoutChildMargin());
    zfint parentMarginY = ZFUIMarginGetY(parent->layoutChildMargin());
    for(zfindex i = 0; i < parent->childCount(); ++i)
    {
        ZFUIView *child = parent->childAtIndex(i);
        ZFUILinearLayoutParam *layoutParam = child->layoutParamT();
        if(!child->viewVisible() && !layoutParam->layoutReserveSpaceWhenNotVisible())
        {
            continue ;
        }
        zfint prevSpace = (hasLayoutedChild ? parent->layoutChildSpace() : 0);
        hasLayoutedChild = zftrue;

        zfint marginX = parentMarginX + ZFUIMarginGetX(layoutParam->layoutMargin());
        zfint marginY = parentMarginY + ZFUIMarginGetY(layoutParam->layoutMargin());
        if(layoutParam->layoutWeight() > 0)
        {
            totalWeight += layoutParam->layoutWeight();
        }
        else
        {
            switch(layoutParam->sizeParam().height)
            {
                case ZFUISizeType::e_Wrap:
                {
                    child->layoutMeasure(
                        ZFUISizeMake(
                            ZFUIViewLayoutParam::sizeHintMerge(
                                layoutParam->sizeHint().width,
                                ZFUIViewLayoutParam::sizeHintOffset(size.width, -marginX)),
                            layoutParam->sizeHint().height),
                        layoutParam->sizeParam());
                    requiredSize += child->layoutMeasuredSize().height;
                }
                    break;
                case ZFUISizeType::e_Fill:
                    requiredSize = size.height;
                    break;
                default:
                    zfCoreCriticalShouldNotGoHere();
                    return ;
            }
        }

        requiredSize += prevSpace + marginY;
    }
    hasLayoutedChild = zffalse;
    zfint flexibleSize = zfmMax(0, size.height - requiredSize);
    zfint offset = (positiveDirection ? 0 : size.height);
    for(zfindex i = 0; i < parent->childCount(); ++i)
    {
        ZFUIView *child = parent->childAtIndex(i);
        ZFUILinearLayoutParam *layoutParam = child->layoutParamT();
        if(!child->viewVisible() && !layoutParam->layoutReserveSpaceWhenNotVisible())
        {
            continue ;
        }
        zfint prevSpace = (hasLayoutedChild ? parent->layoutChildSpace() : 0);
        hasLayoutedChild = zftrue;

        zfint marginX = parentMarginX + ZFUIMarginGetX(layoutParam->layoutMargin());
        zfint marginY = parentMarginY + ZFUIMarginGetY(layoutParam->layoutMargin());
        zfint totalUsedSpace = prevSpace + marginY;
        if(layoutParam->layoutWeight() > 0)
        {
            child->layoutMeasure(
                ZFUISizeMake(
                    ZFUIViewLayoutParam::sizeHintMerge(
                        layoutParam->sizeHint().width,
                        ZFUIViewLayoutParam::sizeHintOffset(size.width, -marginX)),
                    ZFUIViewLayoutParam::sizeHintMerge(flexibleSize * layoutParam->layoutWeight() / totalWeight, layoutParam->sizeHint().height)
                ),
                ZFUISizeParamMake(
                    layoutParam->sizeParam().width,
                    ZFUISizeType::e_Fill
                ));
            flexibleSize -= child->layoutMeasuredSize().height;
            totalWeight -= layoutParam->layoutWeight();
        }
        else
        {
            if(layoutParam->sizeParam().height == ZFUISizeType::e_Fill)
            {
                child->layoutMeasure(
                    ZFUISizeMake(
                        ZFUIViewLayoutParam::sizeHintMerge(
                            layoutParam->sizeHint().width,
                            ZFUIViewLayoutParam::sizeHintOffset(size.width, -marginX)),
                        ZFUIViewLayoutParam::sizeHintMerge(
                            layoutParam->sizeHint().height,
                            positiveDirection ? zfmMax(size.height - offset - totalUsedSpace, 0) : zfmMax(offset - totalUsedSpace, 0))
                    ),
                    layoutParam->sizeParam());
            }
        }
        if(positiveDirection)
        {
            child->layout(ZFUIAlignApply(
                layoutParam->layoutAlign(),
                ZFUIRectMake(0, offset + prevSpace, size.width, child->layoutMeasuredSize().height + marginY),
                child->layoutMeasuredSize(),
                layoutParam->layoutMargin() + parent->layoutChildMargin()));
            offset += prevSpace + child->layoutMeasuredSize().height + marginY;
        }
        else
        {
            offset -= child->layoutMeasuredSize().height + marginY + prevSpace;
            child->layout(ZFUIAlignApply(
                layoutParam->layoutAlign(),
                ZFUIRectMake(0, offset, size.width, child->layoutMeasuredSize().height + marginY),
                child->layoutMeasuredSize(),
                layoutParam->layoutMargin() + parent->layoutChildMargin()));
        }
    }
}
void ZFUIScrollThumb::scrollThumbPosCalc(ZF_OUT zfint &scrollThumbResultPos,
                                         ZF_OUT zfint &scrollThumbResultSize,
                                         ZF_IN zfint scrollViewSize,
                                         ZF_IN zfint scrollContentOffset,
                                         ZF_IN zfint scrollContentSize,
                                         ZF_IN zfint scrollThumbMinSize,
                                         ZF_IN_OPT zfint headMargin /* = 0 */,
                                         ZF_IN_OPT zfint tailMargin /* = 0 */)
{
    if(scrollViewSize <= 0
        || scrollContentSize <= 0)
    {
        scrollThumbResultSize = 0;
        return ;
    }

    zfint pos = 0;
    zfint size = 0;
    zfint fixedContentSize = zfmMax(scrollViewSize, scrollContentSize);
    zfint virtualContentSize = scrollViewSize - headMargin - tailMargin;

    if(scrollContentOffset > 0)
    { // bounce at head
        pos = 0;
        size = scrollViewSize - scrollContentOffset;
    }
    else if(scrollViewSize >= scrollContentSize && scrollContentOffset < 0)
    { // bounce at tail
        pos = -scrollContentOffset;
        size = scrollViewSize - pos;
    }
    else if(scrollContentOffset + fixedContentSize < scrollViewSize)
    { // bounce at tail
        pos = -scrollContentOffset;
        size = scrollContentOffset + fixedContentSize;
    }
    else
    { // no bounce
        pos = -scrollContentOffset;
        size = scrollViewSize;
    }

    scrollThumbResultPos = headMargin + pos * virtualContentSize / fixedContentSize;
    scrollThumbResultSize = size * virtualContentSize / fixedContentSize;

    if(scrollThumbResultSize < scrollThumbMinSize)
    {
        scrollThumbResultPos -= (scrollThumbMinSize - scrollThumbResultSize) / 2;
        scrollThumbResultSize = scrollThumbMinSize;
    }

    if(scrollThumbResultSize >= virtualContentSize - 2)
    {
        scrollThumbResultSize = virtualContentSize;
    }

    if(scrollThumbResultPos + scrollThumbResultSize + tailMargin > scrollViewSize)
    {
        scrollThumbResultPos = scrollViewSize - scrollThumbResultSize - tailMargin;
    }
    if(scrollThumbResultPos < headMargin)
    {
        scrollThumbResultPos = headMargin;
    }
}
static void _ZFP_ZFUIFlowLayout_layoutVertical(ZF_IN ZFUIFlowLayout *parent,
                                               ZF_IN const ZFUISize &size,
                                               ZF_IN zfbool positiveDirection)
{
    zfindex wrapIndex = 0;
    zfint lineSizeUsed = 0;
    while(wrapIndex < parent->childCount()) // for each line
    {
        zfint requiredSize = 0;
        zfint totalWeight = 0;
        zfbool hasLayoutedChild = zffalse;
        zfint parentMarginX = ZFUIMarginGetX(parent->layoutChildMargin());
        zfint parentMarginY = ZFUIMarginGetY(parent->layoutChildMargin());
        zfindex wrapIndexTmp = parent->childCount();
        zfint lineSize = 0;
        zfint prevLineSpace = (wrapIndex > 0 ? parent->layoutChildSpaceX() : 0);
        zfint flexableWrapSize = 0;
        for(zfindex i = wrapIndex; i < parent->childCount(); ++i)
        {
            ZFUIView *child = parent->childAtIndex(i);
            ZFUIFlowLayoutParam *layoutParam = child->layoutParamT();
            if(!child->viewVisible() && !layoutParam->layoutReserveSpaceWhenNotVisible())
            {
                continue ;
            }
            zfint prevSpace = (hasLayoutedChild ? parent->layoutChildSpaceY() : 0);
            hasLayoutedChild = zftrue;

            zfint marginX = parentMarginX + ZFUIMarginGetX(layoutParam->layoutMargin());
            zfint marginY = parentMarginY + ZFUIMarginGetY(layoutParam->layoutMargin());
            zfint childRequiredSize = 0;
            if(layoutParam->layoutWeight() > 0)
            {
                child->layoutMeasure(
                    ZFUISizeMake(
                        ZFUIViewLayoutParam::sizeHintMerge(
                            layoutParam->sizeHint().width,
                            ZFUIViewLayoutParam::sizeHintOffset(size.width, 0 - lineSizeUsed - marginX - prevLineSpace)),
                        layoutParam->sizeHint().height),
                    layoutParam->sizeParam());
                childRequiredSize += child->layoutMeasuredSize().height;
            }
            else
            {
                switch(layoutParam->sizeParam().height)
                {
                    case ZFUISizeType::e_Wrap:
                        child->layoutMeasure(
                            ZFUISizeMake(
                                ZFUIViewLayoutParam::sizeHintMerge(
                                    layoutParam->sizeHint().width,
                                    ZFUIViewLayoutParam::sizeHintOffset(size.width, 0 - lineSizeUsed - marginX - prevLineSpace)),
                                layoutParam->sizeHint().height),
                            layoutParam->sizeParam());
                        childRequiredSize += child->layoutMeasuredSize().height;
                        break;
                    case ZFUISizeType::e_Fill:
                        child->layoutMeasure(
                            ZFUISizeMake(
                                ZFUIViewLayoutParam::sizeHintMerge(
                                    layoutParam->sizeHint().width,
                                    ZFUIViewLayoutParam::sizeHintOffset(size.width, 0 - lineSizeUsed - marginX - prevLineSpace)),
                                layoutParam->sizeHint().height),
                            layoutParam->sizeParam());
                        childRequiredSize = zfmMax(
                            child->layoutMeasuredSize().height,
                            size.height - requiredSize - prevSpace - marginY);
                        break;
                    default:
                        zfCoreCriticalShouldNotGoHere();
                        return ;
                }
            }
            childRequiredSize += prevSpace + marginY;
            if(i > wrapIndex && requiredSize + childRequiredSize > size.height)
            {
                wrapIndexTmp = i;
                break;
            }

            if(layoutParam->layoutWeight() > 0)
            {
                flexableWrapSize += childRequiredSize;
            }
            requiredSize += childRequiredSize;
            lineSize = zfmMax(lineSize, child->layoutMeasuredSize().width + marginX);
            if(layoutParam->layoutWeight() > 0)
            {
                totalWeight += layoutParam->layoutWeight();
            }
            if(requiredSize >= size.height)
            {
                wrapIndexTmp = i + 1;
                break;
            }
        }
        hasLayoutedChild = zffalse;
        zfint flexibleSize = zfmMax(0, size.height - requiredSize + flexableWrapSize);
        zfint offset = (positiveDirection ? 0 : size.height);
        zfint lineX = ((_ZFP_ZFUIFlowLayout_layoutOrientationSecondary(
                parent->layoutOrientationMain(), parent->layoutOrientationSecondary()) == ZFUIOrientation::e_Left)
            ? lineSizeUsed + prevLineSpace
            : size.width - lineSizeUsed - lineSize - prevLineSpace);
        for(zfindex i = wrapIndex; i < wrapIndexTmp; ++i)
        {
            ZFUIView *child = parent->childAtIndex(i);
            ZFUIFlowLayoutParam *layoutParam = child->layoutParamT();
            if(!child->viewVisible() && !layoutParam->layoutReserveSpaceWhenNotVisible())
            {
                continue ;
            }
            zfint prevSpace = (hasLayoutedChild ? parent->layoutChildSpaceY() : 0);
            hasLayoutedChild = zftrue;

            zfint marginX = parentMarginX + ZFUIMarginGetX(layoutParam->layoutMargin());
            zfint marginY = parentMarginY + ZFUIMarginGetY(layoutParam->layoutMargin());
            zfint totalUsedSpace = prevSpace + marginY;
            if(layoutParam->layoutWeight() > 0)
            {
                child->layoutMeasure(
                    ZFUISizeMake(
                        ZFUIViewLayoutParam::sizeHintMerge(
                            layoutParam->sizeHint().width,
                            ZFUIViewLayoutParam::sizeHintOffset(size.width, 0 - lineSizeUsed - marginX - prevLineSpace)),
                        ZFUIViewLayoutParam::sizeHintMerge(flexibleSize * layoutParam->layoutWeight() / totalWeight, layoutParam->sizeHint().height)
                        ),
                    ZFUISizeParamMake(
                        layoutParam->sizeParam().width,
                        ZFUISizeType::e_Fill
                        ));
                flexibleSize -= child->layoutMeasuredSize().height;
                totalWeight -= layoutParam->layoutWeight();
            }
            else
            {
                if(layoutParam->sizeParam().height == ZFUISizeType::e_Fill)
                {
                    child->layoutMeasure(
                        ZFUISizeMake(
                            ZFUIViewLayoutParam::sizeHintMerge(
                                layoutParam->sizeHint().width,
                                ZFUIViewLayoutParam::sizeHintOffset(size.width, 0 - lineSizeUsed - marginX - prevLineSpace)),
                            ZFUIViewLayoutParam::sizeHintMerge(
                                layoutParam->sizeHint().height,
                                positiveDirection ? zfmMax(size.height - offset - totalUsedSpace, 0) : zfmMax(offset - totalUsedSpace, 0))
                            ),
                        layoutParam->sizeParam());
                }
            }
            if(positiveDirection)
            {
                child->layout(ZFUIAlignApply(
                    layoutParam->layoutAlign(),
                    ZFUIRectMake(lineX, offset + prevSpace, lineSize, child->layoutMeasuredSize().height + marginY),
                    child->layoutMeasuredSize(),
                    layoutParam->layoutMargin() + parent->layoutChildMargin()));
                offset += prevSpace + child->layoutMeasuredSize().height + marginY;
            }
            else
            {
                offset -= child->layoutMeasuredSize().height + marginY + prevSpace;
                child->layout(ZFUIAlignApply(
                    layoutParam->layoutAlign(),
                    ZFUIRectMake(lineX, offset, lineSize, child->layoutMeasuredSize().height + marginY),
                    child->layoutMeasuredSize(),
                    layoutParam->layoutMargin() + parent->layoutChildMargin()));
            }
        } // for(zfindex i = wrapIndex; i < wrapIndexTmp; ++i)
        wrapIndex = wrapIndexTmp;
        lineSizeUsed += lineSize + prevLineSpace;
    } // for each line
}
static ZFUISize _ZFP_ZFUIFlowLayout_measureVertical(ZF_IN ZFUIFlowLayout *parent,
                                                    ZF_IN const ZFUISize &sizeHint,
                                                    ZF_IN const ZFUISizeParam &sizeParam)
{
    ZFUISize ret = ZFUISizeZero;
    zfindex wrapIndex = 0;
    zfint lineSizeUsed = 0;
    while(wrapIndex < parent->childCount()) // for each line
    {
        ZFUISize lineSize = ZFUISizeZero;
        zfbool hasLayoutedChild = zffalse;
        zfint parentMarginX = ZFUIMarginGetX(parent->layoutChildMargin());
        zfint parentMarginY = ZFUIMarginGetY(parent->layoutChildMargin());
        zfint sizeHintLast = -1;
        zfindex wrapIndexTmp = -1;
        zfint prevLineSpace = (wrapIndex ? parent->layoutChildSpaceX() : 0);
        do
        {
            lineSize.height = 0;
            sizeHintLast = -1;
            wrapIndexTmp = parent->childCount();
            for(zfindex i = wrapIndex; i < parent->childCount(); ++i)
            {
                ZFUIView *child = parent->childAtIndex(i);
                ZFUIFlowLayoutParam *layoutParam = child->layoutParamT();
                if(!child->viewVisible() && !layoutParam->layoutReserveSpaceWhenNotVisible())
                {
                    continue ;
                }
                zfint prevSpace = (hasLayoutedChild ? parent->layoutChildSpaceY() : 0);
                hasLayoutedChild = zftrue;

                zfint marginX = parentMarginX + ZFUIMarginGetX(layoutParam->layoutMargin());
                zfint marginY = parentMarginY + ZFUIMarginGetY(layoutParam->layoutMargin());
                zfint sizeHintTmp = ZFUIViewLayoutParam::sizeHintMerge(
                    layoutParam->sizeHint().width,
                    ZFUIViewLayoutParam::sizeHintOffset(sizeHint.width, 0 - lineSizeUsed - marginX - prevLineSpace));
                if(sizeParam.width == ZFUISizeType::e_Wrap && layoutParam->sizeParam().width == ZFUISizeType::e_Fill)
                {
                    child->layoutMeasure(
                        ZFUISizeMake(sizeHintTmp, layoutParam->sizeHint().height),
                        ZFUISizeParamWrapWidthWrapHeight);
                    sizeHintTmp = child->layoutMeasuredSize().width;
                    if(sizeHintTmp < lineSize.width)
                    {
                        sizeHintTmp = lineSize.width;
                    }
                    if(sizeHintLast == -1)
                    {
                        sizeHintLast = sizeHintTmp;
                    }
                }
                child->layoutMeasure(
                    ZFUISizeMake(sizeHintTmp, layoutParam->sizeHint().height),
                    layoutParam->sizeParam());
                if(i > wrapIndex && sizeHint.height >= 0 && lineSize.height + prevSpace + child->layoutMeasuredSize().height + marginY > sizeHint.height)
                {
                    wrapIndexTmp = i;
                    break;
                }
                lineSize.height += prevSpace + child->layoutMeasuredSize().height + marginY;
                lineSize.width = zfmMax(lineSize.width, child->layoutMeasuredSize().width + marginX + prevLineSpace);

                if(layoutParam->sizeParam().height == ZFUISizeType::e_Fill
                    || (sizeHint.height >= 0 && lineSize.height == sizeHint.height))
                {
                    wrapIndexTmp = i + 1;
                    break;
                }
            }
        } while(sizeHintLast != -1 && sizeHintLast != lineSize.width);
        lineSizeUsed += lineSize.width;
        wrapIndex = wrapIndexTmp;
        ret.height = zfmMax(ret.height, lineSize.height);
        ret.width += lineSize.width;
    } // for each line
    return ret;
}