package com.lcodecore.tkrefreshlayout;

import static androidx.customview.widget.ViewDragHelper.INVALID_POINTER;

import android.content.Context;
import android.content.res.TypedArray;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;

import com.lcodecore.tkrefreshlayout.Footer.BallPulseView;
import com.lcodecore.tkrefreshlayout.header.GoogleDotView;
import com.lcodecore.tkrefreshlayout.processor.AnimProcessor;
import com.lcodecore.tkrefreshlayout.processor.IDecorator;
import com.lcodecore.tkrefreshlayout.processor.OverScrollDecorator;
import com.lcodecore.tkrefreshlayout.processor.RefreshProcessor;
import com.lcodecore.tkrefreshlayout.utils.DensityUtil;
import java.lang.reflect.Constructor;

import androidx.core.view.MotionEventCompat;
import androidx.core.view.NestedScrollingChild;
import androidx.core.view.NestedScrollingChildHelper;
import androidx.core.view.ViewCompat;

/**
 * Created by lcodecore on 16/3/2.
 */
public class TwinklingRefreshLayout extends RelativeLayout implements PullListener, NestedScrollingChild {

    //波浪的高度,最大扩展高度
    protected float mMaxHeadHeight;
    protected float mMaxBottomHeight;

    //头部的高度
    protected float mHeadHeight;

    //允许的越界回弹的高度
    protected float mOverScrollHeight;

    //子控件
    private View mChildView;

    //头部layout
    protected FrameLayout mHeadLayout;

    //整个头部
    private FrameLayout mExtraHeadLayout;
    //附加顶部高度
    private int mExHeadHeight = 0;

    private IHeaderView mHeadView;
    private IBottomView mBottomView;

    //设置的默认的header/footer class的完整包名+类名
    private static String HEADER_CLASS_NAME = "";
    private static String FOOTER_CLASS_NAME = "";

    //底部高度
    private float mBottomHeight;

    //底部layout
    private FrameLayout mBottomLayout;


    //是否刷新视图可见
    protected boolean isRefreshVisible = false;

    //是否加载更多视图可见
    protected boolean isLoadingVisible = false;

    //是否处于刷新状态（和isRefreshVisible区别为是否开启了enableKeepHeadWhenRefresh）
    protected boolean isRefreshing = false;
    protected boolean isLoadingMore = false;

    //是否需要加载更多,默认需要
    protected boolean enableLoadmore = true;
    //是否需要下拉刷新,默认需要
    protected boolean enableRefresh = true;

    //是否在越界回弹的时候显示下拉图标
    protected boolean isOverScrollTopShow = true;
    //是否在越界回弹的时候显示上拉图标
    protected boolean isOverScrollBottomShow = true;

    //是否隐藏刷新控件,开启越界回弹模式(开启之后刷新控件将隐藏)
    protected boolean isPureScrollModeOn = false;

    //是否自动加载更多
    protected boolean autoLoadMore = false;

    //是否开启悬浮刷新模式
    protected boolean floatRefresh = false;

    //是否允许进入越界回弹模式
    protected boolean enableOverScroll = true;

    //是否在刷新或者加载更多后保持状态
    protected boolean enableKeepIView = true;

    //是否在越界且处于刷新时直接显示顶部
    protected boolean showRefreshingWhenOverScroll = true;

    //是否在越界且处于加载更多时直接显示底部
    protected boolean showLoadingWhenOverScroll = true;

    private CoContext cp;
    private final int mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
    //设置手势动作的监听器
    private PullListener pullListener = this;

    private final NestedScrollingChildHelper mChildHelper;

    public TwinklingRefreshLayout(Context context) {
        this(context, null, 0);
    }

