package com.phl.refresh.impl;

import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.graphics.PointF;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CoordinatorLayout;
import android.support.v4.view.NestedScrollingChild;
import android.support.v4.view.NestedScrollingParent;
import android.support.v4.view.ScrollingView;
import android.support.v4.view.ViewPager;
import android.support.v4.widget.NestedScrollView;
import android.support.v4.widget.Space;
import android.support.v7.widget.RecyclerView;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.widget.AbsListView;
import android.widget.FrameLayout;
import android.widget.ListView;
import android.widget.ScrollView;

import com.phl.refresh.api.RefreshContent;
import com.phl.refresh.api.RefreshKernel;
import com.phl.refresh.api.RefreshLayout;
import com.phl.refresh.api.ScrollBoundaryDecider;

import java.util.Collections;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import static com.phl.refresh.util.ScrollBoundaryUtil.canScrollDown;
import static com.phl.refresh.util.ScrollBoundaryUtil.isTransformedTouchPointInView;

import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;

/**
 * 刷新内容包装
 * Created by SCWANG on 2017/5/26.
 */
@SuppressWarnings("WeakerAccess")
public class RefreshContentWrapper implements RefreshContent {

    protected int mHeaderHeight = Integer.MAX_VALUE;
    protected int mFooterHeight = mHeaderHeight - 1;
    protected View mContentView;//直接内容视图
    protected View mRealContentView;//被包裹的原真实视图
    protected View mScrollableView;
    protected View mFixedHeader;
    protected View mFixedFooter;
    protected boolean mEnableRefresh = true;
    protected boolean mEnableLoadmore = true;
    protected MotionEvent mMotionEvent;
    protected ScrollBoundaryDeciderAdapter mBoundaryAdapter = new ScrollBoundaryDeciderAdapter();

    public RefreshContentWrapper(View view) {
        this.mContentView = mRealContentView = view;
    }

    public RefreshContentWrapper(Context context) {
        this.mContentView = mRealContentView = new View(context);
    }

    //<editor-fold desc="findScrollableView">
    protected void findScrollableView(View content, RefreshKernel kernel) {
        mScrollableView = null;
        while (mScrollableView == null || (mScrollableView instanceof NestedScrollingParent
                && !(mScrollableView instanceof NestedScrollingChild))) {
            content = findScrollableViewInternal(content, mScrollableView == null);
            if (content == mScrollableView) {
                break;
            }
            try {//try 不能删除,不然会出现兼容性问题
                if (content instanceof CoordinatorLayout) {
                    kernel.getRefreshLayout().setEnableNestedScroll(false);
                    wrapperCoordinatorLayout(((CoordinatorLayout) content), kernel.getRefreshLayout());
                }
            } catch (Throwable ignored) {
            }
            mScrollableView = content;
        }
    }

