/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.messaging.ui;

import android.content.Context;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import com.android.messaging.util.OsUtil;
import com.android.messaging.util.UiUtils;

import java.util.ArrayList;

/**
* A line-wrapping flow layout. Arranges children in horizontal flow, packing as many
* child views as possible on each line. When the current line does not
* have enough horizontal space, the layout continues on the next line.
*/
public class LineWrapLayout extends ViewGroup {
    public LineWrapLayout(Context context) {
        this(context, null);
    }

    public LineWrapLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int startPadding = UiUtils.getPaddingStart(this);
        final int endPadding = UiUtils.getPaddingEnd(this);
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        final int widthSize = MeasureSpec.getSize(widthMeasureSpec) - startPadding - endPadding;
        final boolean isFixedSize = (widthMode == MeasureSpec.EXACTLY);

        int height = 0;

        int childCount = getChildCount();
        int childWidthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.AT_MOST);

        int x = startPadding;
        int currLineWidth = 0;
        int currLineHeight = 0;
        int maxLineWidth = 0;

        for (int i = 0; i < childCount; i++) {
            View currChild = getChildAt(i);
            if (currChild.getVisibility() == GONE) {
                continue;
            }
            LayoutParams layoutParams = (LayoutParams) currChild.getLayoutParams();
            int startMargin = layoutParams.getStartMargin();
            int endMargin = layoutParams.getEndMargin();
            currChild.measure(childWidthSpec, MeasureSpec.UNSPECIFIED);
            int childMeasuredWidth = currChild.getMeasuredWidth() + startMargin + endMargin;
            int childMeasuredHeight = currChild.getMeasuredHeight() + layoutParams.topMargin +
                    layoutParams.bottomMargin;

            if ((x + childMeasuredWidth) > widthSize) {
                // New line. Update the overall height and reset trackers.
                height += currLineHeight;
                currLineHeight = 0;
                x = startPadding;
                currLineWidth = 0;
                startMargin = 0;
            }

            x += childMeasuredWidth;
            currLineWidth += childMeasuredWidth;
            currLineHeight = Math.max(currLineHeight, childMeasuredHeight);
            maxLineWidth = Math.max(currLineWidth, maxLineWidth);
        }
        // And account for the height of the last line.
        height += currLineHeight;

        int width = isFixedSize ? widthSize : maxLineWidth;
        setMeasuredDimension(width + startPadding + endPadding,
                height + getPaddingTop() + getPaddingBottom());
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        final int startPadding = UiUtils.getPaddingStart(this);
        final int endPadding = UiUtils.getPaddingEnd(this);
        int width = getWidth() - startPadding - endPadding;
        int y = getPaddingTop();
        int x = startPadding;
        int childCount = getChildCount();

        int currLineHeight = 0;

        // Do a dry-run first to get the line heights.
        final ArrayList<Integer> lineHeights = new ArrayList<Integer>();
        for (int i = 0; i < childCount; i++) {
            View currChild = getChildAt(i);
            if (currChild.getVisibility() == GONE) {
                continue;
            }
            LayoutParams layoutParams = (LayoutParams) currChild.getLayoutParams();
            int childWidth = currChild.getMeasuredWidth();
            int childHeight = currChild.getMeasuredHeight();
            int startMargin = layoutParams.getStartMargin();
            int endMargin = layoutParams.getEndMargin();

            if ((x + childWidth + startMargin + endMargin) > width) {
                // new line
                lineHeights.add(currLineHeight);
                currLineHeight = 0;
                x = startPadding;
                startMargin = 0;
            }
            currLineHeight = Math.max(currLineHeight, childHeight + layoutParams.topMargin +
                    layoutParams.bottomMargin);
            x += childWidth + startMargin + endMargin;
        }
        // Add the last line height.
        lineHeights.add(currLineHeight);

        // Now perform the actual layout.
        x = startPadding;
        currLineHeight = 0;
        int lineIndex = 0;
        for (int i = 0; i < childCount; i++) {
            View currChild = getChildAt(i);
            if (currChild.getVisibility() == GONE) {
                continue;
            }
            LayoutParams layoutParams = (LayoutParams) currChild.getLayoutParams();
            int childWidth = currChild.getMeasuredWidth();
            int childHeight = currChild.getMeasuredHeight();
            int startMargin = layoutParams.getStartMargin();
            int endMargin = layoutParams.getEndMargin();

            if ((x + childWidth + startMargin + endMargin) > width) {
                // new line
                y += currLineHeight;
                currLineHeight = 0;
                x = startPadding;
                startMargin = 0;
                lineIndex++;
            }
            final int startPositionX = x + startMargin;
            int startPositionY = y + layoutParams.topMargin;    // default to top gravity
            final int majorGravity = layoutParams.gravity & Gravity.VERTICAL_GRAVITY_MASK;
            if (majorGravity != Gravity.TOP && lineHeights.size() > lineIndex) {
                final int lineHeight = lineHeights.get(lineIndex);
                switch (majorGravity) {
                    case Gravity.BOTTOM:
                        startPositionY = y + lineHeight - childHeight - layoutParams.bottomMargin;
                        break;

                    case Gravity.CENTER_VERTICAL:
                        startPositionY = y + (lineHeight - childHeight) / 2;
                        break;
                }
            }

            if (OsUtil.isAtLeastJB_MR2() && getResources().getConfiguration()
                    .getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
                currChild.layout(width - startPositionX - childWidth, startPositionY,
                        width - startPositionX, startPositionY + childHeight);
            } else {
                currChild.layout(startPositionX, startPositionY, startPositionX + childWidth,
                        startPositionY + childHeight);
            }
            currLineHeight = Math.max(currLineHeight, childHeight + layoutParams.topMargin +
                    layoutParams.bottomMargin);
            x += childWidth + startMargin + endMargin;
        }
    }

    @Override
    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
        return new LayoutParams(p);
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new LayoutParams(getContext(), attrs);
    }

    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    }

    public static final class LayoutParams extends FrameLayout.LayoutParams {
        public LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
        }

        public LayoutParams(int width, int height) {
            super(width, height);
        }

        public LayoutParams(ViewGroup.LayoutParams source) {
            super(source);
        }

        public int getStartMargin() {
            if (OsUtil.isAtLeastJB_MR2()) {
                return getMarginStart();
            } else {
                return leftMargin;
            }
        }

        public int getEndMargin() {
            if (OsUtil.isAtLeastJB_MR2()) {
                return getMarginEnd();
            } else {
                return rightMargin;
            }
        }
    }
}