    public TwinklingRefreshLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public TwinklingRefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TwinklingRefreshLayout, defStyleAttr, 0);
        try {
            mMaxHeadHeight = a.getDimensionPixelSize(R.styleable.TwinklingRefreshLayout_tr_max_head_height, (int) DensityUtil.dp2px(context, 120));
            mHeadHeight = a.getDimensionPixelSize(R.styleable.TwinklingRefreshLayout_tr_head_height, (int) DensityUtil.dp2px(context, 80));
            mMaxBottomHeight = a.getDimensionPixelSize(R.styleable.TwinklingRefreshLayout_tr_max_bottom_height, (int) DensityUtil.dp2px(context, 120));
            mBottomHeight = a.getDimensionPixelSize(R.styleable.TwinklingRefreshLayout_tr_bottom_height, (int) DensityUtil.dp2px(context, 60));
            mOverScrollHeight = a.getDimensionPixelSize(R.styleable.TwinklingRefreshLayout_tr_overscroll_height, (int) mHeadHeight);
            enableRefresh = a.getBoolean(R.styleable.TwinklingRefreshLayout_tr_enable_refresh, true);
            enableLoadmore = a.getBoolean(R.styleable.TwinklingRefreshLayout_tr_enable_loadmore, true);
            isPureScrollModeOn = a.getBoolean(R.styleable.TwinklingRefreshLayout_tr_pureScrollMode_on, false);
            isOverScrollTopShow = a.getBoolean(R.styleable.TwinklingRefreshLayout_tr_overscroll_top_show, true);
            isOverScrollBottomShow = a.getBoolean(R.styleable.TwinklingRefreshLayout_tr_overscroll_bottom_show, true);
            enableOverScroll = a.getBoolean(R.styleable.TwinklingRefreshLayout_tr_enable_overscroll, true);
            floatRefresh = a.getBoolean(R.styleable.TwinklingRefreshLayout_tr_floatRefresh, false);
            autoLoadMore = a.getBoolean(R.styleable.TwinklingRefreshLayout_tr_autoLoadMore, false);
            enableKeepIView = a.getBoolean(R.styleable.TwinklingRefreshLayout_tr_enable_keepIView, true);
            showRefreshingWhenOverScroll = a.getBoolean(R.styleable.TwinklingRefreshLayout_tr_showRefreshingWhenOverScroll, true);
            showLoadingWhenOverScroll = a.getBoolean(R.styleable.TwinklingRefreshLayout_tr_showLoadingWhenOverScroll, true);
        } finally {
            a.recycle();
        }

        cp = new CoContext();

        addHeader();
        addFooter();

        setFloatRefresh(floatRefresh);
        setAutoLoadMore(autoLoadMore);
        setEnableRefresh(enableRefresh);
        setEnableLoadmore(enableLoadmore);

        mChildHelper = new NestedScrollingChildHelper(this);
        setNestedScrollingEnabled(true);
    }

    private void addHeader() {
        FrameLayout headViewLayout = new FrameLayout(getContext());
        LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, 0);
        layoutParams.addRule(ALIGN_PARENT_TOP);

        FrameLayout extraHeadLayout = new FrameLayout(getContext());
        extraHeadLayout.setId(R.id.ex_header);
        LayoutParams layoutParams2 = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);

        this.addView(extraHeadLayout, layoutParams2);
        this.addView(headViewLayout, layoutParams);

        mExtraHeadLayout = extraHeadLayout;
        mHeadLayout = headViewLayout;

        if (mHeadView == null) {
            if (!TextUtils.isEmpty(HEADER_CLASS_NAME)) {
                try {
                    Class headClazz = Class.forName(HEADER_CLASS_NAME);
                    Constructor ct = headClazz.getDeclaredConstructor(Context.class);
                    setHeaderView((IHeaderView) ct.newInstance(getContext()));
                } catch (Exception e) {
                    Log.e("TwinklingRefreshLayout:", "setDefaultHeader classname=" + e.getMessage());
                    setHeaderView(new GoogleDotView(getContext()));
                }
            } else {
                setHeaderView(new GoogleDotView(getContext()));
            }
        }
    }

    private void addFooter() {
        FrameLayout bottomViewLayout = new FrameLayout(getContext());
        LayoutParams layoutParams2 = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0);
        layoutParams2.addRule(ALIGN_PARENT_BOTTOM);
        bottomViewLayout.setLayoutParams(layoutParams2);

        mBottomLayout = bottomViewLayout;
        this.addView(mBottomLayout);

        if (mBottomView == null) {
            if (!TextUtils.isEmpty(FOOTER_CLASS_NAME)) {
                try {
                    Class clazz = Class.forName(FOOTER_CLASS_NAME);
                    Constructor ct = clazz.getDeclaredConstructor(Context.class);
                    setBottomView((IBottomView) ct.newInstance(getContext()));
                } catch (Exception e) {
                    Log.e("TwinklingRefreshLayout:", "setDefaultFooter classname=" + e.getMessage());
                    setBottomView(new BallPulseView(getContext()));
                }
            } else {
                setBottomView(new BallPulseView(getContext()));
            }
        }
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        //获得子控件
        //onAttachedToWindow方法中mChildView始终是第0个child，把header、footer放到构造函数中，mChildView最后被inflate
        mChildView = getChildAt(3);

        cp.init();
        decorator = new OverScrollDecorator(cp, new RefreshProcessor(cp));
        initGestureDetector();
    }

    private IDecorator decorator;
    private OnGestureListener listener;

    private void initGestureDetector() {
        listener = new OnGestureListener() {
            @Override
            public void onDown(MotionEvent ev) {
                decorator.onFingerDown(ev);
            }

            @Override
            public void onScroll(MotionEvent downEvent, MotionEvent currentEvent, float distanceX, float distanceY) {
                decorator.onFingerScroll(downEvent, currentEvent, distanceX, distanceY, vx, vy);
            }

            @Override
            public void onUp(MotionEvent ev, boolean isFling) {
                decorator.onFingerUp(ev, isFling);
            }

            @Override
            public void onFling(MotionEvent downEvent, MotionEvent upEvent, float velocityX, float velocityY) {
                decorator.onFingerFling(downEvent, upEvent, velocityX, velocityY);
            }
        };
    }

    //VelocityX,VelocityY
    private float vx, vy;

    private VelocityTracker mVelocityTracker;
    private float mLastFocusX;
    private float mLastFocusY;
    private float mDownFocusX;
    private float mDownFocusY;
    private int mMaximumFlingVelocity = ViewConfiguration.getMaximumFlingVelocity();
    private int mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity();
    private MotionEvent mCurrentDownEvent;
    private boolean mAlwaysInTapRegion;
    private int mTouchSlopSquare = mTouchSlop * mTouchSlop;

    private void detectGesture(MotionEvent ev, OnGestureListener listener) {
        final int action = ev.getAction();

        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(ev);

        final boolean pointerUp =
                (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP;
        final int skipIndex = pointerUp ? ev.getActionIndex() : -1;

        // Determine focal point
        float sumX = 0, sumY = 0;
        final int count = ev.getPointerCount();
        for (int i = 0; i < count; i++) {
            if (skipIndex == i) continue;
            sumX += ev.getX(i);
            sumY += ev.getY(i);
        }
        final int div = pointerUp ? count - 1 : count;
        final float focusX = sumX / div;
        final float focusY = sumY / div;

        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_POINTER_DOWN:
                mDownFocusX = mLastFocusX = focusX;
                mDownFocusY = mLastFocusY = focusY;
                break;
            case MotionEvent.ACTION_POINTER_UP:
                mDownFocusX = mLastFocusX = focusX;
                mDownFocusY = mLastFocusY = focusY;

                // Check the dot product of current velocities.
                // If the pointer that left was opposing another velocity vector, clear.
                mVelocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
                final int upIndex = ev.getActionIndex();
                final int id1 = ev.getPointerId(upIndex);
                final float x1 = mVelocityTracker.getXVelocity(id1);
                final float y1 = mVelocityTracker.getYVelocity(id1);
                for (int i = 0; i < count; i++) {
                    if (i == upIndex) continue;

                    final int id2 = ev.getPointerId(i);
                    final float x = x1 * mVelocityTracker.getXVelocity(id2);
                    final float y = y1 * mVelocityTracker.getYVelocity(id2);

                    final float dot = x + y;
                    if (dot < 0) {
                        mVelocityTracker.clear();
                        break;
                    }
                }
                break;
            case MotionEvent.ACTION_DOWN:
                mDownFocusX = mLastFocusX = focusX;
                mDownFocusY = mLastFocusY = focusY;
                if (mCurrentDownEvent != null) {
                    mCurrentDownEvent.recycle();
                }
                mCurrentDownEvent = MotionEvent.obtain(ev);
                mAlwaysInTapRegion = true;
                listener.onDown(ev);
                break;
            case MotionEvent.ACTION_MOVE:
                final float scrollX = mLastFocusX - focusX;
                final float scrollY = mLastFocusY - focusY;
                if (mAlwaysInTapRegion) {
                    final int deltaX = (int) (focusX - mDownFocusX);
                    final int deltaY = (int) (focusY - mDownFocusY);
                    int distance = (deltaX * deltaX) + (deltaY * deltaY);
                    if (distance > mTouchSlopSquare) {
                        listener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
                        mLastFocusX = focusX;
                        mLastFocusY = focusY;
                        mAlwaysInTapRegion = false;
                    }
                } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
                    listener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
                    mLastFocusX = focusX;
                    mLastFocusY = focusY;
                }
                break;
            case MotionEvent.ACTION_UP:
                final int pointerId = ev.getPointerId(0);
                mVelocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
                vy = mVelocityTracker.getYVelocity(pointerId);
                vx = mVelocityTracker.getXVelocity(pointerId);

                boolean isFling = false;
                if ((Math.abs(vy) > mMinimumFlingVelocity)
                        || (Math.abs(vx) > mMinimumFlingVelocity)) {
                    listener.onFling(mCurrentDownEvent, ev, vx, vy);
                    isFling = true;
                }

                listener.onUp(ev, isFling);

                if (mVelocityTracker != null) {
                    mVelocityTracker.recycle();
                    mVelocityTracker = null;
                }
                break;
            case MotionEvent.ACTION_CANCEL:
                mAlwaysInTapRegion = false;
                if (mVelocityTracker != null) {
                    mVelocityTracker.recycle();
                    mVelocityTracker = null;
                }
                break;
        }
    }

    /************************************* 触摸事件处理 *****************************************/
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        //1.监听fling动作 2.获取手指滚动速度（存在滚动但非fling的状态）3.分发事件
        boolean consume = decorator.dispatchTouchEvent(ev);
        detectGesture(ev, listener);
        detectNestedScroll(ev);
        return consume;
    }

    /**
     * 拦截事件
     *
     * @return return true时,ViewGroup的事件有效,执行onTouchEvent事件
     * return false时,事件向下传递,onTouchEvent无效
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean intercept = decorator.interceptTouchEvent(ev);
        return intercept || super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent e) {
        boolean consume = decorator.dealTouchEvent(e);
        return consume || super.onTouchEvent(e);
    }

    private final int[] mScrollOffset = new int[2];
    private final int[] mScrollConsumed = new int[2];
    private final int[] mNestedOffsets = new int[2];
    private int mActivePointerId = INVALID_POINTER;
    private int mLastTouchX;
    private int mLastTouchY;
    private boolean mIsBeingDragged;

    private boolean detectNestedScroll(MotionEvent e) {
        final MotionEvent vtev = MotionEvent.obtain(e);
        final int action = MotionEventCompat.getActionMasked(e);
        final int actionIndex = MotionEventCompat.getActionIndex(e);

        if (action == MotionEvent.ACTION_DOWN) {
            mNestedOffsets[0] = mNestedOffsets[1] = 0;
        }
        vtev.offsetLocation(mNestedOffsets[0], mNestedOffsets[1]);

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mActivePointerId = e.getPointerId(0);
                mLastTouchX = (int) e.getX();
                mLastTouchY = (int) e.getY();
                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
                break;
            case MotionEventCompat.ACTION_POINTER_DOWN:
                mActivePointerId = e.getPointerId(actionIndex);
                mLastTouchX = (int) e.getX(actionIndex);
                mLastTouchY = (int) e.getY(actionIndex);
                break;
            case MotionEvent.ACTION_MOVE:
                final int index = e.findPointerIndex(mActivePointerId);
                if (index < 0) {
                    Log.e("TwinklingRefreshLayout", "Error processing scroll; pointer index for id " +
                            mActivePointerId + " not found. Did any MotionEvents get skipped?");
                    return false;
                }

                final int x = (int) e.getX(index);
                final int y = (int) e.getY(index);

                int dx = mLastTouchX - x;
                int dy = mLastTouchY - y;

                if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) {
                    dx -= mScrollConsumed[0];
                    dy -= mScrollConsumed[1];
                    vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
                    // Updated the nested offsets
                    mNestedOffsets[0] += mScrollOffset[0];
                    mNestedOffsets[1] += mScrollOffset[1];
                }

                if (!mIsBeingDragged && Math.abs(dy) > mTouchSlop) {
                    final ViewParent parent = getParent();
                    if (parent != null) {
                        parent.requestDisallowInterceptTouchEvent(true);
                    }
                    mIsBeingDragged = true;
                    if (dy > 0) {
                        dy -= mTouchSlop;
                    } else {
                        dy += mTouchSlop;
                    }
                }

                if (mIsBeingDragged) {
                    mLastTouchY = y - mScrollOffset[1];

                    final int scrolledDeltaY = 0;
                    final int unconsumedY = dy - scrolledDeltaY;
                    if (dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, mScrollOffset)) {
                        mLastTouchX -= mScrollOffset[0];
                        mLastTouchY -= mScrollOffset[1];
                        vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
                        mNestedOffsets[0] += mScrollOffset[0];
                        mNestedOffsets[1] += mScrollOffset[1];
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                stopNestedScroll();
                mIsBeingDragged = false;
                mActivePointerId = INVALID_POINTER;
                break;
        }
        vtev.recycle();
        return true;
    }

    //NestedScroll
    @Override
    public void setNestedScrollingEnabled(boolean enabled) {
        mChildHelper.setNestedScrollingEnabled(enabled);
    }

    @Override
    public boolean isNestedScrollingEnabled() {
        return mChildHelper.isNestedScrollingEnabled();
    }

    @Override
    public boolean startNestedScroll(int axes) {
        return mChildHelper.startNestedScroll(axes);
    }

    @Override
    public void stopNestedScroll() {
        mChildHelper.stopNestedScroll();
    }

    @Override
    public boolean hasNestedScrollingParent() {
        return mChildHelper.hasNestedScrollingParent();
    }

    @Override
    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
        return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
    }

    @Override
    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
        return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
    }

    @Override
    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
        return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
    }

    @Override
    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
        return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);
    }

    /************************************** 开放api区  *****************************************/
    //设置默认的header class 名
    public static void setDefaultHeader(String className) {
        HEADER_CLASS_NAME = className;
    }

    //设置默认的footer class 名
    public static void setDefaultFooter(String className) {
        FOOTER_CLASS_NAME = className;
    }

    //主动刷新
    public void startRefresh() {
        cp.startRefresh();
    }

    //主动加载跟多
    public void startLoadMore() {
        cp.startLoadMore();
    }

    /**
     * 结束刷新
     */
    public void finishRefreshing() {
        cp.finishRefreshing();
    }

    /**
     * 结束加载更多
     */
    public void finishLoadmore() {
        cp.finishLoadmore();
    }

    /**
     * 手动设置刷新View
     */
    public void setTargetView(View targetView) {
        if (targetView != null) mChildView = targetView;
    }

    /**
     * 手动设置RefreshLayout的装饰器
     */
    public void setDecorator(IDecorator decorator1) {
        if (decorator1 != null) decorator = decorator1;
    }

    /**
     * 设置头部刷新View
     */
    public void setHeaderView(final IHeaderView headerView) {
        if (headerView != null) {
            mHeadLayout.removeAllViewsInLayout();
            mHeadLayout.addView(headerView.getView());
            mHeadView = headerView;
        }
    }

    /**
     * 设置固定在顶部的header
     */
    @Deprecated
    public void addFixedExHeader(final View view) {
        if (view != null && mExtraHeadLayout != null) {
            mExtraHeadLayout.addView(view);
            mExtraHeadLayout.bringToFront();
            if (floatRefresh) mHeadLayout.bringToFront();
            cp.onAddExHead();
            cp.setExHeadFixed();
        }
    }

    /**
     * 获取额外附加的头部
     */
    public View getExtraHeaderView() {
        return mExtraHeadLayout;
    }

    /**
     * 设置底部View
     */
    public void setBottomView(final IBottomView bottomView) {
        if (bottomView != null) {
            mBottomLayout.removeAllViewsInLayout();
            mBottomLayout.addView(bottomView.getView());
            mBottomView = bottomView;
        }
    }

    public void setFloatRefresh(boolean ifOpenFloatRefreshMode) {
        floatRefresh = ifOpenFloatRefreshMode;
        if (!floatRefresh) return;
        post(new Runnable() {
            @Override
            public void run() {
                if (mHeadLayout != null) mHeadLayout.bringToFront();
            }
        });
    }

    /**
     * 设置wave的下拉高度
     */
    public void setMaxHeadHeight(float maxHeightDp) {
        this.mMaxHeadHeight = DensityUtil.dp2px(getContext(), maxHeightDp);
    }

    /**
     * 设置下拉头的高度
     */
    public void setHeaderHeight(float headHeightDp) {
        this.mHeadHeight = DensityUtil.dp2px(getContext(), headHeightDp);
    }

    public void setMaxBottomHeight(float maxBottomHeight) {
        mMaxBottomHeight = DensityUtil.dp2px(getContext(), maxBottomHeight);
    }

    /**
     * 设置底部高度
     */
    public void setBottomHeight(float bottomHeightDp) {
        this.mBottomHeight = DensityUtil.dp2px(getContext(), bottomHeightDp);
    }

    /**
     * 是否允许加载更多
     */
    public void setEnableLoadmore(boolean enableLoadmore1) {
        enableLoadmore = enableLoadmore1;
        if (mBottomView != null) {
            if (enableLoadmore) mBottomView.getView().setVisibility(VISIBLE);
            else mBottomView.getView().setVisibility(GONE);
        }
    }

    /**
     * 是否允许下拉刷新
     */
    public void setEnableRefresh(boolean enableRefresh1) {
        this.enableRefresh = enableRefresh1;
        if (mHeadView != null) {
            if (enableRefresh) mHeadView.getView().setVisibility(VISIBLE);
            else mHeadView.getView().setVisibility(GONE);
        }
    }

    /**
     * 是否允许越界时显示头部刷新控件
     */
    public void setOverScrollTopShow(boolean isOverScrollTopShow) {
        this.isOverScrollTopShow = isOverScrollTopShow;
    }

    /**
     * 是否允许越界时显示底部刷新控件
     */
    public void setOverScrollBottomShow(boolean isOverScrollBottomShow) {
        this.isOverScrollBottomShow = isOverScrollBottomShow;
    }

    /**
     * 是否允许越界时显示刷新控件（setOverScrollTopShow,setOverScrollBottomShow统一设置方法）
     */
    public void setOverScrollRefreshShow(boolean isOverScrollRefreshShow) {
        this.isOverScrollTopShow = isOverScrollRefreshShow;
        this.isOverScrollBottomShow = isOverScrollRefreshShow;
    }

    /**
     * 是否允许开启越界回弹模式
     */
    public void setEnableOverScroll(boolean enableOverScroll1) {
        this.enableOverScroll = enableOverScroll1;
    }

    /**
     * 是否开启纯净的越界回弹模式,开启时刷新和加载更多控件不显示
     */
    public void setPureScrollModeOn() {
        isPureScrollModeOn = true;

        isOverScrollTopShow = false;
        isOverScrollBottomShow = false;
        setMaxHeadHeight(mOverScrollHeight);
        setHeaderHeight(mOverScrollHeight);
        setMaxBottomHeight(mOverScrollHeight);
        setBottomHeight(mOverScrollHeight);
    }

    /**
     * 设置越界高度
     */
    public void setOverScrollHeight(float overScrollHeightDp) {
        this.mOverScrollHeight = DensityUtil.dp2px(getContext(), overScrollHeightDp);
    }

    /**
     * 设置OverScroll时自动加载更多
     *
     * @param ifAutoLoadMore 为true表示底部越界时主动进入加载跟多模式，否则直接回弹
     */
    public void setAutoLoadMore(boolean ifAutoLoadMore) {
        autoLoadMore = ifAutoLoadMore;
        if (!autoLoadMore) return;
        setEnableLoadmore(true);
    }

    public void showRefreshingWhenOverScroll(boolean ifShow) {
        showRefreshingWhenOverScroll = ifShow;
    }

    public void showLoadingWhenOverScroll(boolean ifShow) {
        showLoadingWhenOverScroll = ifShow;
    }

    public void setEnableKeepIView(boolean ifKeep) {
        enableKeepIView = ifKeep;
    }

    /**
     * 设置刷新控件监听器
     */
    private RefreshListenerAdapter refreshListener;

    public void setOnRefreshListener(RefreshListenerAdapter refreshListener) {
        if (refreshListener != null) {
            this.refreshListener = refreshListener;
        }
    }

    @Override
    public void onPullingDown(TwinklingRefreshLayout refreshLayout, float fraction) {
        mHeadView.onPullingDown(fraction, mMaxHeadHeight, mHeadHeight);
        if (!enableRefresh) return;
        if (refreshListener != null) refreshListener.onPullingDown(refreshLayout, fraction);
    }

    @Override
    public void onPullingUp(TwinklingRefreshLayout refreshLayout, float fraction) {
        mBottomView.onPullingUp(fraction, mMaxHeadHeight, mHeadHeight);
        if (!enableLoadmore) return;
        if (refreshListener != null) refreshListener.onPullingUp(refreshLayout, fraction);
    }

    @Override
    public void onPullDownReleasing(TwinklingRefreshLayout refreshLayout, float fraction) {
        mHeadView.onPullReleasing(fraction, mMaxHeadHeight, mHeadHeight);
        if (!enableRefresh) return;
        if (refreshListener != null)
            refreshListener.onPullDownReleasing(refreshLayout, fraction);
    }

    @Override
    public void onPullUpReleasing(TwinklingRefreshLayout refreshLayout, float fraction) {
        mBottomView.onPullReleasing(fraction, mMaxBottomHeight, mBottomHeight);
        if (!enableLoadmore) return;
        if (refreshListener != null) refreshListener.onPullUpReleasing(refreshLayout, fraction);
    }

    @Override
    public void onRefresh(TwinklingRefreshLayout refreshLayout) {
        mHeadView.startAnim(mMaxHeadHeight, mHeadHeight);
        if (refreshListener != null) refreshListener.onRefresh(refreshLayout);
    }

    @Override
    public void onLoadMore(TwinklingRefreshLayout refreshLayout) {
        mBottomView.startAnim(mMaxBottomHeight, mBottomHeight);
        if (refreshListener != null) refreshListener.onLoadMore(refreshLayout);
    }

    @Override
    public void onFinishRefresh() {
        if (refreshListener != null) {
            refreshListener.onFinishRefresh();
        }
        if (!cp.isEnableKeepIView() && !cp.isRefreshing()) return;
        mHeadView.onFinish(new OnAnimEndListener() {
            @Override
            public void onAnimEnd() {
                cp.finishRefreshAfterAnim();
            }
        });
    }

    @Override
    public void onFinishLoadMore() {
        if (refreshListener != null) {
            refreshListener.onFinishLoadMore();
        }
        if (!cp.isEnableKeepIView() && !cp.isLoadingMore()) return;
        mBottomView.onFinish();
    }

    @Override
    public void onRefreshCanceled() {
        if (refreshListener != null) refreshListener.onRefreshCanceled();
    }

    @Override
    public void onLoadmoreCanceled() {
        if (refreshListener != null) refreshListener.onLoadmoreCanceled();
    }


    public class CoContext {
        private AnimProcessor animProcessor;

        private final static int PULLING_TOP_DOWN = 0;
        private final static int PULLING_BOTTOM_UP = 1;
        private int state = PULLING_TOP_DOWN;

        private static final int EX_MODE_NORMAL = 0;
        private static final int EX_MODE_FIXED = 1;
        private int exHeadMode = EX_MODE_NORMAL;

        public CoContext() {
            animProcessor = new AnimProcessor(this);
        }

        public void init() {
            if (isPureScrollModeOn) {
                setOverScrollTopShow(false);
                setOverScrollBottomShow(false);
                if (mHeadLayout != null) mHeadLayout.setVisibility(GONE);
                if (mBottomLayout != null) mBottomLayout.setVisibility(GONE);
            }
        }

        public AnimProcessor getAnimProcessor() {
            return animProcessor;
        }

        public boolean isEnableKeepIView() {
            return enableKeepIView;
        }

        public boolean showRefreshingWhenOverScroll() {
            return showRefreshingWhenOverScroll;
        }

        public boolean showLoadingWhenOverScroll() {
            return showLoadingWhenOverScroll;
        }

        public float getMaxHeadHeight() {
            return mMaxHeadHeight;
        }

        public int getHeadHeight() {
            return (int) mHeadHeight;
        }

        public int getExtraHeadHeight() {
            return mExtraHeadLayout.getHeight();
        }

        public int getMaxBottomHeight() {
            return (int) mMaxBottomHeight;
        }

        public int getBottomHeight() {
            return (int) mBottomHeight;
        }

        public int getOsHeight() {
            return (int) mOverScrollHeight;
        }

        public View getTargetView() {
            return mChildView;
        }

        public View getHeader() {
            return mHeadLayout;
        }

        public View getFooter() {
            return mBottomLayout;
        }

        public int getTouchSlop() {
            return mTouchSlop;
        }

        public void resetHeaderView() {
            if (mHeadView != null) mHeadView.reset();
        }

        public void resetBottomView() {
            if (mBottomView != null) mBottomView.reset();
        }

        public View getExHead() {
            return mExtraHeadLayout;
        }

        public void setExHeadNormal() {
            exHeadMode = EX_MODE_NORMAL;
        }

        public void setExHeadFixed() {
            exHeadMode = EX_MODE_FIXED;
        }

        public boolean isExHeadNormal() {
            return exHeadMode == EX_MODE_NORMAL;
        }

        public boolean isExHeadFixed() {
            return exHeadMode == EX_MODE_FIXED;
        }

        /**
         * 在添加附加Header前锁住，阻止一些额外的位移动画
         */
        private boolean isExHeadLocked = true;

        public boolean isExHeadLocked() {
            return isExHeadLocked;
        }

        //添加了额外头部时触发
        public void onAddExHead() {
            isExHeadLocked = false;
            LayoutParams params = (LayoutParams) mChildView.getLayoutParams();
            params.addRule(BELOW, mExtraHeadLayout.getId());
            mChildView.setLayoutParams(params);
            requestLayout();
        }


        /**
         * 主动刷新、加载更多、结束
         */
        public void startRefresh() {
            post(new Runnable() {
                @Override
                public void run() {
                    setStatePTD();
                    if (!isPureScrollModeOn && mChildView != null) {
                        setRefreshing(true);
                        animProcessor.animHeadToRefresh();
                    }
                }
            });
        }

        public void startLoadMore() {
            post(new Runnable() {
                @Override
                public void run() {
                    setStatePBU();
                    if (!isPureScrollModeOn && mChildView != null) {
                        setLoadingMore(true);
                        animProcessor.animBottomToLoad();
                    }
                }
            });
        }

        public void finishRefreshing() {
            onFinishRefresh();
        }

        public void finishRefreshAfterAnim() {
            if (mChildView != null) {
                animProcessor.animHeadBack(true);
            }
        }

        public void finishLoadmore() {
            onFinishLoadMore();
            if (mChildView != null) {
                animProcessor.animBottomBack(true);
            }
        }

        public boolean enableOverScroll() {
            return enableOverScroll;
        }

        public boolean allowPullDown() {
            return enableRefresh || enableOverScroll;
        }

        public boolean allowPullUp() {
            return enableLoadmore || enableOverScroll;
        }

        public boolean enableRefresh() {
            return enableRefresh;
        }

        public boolean enableLoadmore() {
            return enableLoadmore;
        }

        public boolean allowOverScroll() {
            return (!isRefreshVisible && !isLoadingVisible);
        }

        public boolean isRefreshVisible() {
            return isRefreshVisible;
        }

        public boolean isLoadingVisible() {
            return isLoadingVisible;
        }

        public void setRefreshVisible(boolean visible) {
            isRefreshVisible = visible;
        }

        public void setLoadVisible(boolean visible) {
            isLoadingVisible = visible;
        }

        public void setRefreshing(boolean refreshing) {
            isRefreshing = refreshing;
        }

        public boolean isRefreshing() {
            return isRefreshing;
        }

        public boolean isLoadingMore() {
            return isLoadingMore;
        }

        public void setLoadingMore(boolean loadingMore) {
            isLoadingMore = loadingMore;
        }

        public boolean isOpenFloatRefresh() {
            return floatRefresh;
        }

        public boolean autoLoadMore() {
            return autoLoadMore;
        }

        public boolean isPureScrollModeOn() {
            return isPureScrollModeOn;
        }

        public boolean isOverScrollTopShow() {
            return isOverScrollTopShow;
        }

        public boolean isOverScrollBottomShow() {
            return isOverScrollBottomShow;
        }

        public void onPullingDown(float offsetY) {
            pullListener.onPullingDown(TwinklingRefreshLayout.this, offsetY / mHeadHeight);
        }

        public void onPullingUp(float offsetY) {
            pullListener.onPullingUp(TwinklingRefreshLayout.this, offsetY / mBottomHeight);
        }

        public void onRefresh() {
            pullListener.onRefresh(TwinklingRefreshLayout.this);
        }

        public void onLoadMore() {
            pullListener.onLoadMore(TwinklingRefreshLayout.this);
        }

        public void onFinishRefresh() {
            pullListener.onFinishRefresh();
        }

        public void onFinishLoadMore() {
            pullListener.onFinishLoadMore();
        }

        public void onPullDownReleasing(float offsetY) {
            pullListener.onPullDownReleasing(TwinklingRefreshLayout.this, offsetY / mHeadHeight);
        }

        public void onPullUpReleasing(float offsetY) {
            pullListener.onPullUpReleasing(TwinklingRefreshLayout.this, offsetY / mBottomHeight);
        }

        public boolean dispatchTouchEventSuper(MotionEvent ev) {
            return TwinklingRefreshLayout.super.dispatchTouchEvent(ev);
        }

        public void onRefreshCanceled() {
            pullListener.onRefreshCanceled();
        }

        public void onLoadmoreCanceled() {
            pullListener.onLoadmoreCanceled();
        }

        public void setStatePTD() {
            state = PULLING_TOP_DOWN;
        }

        public void setStatePBU() {
            state = PULLING_BOTTOM_UP;
        }

        public boolean isStatePTD() {
            return PULLING_TOP_DOWN == state;
        }

        public boolean isStatePBU() {
            return PULLING_BOTTOM_UP == state;
        }

        private boolean prepareFinishRefresh = false;
        private boolean prepareFinishLoadMore = false;

        public boolean isPrepareFinishRefresh() {
            return prepareFinishRefresh;
        }

        public boolean isPrepareFinishLoadMore() {
            return prepareFinishLoadMore;
        }

        public void setPrepareFinishRefresh(boolean prepareFinishRefresh) {
            this.prepareFinishRefresh = prepareFinishRefresh;
        }

        public void setPrepareFinishLoadMore(boolean prepareFinishLoadMore) {
            this.prepareFinishLoadMore = prepareFinishLoadMore;
        }
    }
}