    protected void wrapperCoordinatorLayout(CoordinatorLayout layout, final RefreshLayout refreshLayout) {
        for (int i = layout.getChildCount() - 1; i >= 0; i--) {
            View view = layout.getChildAt(i);
            if (view instanceof AppBarLayout) {
                ((AppBarLayout) view).addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
                    @Override
                    public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
                        mEnableRefresh = verticalOffset >= 0;
                        mEnableLoadmore = refreshLayout.isEnableLoadmore() && (appBarLayout.getTotalScrollRange() + verticalOffset) <= 0;
                    }
                });
            }
        }
    }

    protected View findScrollableViewInternal(View content, boolean selfable) {
        View scrollableView = null;
        Queue<View> views = new LinkedBlockingQueue<>(Collections.singletonList(content));
        while (!views.isEmpty() && scrollableView == null) {
            View view = views.poll();
            if (view != null) {
                if ((selfable || view != content) && isScrollableView(view)) {
                    scrollableView = view;
                } else if (view instanceof ViewGroup) {
                    ViewGroup group = (ViewGroup) view;
                    for (int j = 0; j < group.getChildCount(); j++) {
                        views.add(group.getChildAt(j));
                    }
                }
            }
        }
        return scrollableView == null ? content : scrollableView;
    }

    protected boolean isScrollableView(View view) {
        return view instanceof AbsListView
                || view instanceof ScrollView
                || view instanceof ScrollingView
                || view instanceof NestedScrollingChild
                || view instanceof NestedScrollingParent
                || view instanceof WebView
                || view instanceof ViewPager;
    }

    protected View findScrollableViewByEvent(View content, MotionEvent event, View orgScrollableView) {
        if (content instanceof ViewGroup && event != null) {
            ViewGroup viewGroup = (ViewGroup) content;
            final int childCount = viewGroup.getChildCount();
            PointF point = new PointF();
            for (int i = childCount; i > 0; i--) {
                View child = viewGroup.getChildAt(i - 1);
                if (isTransformedTouchPointInView(viewGroup, child, event.getX(), event.getY(), point)) {
                    if (!(child instanceof ViewPager) && isScrollableView(child)) {
                        return child;
                    } else {
                        event = MotionEvent.obtain(event);
                        event.offsetLocation(point.x, point.y);
                        return findScrollableViewByEvent(child, event, orgScrollableView);
                    }
                }
            }
        }
        return orgScrollableView;
    }
    //</editor-fold>

    //<editor-fold desc="implements">
    @NonNull
    public View getView() {
        return mContentView;
    }

    @Override
    public void moveSpinner(int spinner) {
        mRealContentView.setTranslationY(spinner);
        if (mFixedHeader != null) {
            mFixedHeader.setTranslationY(Math.max(0, spinner));
        }
        if (mFixedFooter != null) {
            mFixedFooter.setTranslationY(Math.min(0, spinner));
        }
    }

    @Override
    public boolean canRefresh() {
        return mEnableRefresh && mBoundaryAdapter.canRefresh(mContentView);
    }

    @Override
    public boolean canLoadmore() {
        return mEnableLoadmore && mBoundaryAdapter.canLoadmore(mContentView);
    }

    @Override
    public void measure(int widthSpec, int heightSpec) {
        mContentView.measure(widthSpec, heightSpec);
    }

    @Override
    public ViewGroup.LayoutParams getLayoutParams() {
        return mContentView.getLayoutParams();
    }

    @Override
    public int getMeasuredWidth() {
        return mContentView.getMeasuredWidth();
    }

    @Override
    public int getMeasuredHeight() {
        return mContentView.getMeasuredHeight();
    }

    @Override
    public void layout(int left, int top, int right, int bottom, boolean skip) {
        if (!skip
                || mContentView.getLeft() != left
                || mContentView.getTop() != top
                || mContentView.getRight() != right
                || mContentView.getBottom() != bottom) {
            mContentView.layout(left, top, right, bottom);
        }
    }

    @Override
    public View getScrollableView() {
        return mScrollableView;
    }

    @Override
    public void onActionDown(MotionEvent e) {
        mMotionEvent = MotionEvent.obtain(e);
        mMotionEvent.offsetLocation(-mContentView.getLeft(), -mContentView.getTop());
        mBoundaryAdapter.setActionEvent(mMotionEvent);
        mScrollableView = findScrollableViewByEvent(mContentView, mMotionEvent, mScrollableView);
    }

    @Override
    public void onActionUpOrCancel() {
        mMotionEvent = null;
    }

    @Override
    public boolean fling(int velocity) {
        if (mScrollableView instanceof ScrollView) {
            ((ScrollView) mScrollableView).fling(velocity);
        } else if (mScrollableView instanceof AbsListView) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                ((AbsListView) mScrollableView).fling(velocity);
            }
        } else if (mScrollableView instanceof WebView) {
            ((WebView) mScrollableView).flingScroll(0, velocity);
        } else if (mScrollableView instanceof RecyclerView) {
            ((RecyclerView) mScrollableView).fling(0, velocity);
        } else if (mScrollableView instanceof NestedScrollView) {
            ((NestedScrollView) mScrollableView).fling(velocity);
        } else {
            return false;
        }
        return true;
    }

    @Override
    public void setupComponent(RefreshKernel kernel, View fixedHeader, View fixedFooter) {
        findScrollableView(mContentView, kernel);

        if (fixedHeader != null || fixedFooter != null) {
            mFixedHeader = fixedHeader;
            mFixedFooter = fixedFooter;
            FrameLayout frameLayout = new FrameLayout(mContentView.getContext());
            kernel.getRefreshLayout().getLayout().removeView(mContentView);
            ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
            frameLayout.addView(mContentView, MATCH_PARENT, MATCH_PARENT);
            kernel.getRefreshLayout().getLayout().addView(frameLayout, layoutParams);
            mContentView = frameLayout;
            if (fixedHeader != null) {
                fixedHeader.setClickable(true);
                ViewGroup.LayoutParams lp = fixedHeader.getLayoutParams();
                ViewGroup parent = (ViewGroup) fixedHeader.getParent();
                int index = parent.indexOfChild(fixedHeader);
                parent.removeView(fixedHeader);
                lp.height = measureViewHeight(fixedHeader);
                parent.addView(new Space(mContentView.getContext()), index, lp);
                frameLayout.addView(fixedHeader);
            }
            if (fixedFooter != null) {
                fixedFooter.setClickable(true);
                ViewGroup.LayoutParams lp = fixedFooter.getLayoutParams();
                ViewGroup parent = (ViewGroup) fixedFooter.getParent();
                int index = parent.indexOfChild(fixedFooter);
                parent.removeView(fixedFooter);
                FrameLayout.LayoutParams flp = new FrameLayout.LayoutParams(lp);
                lp.height = measureViewHeight(fixedFooter);
                parent.addView(new Space(mContentView.getContext()), index, lp);
                flp.gravity = Gravity.BOTTOM;
                frameLayout.addView(fixedFooter, flp);
            }
        }
    }

    @Override
    public void onInitialHeaderAndFooter(int headerHeight, int footerHeight) {
        mHeaderHeight = headerHeight;
        mFooterHeight = footerHeight;
    }

    @Override
    public void setScrollBoundaryDecider(ScrollBoundaryDecider boundary) {
        if (boundary instanceof ScrollBoundaryDeciderAdapter) {
            mBoundaryAdapter = ((ScrollBoundaryDeciderAdapter) boundary);
        } else {
            mBoundaryAdapter.setScrollBoundaryDecider(boundary);
        }
    }

    @Override
    public void setEnableLoadmoreWhenContentNotFull(boolean enable) {
        mBoundaryAdapter.setEnableLoadmoreWhenContentNotFull(enable);
    }

    @Override
    public AnimatorUpdateListener onLoadingFinish(final RefreshKernel kernel, final int footerHeight, int startDelay, final int duration) {
        if (mScrollableView != null && kernel.getRefreshLayout().isEnableScrollContentWhenLoaded()) {
            if (!canScrollDown(mScrollableView)) {
                return null;
            }
            if (mScrollableView instanceof AbsListView && !(mScrollableView instanceof ListView) && Build.VERSION.SDK_INT < 19) {
                if (startDelay > 0) {
                    kernel.getRefreshLayout().getLayout().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            ((AbsListView) mScrollableView).smoothScrollBy(footerHeight, duration);
                        }
                    }, startDelay);
                } else {
                    ((AbsListView) mScrollableView).smoothScrollBy(footerHeight, duration);
                }
                return null;
            }
            return new AnimatorUpdateListener() {
                int lastValue = kernel.getSpinner();
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    int value = (int) animation.getAnimatedValue();
                    try {
                        if (mScrollableView instanceof ListView) {
                            if (Build.VERSION.SDK_INT >= 19) {
                                ((ListView) RefreshContentWrapper.this.mScrollableView).scrollListBy(value - lastValue);
                            } else {
//                                ListViewCompat.scrollListBy((ListView) mScrollableView, value - lastValue);
                                ListView listView = (ListView) RefreshContentWrapper.this.mScrollableView;
                                final int firstPosition = listView.getFirstVisiblePosition();
                                if (firstPosition == ListView.INVALID_POSITION) {
                                    return;
                                }
                                final View firstView = listView.getChildAt(0);
                                if (firstView == null) {
                                    return;
                                }
                                final int newTop = firstView.getTop() - (value - lastValue);
                                listView.setSelectionFromTop(firstPosition, newTop);
                            }
                        } else {
                            mScrollableView.scrollBy(0, value - lastValue);
                        }
                    } catch (Throwable ignored) {
                        //根据用户反馈,此处可能会有BUG
                    }
                    lastValue = value;
                }
            };
        }
        return null;
    }
    //</editor-fold>

    //<editor-fold desc="protected">
    protected static int measureViewHeight(View view) {
        ViewGroup.LayoutParams p = view.getLayoutParams();
        if (p == null) {
            p = new ViewGroup.LayoutParams(MATCH_PARENT,WRAP_CONTENT);
        }
        int childHeightSpec;
        int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0, p.width);
        if (p.height > 0) {
            childHeightSpec = MeasureSpec.makeMeasureSpec(p.height, MeasureSpec.EXACTLY);
        } else {
            childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
        }
        view.measure(childWidthSpec, childHeightSpec);
        return view.getMeasuredHeight();
    }
    //</editor-fold>

}