package com.d.music.widget.flowlayout;

import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;

import com.d.music.R;

/**
 * FlowLayout
 * Created by D on 2018/7/11.
 */
public class FlowLayout extends ViewGroup {
    private static final int ROW_COUNT = 4;
    private final float mLoadFactor = 1.75f;

    private int mWidth;
    private int mHeight;

    private float mPaddingLeft, mPaddingTop, mPaddingRight, mPaddingBottom;
    private float mDividerWidth, mDividerHeight;

    private FlowLayoutAdapter mAdapter;
    private SparseArray<View> mItemViews;

    public FlowLayout(Context context) {
        super(context);
        init();
    }

    public FlowLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        initAttr(context, attrs);
        init();
    }

    public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initAttr(context, attrs);
        init();
    }

    private void initAttr(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout);
        float padding = typedArray.getDimension(R.styleable.FlowLayout_flow_padding, 0);
        mPaddingLeft = typedArray.getDimension(R.styleable.FlowLayout_flow_paddingLeft, padding);
        mPaddingTop = typedArray.getDimension(R.styleable.FlowLayout_flow_paddingTop, padding);
        mPaddingRight = typedArray.getDimension(R.styleable.FlowLayout_flow_paddingRight, padding);
        mPaddingBottom = typedArray.getDimension(R.styleable.FlowLayout_flow_paddingBottom, padding);
        mDividerWidth = typedArray.getDimension(R.styleable.FlowLayout_flow_dividerWidth, 0);
        mDividerHeight = typedArray.getDimension(R.styleable.FlowLayout_flow_dividerHeight, 0);
        typedArray.recycle();
    }

    private void init() {
        this.mItemViews = new SparseArray<>();
    }

    private void reset() {
        removeAllViews();
        int recyclers = mItemViews.size();
        int size = mAdapter != null ? mAdapter.getCount() : 0;
        if (size <= 0) {
            mItemViews.clear();
        } else if (size + (int) (ROW_COUNT * mLoadFactor) < recyclers) {
            for (int i = size + (int) (ROW_COUNT * mLoadFactor); i < recyclers; i++) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                    mItemViews.removeAt(i);
                } else {
                    mItemViews.delete(i);
                }
            }
        }

        for (int i = 0; i < size; i++) {
            View child = mAdapter.getView(i, mItemViews.get(i), this);
            mItemViews.put(i, child);
            addChild(this, child);
        }
        requestLayout();
    }

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

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int count = getChildCount();
        final boolean measureMatchParentChildren = MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY;

        if (count <= 0 || measureMatchParentChildren) {
            // Not support mode
            super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY));
            return;
        }

        mWidth = MeasureSpec.getSize(widthMeasureSpec);
        int left = (int) mPaddingLeft;
        int top = (int) mPaddingTop;
        int maxHeightRow = 0;
        for (int i = 0; i < count; i++) {
            // Measure size for each child view in the ViewGroup
            final View child = getChildAt(i);
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();

            final int childWidthMeasureSpec;
            if (lp.width == LayoutParams.MATCH_PARENT) {
                final int width = Math.max(0, MeasureSpec.getSize(widthMeasureSpec));
                childWidthMeasureSpec = MeasureSpec.makeMeasureSpec((int) (width - mPaddingLeft - mPaddingRight),
                        MeasureSpec.EXACTLY);
            } else {
                childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                        (int) (mPaddingLeft + mPaddingRight), lp.width);
            }

            final int childHeightMeasureSpec;
            if (lp.height == LayoutParams.MATCH_PARENT) {
                final int height = Math.max(0, MeasureSpec.getSize(heightMeasureSpec));
                childHeightMeasureSpec = MeasureSpec.makeMeasureSpec((int) (height - mPaddingTop - mPaddingBottom),
                        MeasureSpec.EXACTLY);
            } else {
                childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
                        (int) (mPaddingTop + mPaddingBottom), lp.height);
            }

            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);

            final int width = child.getMeasuredWidth();
            final int height = child.getMeasuredHeight();
            if (!(left == (int) mPaddingLeft && width == mWidth - mPaddingLeft - mPaddingRight)
                    && left + width + mDividerWidth > mWidth - mPaddingRight) {
                left = (int) mPaddingLeft;
                top += maxHeightRow + mDividerHeight;
                maxHeightRow = 0;
            }
            left += width + mDividerWidth;
            maxHeightRow = Math.max(maxHeightRow, height);
        }
        final int totalHeight = (int) (top + maxHeightRow + mPaddingBottom);

        mHeight = MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY ? totalHeight
                : MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(mWidth, mHeight);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        final int count = getChildCount();
        if (count <= 0) {
            return;
        }

        int left = (int) mPaddingLeft;
        int top = (int) mPaddingTop;
        int maxHeightRow = 0;
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            final int width = child.getMeasuredWidth();
            final int height = child.getMeasuredHeight();
            if (!(left == (int) mPaddingLeft && width == mWidth - mPaddingLeft - mPaddingRight)
                    && left + width + mDividerWidth > mWidth - mPaddingRight) {
                left = (int) mPaddingLeft;
                top += maxHeightRow + mDividerHeight;
                maxHeightRow = 0;
            }
            // Layout horizontally for each child view in the ViewGroup
            child.layout(left, top, left + width, top + height);
            left += width + mDividerWidth;
            maxHeightRow = Math.max(maxHeightRow, height);
        }
    }

    private void addChild(ViewGroup group, View child) {
        if (group == null || child == null) {
            return;
        }
        if (child.getParent() != null) {
            ((ViewGroup) child.getParent()).removeView(child);
        }
        group.addView(child);
    }

    public void setAdapter(FlowLayoutAdapter adapter) {
        this.mAdapter = adapter;
        this.mAdapter.registerDataSetObserver(new FlowLayoutAdapter.DataSetObserver() {
            @Override
            public void notifyChanged() {
                reset();
            }
        });
        reset();
    }

    public static class LayoutParams extends MarginLayoutParams {

        public LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
        }

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

        public LayoutParams(MarginLayoutParams source) {
            super(source);
        }

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