package com.heaven7.android.dragflowlayout;

import android.annotation.TargetApi;
import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;

import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.core.view.GestureDetectorCompat;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;

/**
 * the drag flow layout: you should not use normal onclick listener for child or else may cause problem.
 * Created by heaven7 on 2016/8/1.
 */
public class DragFlowLayout extends FlowLayout implements IViewManager {

    private static final String TAG = "DragGridLayout";
    private static final boolean DEBUG = true;

    /*private*/ static final Debugger sDebugger = new Debugger(TAG, DEBUG);

    public static final int INVALID_INDXE = -1;
    /**
     * the delay of check click event.
     */
    private static final int DELAY_CHECK_CLICK = 360;

    /**
     * indicate current is idle, and can't draggable
     */
    public static final int DRAG_STATE_IDLE = 1;
    /**
     * indicate current is dragging
     */
    public static final int DRAG_STATE_DRAGGING = 2;
    /**
     * indicate current is not dragging but can drag
     */
    public static final int DRAG_STATE_DRAGGABLE = 3;

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({DRAG_STATE_IDLE, DRAG_STATE_DRAGGING, DRAG_STATE_DRAGGABLE})
    public @interface DragState {
    }

    private static final Comparator<Item> sComparator = new Comparator<Item>() {
        @Override
        public int compare(Item lhs, Item rhs) {
            return compareImpl(lhs.index, rhs.index);
        }

        public int compareImpl(int lhs, int rhs) {
            return lhs < rhs ? -1 : (lhs == rhs ? 0 : 1);
        }
    };

    private final InternalItemHelper mItemManager = new InternalItemHelper();
    private AlertWindowHelper mWindomHelper;
    private
    @DragState
    int mDragState = DRAG_STATE_IDLE;

    private DragItemManager mDragManager;
    private DefaultDragCallback mCallback;
    private OnItemClickListener mClickListener;
    private OnDragStateChangeListener mDragStateListener;

    /**
     * indicate whether dispatch the event to the alert window or not.
     */
    private boolean mDispatchToAlertWindow;
    private final int[] mTempLocation = new int[2];

    private CheckForDrag mCheckForDrag;
    private CheckForRelease mCheckForRelease;

    private boolean mReDrag;
    private volatile boolean mCancelled;

    private GestureDetectorCompat mGestureDetector;
    private volatile View mTouchChild;

    private final AlertWindowHelper.ICallback mWindowCallback = new AlertWindowHelper.ICallback() {
        @Override
        public void onCancel(View view, MotionEvent event) {
            sDebugger.i("onCancel", "------------->");
            releaseDragInternal(true);
        }

        @Override
        public boolean onMove(View view, MotionEvent event) {
            //infoWhenDebug("onMove","------------->");
            return processOverlap(view);
        }
    };
    /**
     * indicate can draggable for all items
     */
    private boolean mDraggable = true;

    private boolean mRequestedDisallowIntercept;
    private boolean mPendingDrag;

    /**
     * the drag state change listener
     * if {@link DragFlowLayout #setDraggable(false)} is called, this listener will have nothing effect.
     */
    public interface OnDragStateChangeListener {
        /**
         * callen when drag state changed
         *
         * @param dfl       the DragFlowLayout
         * @param dragState the drag state, see {@link DragFlowLayout#DRAG_STATE_DRAGGING} and etc.
         */
        void onDragStateChange(DragFlowLayout dfl, int dragState);
    }

    /**
     * the listener of on click item(child of DragFlowLayout) view of DragFlowLayout.
     * if {@link DragFlowLayout #setDraggable(false)} is called, this listener will have nothing effect.
     */
    public interface OnItemClickListener {
        /**
         * called when a click event occurrence ,perform the click event if you need. and return true if you performed the click event.
         *
         * @param dragFlowLayout the DragFlowLayout
         * @param child          the direct child of DragFlowLayout.
         * @param event          the event of trigger this click event
         * @param dragState      indicate current drag state , see {@link DragFlowLayout#DRAG_STATE_DRAGGING} and etc.
         * @return true, if you performed the click event
         */
        boolean performClick(DragFlowLayout dragFlowLayout, View child,
                             MotionEvent event, int dragState);
    }

    /**
     * the callback of DragFlowLayout.
     */
    static abstract class Callback {

        private final DragFlowLayout mParent;

        Callback(DragFlowLayout parent) {
            this.mParent = parent;
        }

        public DragFlowLayout getDragFlowLayout() {
            return mParent;
        }

        /**
         * set the child data by target drag state.
         *
         * @param child     the direct child of DragFlowLayout
         * @param dragState the drag state of current,see {@link DragFlowLayout#DRAG_STATE_DRAGGING} and etc.
         */
        public abstract void setChildByDragState(View child, int dragState);

        /**
         * create a child view  from the target child view.
         *
         * @param child     the direct child of DragFlowLayout
         * @param index     the index of this child view, or -1 for unknown index.
         * @param dragState current drag state. see {@link DragFlowLayout#DRAG_STATE_DRAGGING} and etc.
         * @return the new child view
         */
        @NonNull
        public abstract View createChildView(View child, int index, int dragState);

        /**
         * set the window view by target child view.
         *
         * @param windowView the window view, often  like the child view.
         * @param child      the direct child view of DragFlowLayout
         * @param dragState  current drag state. see {@link DragFlowLayout#DRAG_STATE_DRAGGING} and etc.
         */
        public abstract void setWindowViewByChild(View windowView, View child, int dragState);

        /**
         * create window view by target child view
         *
         * @param child     the direct child view of DragFlowLayout
         * @param dragState current drag state. see {@link DragFlowLayout#DRAG_STATE_DRAGGING} and etc.
         * @return a window view that will attach to application.
         */
        public View createWindowView(View child, int dragState) {
            return createChildView(child, -1, dragState);
        }

        /**
         * is the child draggable,default is true.
         *
         * @param child the direct child of DragFlowLayout
         * @return true if the child is draggable
         */
        public boolean isChildDraggable(View child) {
            return true;
        }

    }

    public DragFlowLayout(Context context) {
        this(context, null);
    }

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

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

    @TargetApi(21)
    public DragFlowLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        mWindomHelper = new AlertWindowHelper(context);
        mGestureDetector = new GestureDetectorCompat(context, new GestureListenerImpl());
    }

    /* package */ DefaultDragCallback getCallback() {
        return mCallback;
    }

    /**
     * get the drag state
     */
    public
    @DragState
    int getDragState() {
        return mDragState;
    }

    /***
     * set the item click listenr
     *
     * @param l the item listener
     */
    public void setOnItemClickListener(OnItemClickListener l) {
        this.mClickListener = l;
    }

    /**
     * set the adapter
     */
    public <T> void setDragAdapter(DragAdapter<T> adapter) {
        if (adapter == null) {
            throw new NullPointerException();
        }
        if (mCallback != null) {
            this.mItemManager.removeViewObserver(mCallback);
        }
        this.mCallback = new DefaultDragCallback<T>(this, adapter);
        this.mItemManager.addViewObserver(mCallback);
    }

    /**
     * get the {@link DragItemManager} for manage the item of DragFlowLayout. eg 'CRUD'
     *
     * @return the DragItemManager.
     */
    public DragItemManager getDragItemManager() {
        if (mDragManager == null) {
            mDragManager = new DragItemManager();
        }
        return mDragManager;
    }

    /**
     * set the DragFlowLayout can drag or not. default is true;
     *
     * @param draggable if can drag or not. false to disable drag for DragFlowLayout. default is true.
     */
    public void setDraggable(boolean draggable) {
        this.mDraggable = draggable;
    }

    /**
     * add a view observer
     *
     * @param observer the  view observer
     */
    public void addViewObserver(IViewObserver observer) {
        mItemManager.addViewObserver(observer);
    }

    /**
     * set the drag state change listener.
     *
     * @param l the listener
     */
    public void setOnDragStateChangeListener(OnDragStateChangeListener l) {
        this.mDragStateListener = l;
    }

    /**
     * get the drag adapter
     *
     * @return the DragAdapter
     */
    public DragAdapter getDragAdapter() {
        return mCallback.getDragAdapter();
    }

    /**
     * prepare items by the target count, this is useful for recycle item view.
     * must called after {@link #setDragAdapter(DragAdapter)}.
     *
     * @param count the  count of items
     */
    public void prepareItemsByCount(int count) {
        mCallback.prepareItemsByCount(count);
    }

    /**
     * set the drag state
     *
     * @param dragState    the drag state of current,see {@link DragFlowLayout#DRAG_STATE_DRAGGING} and etc
     * @param showChildren if force show all direct children of DragFlowLayout
     */
    private void setDragState(@DragState int dragState, boolean showChildren) {
        if (this.mDragState == dragState) {
            return;
        }
        checkCallback();
        this.mDragState = dragState;
        final Callback mCallback = this.mCallback;
        View view;
        for (int i = 0, size = getChildCount(); i < size; i++) {
            view = getChildAt(i);
            if (showChildren && view.getVisibility() != View.VISIBLE) {
                view.setVisibility(View.VISIBLE);
            }
            mCallback.setChildByDragState(view, dragState);
        }
    }

    /**
     * tag finish the drag state
     */
    public void finishDrag() {
        releaseDragInternal(false);
        setDragState(DragFlowLayout.DRAG_STATE_IDLE, true);
        dispatchDragStateChange(DragFlowLayout.DRAG_STATE_IDLE);
    }

    /**
     * begin drag, this will cause the drag state turn to {@link #DRAG_STATE_DRAGGABLE}.
     */
    public void beginDrag() {
        beginDragInternal(DRAG_STATE_DRAGGABLE);
    }

    private void beginDragInternal(@DragState int state) {
        if (getParent() != null) {
            getParent().requestDisallowInterceptTouchEvent(true);
            mRequestedDisallowIntercept = true;
        }
        setDragState(state, false);
        checkForDrag(0, false);
    }

    private void checkForRelease() {
        if (mCheckForRelease == null) {
            mCheckForRelease = new CheckForRelease();
        }
        postDelayed(mCheckForRelease, 100);
    }

    private void checkForDrag(long delay, boolean checkRelease) {
        if (mCheckForDrag == null) {
            mCheckForDrag = new CheckForDrag();
        }
        postDelayed(mCheckForDrag, delay);
        if (checkRelease) {
            checkForRelease();
        }
    }

    private void beginDragImpl(View childView) {
        checkCallback();
        //impl
        childView.setVisibility(View.INVISIBLE);
        mDispatchToAlertWindow = true;
        mItemManager.findDragItem(childView);
        childView.getLocationInWindow(mTempLocation);
        // sDebugger.w("beginDragImpl",);
        mWindomHelper.showView(mCallback.createWindowView(childView, mDragState), mTempLocation[0],
                mTempLocation[1], true, mWindowCallback);
        mDragState = DRAG_STATE_DRAGGING;
        dispatchDragStateChange(DRAG_STATE_DRAGGING);
    }

    private void dispatchDragStateChange(int dragState) {
        if (mDragStateListener != null) {
            mDragStateListener.onDragStateChange(this, dragState);
        }
    }

    /**
     * 根据指定的view,处理重叠事件
     *
     * @param view the target view
     * @return true 如果处理重叠成功。
     */
    private boolean processOverlap(View view) {
        final List<Item> mItems = mItemManager.mItems;
        final Callback mCallback = this.mCallback;
        Item item = null;
        int centerX, centerY;
        boolean found = false;
        for (int i = 0, size = mItems.size(); i < size; i++) {
            item = mItems.get(i);
            item.view.getLocationOnScreen(mTempLocation);
            centerX = mTempLocation[0] + item.view.getWidth() / 2;
            centerY = mTempLocation[1] + item.view.getHeight() / 2;
            if (isViewUnderInScreen(view, centerX, centerY, false) && item != mItemManager.mDragItem
                    && mCallback.isChildDraggable(item.view)) {
                sDebugger.i("onMove_isViewUnderInScreen", "index = " + item.index);
                /**
                 * Drag到target目标的center时,判断有没有已经hold item, 有的话,先删除旧的,
                 */
                found = true;
                break;
            }
        }
        if (found) {
            //the really index to add
            final int index = item.index;
            Item dragItem = mItemManager.mDragItem;
            // remove old
            removeView(mItemManager.mDragItem.view);
            //add hold
            View hold = mCallback.createChildView(dragItem.view, dragItem.index, mDragState);
            hold.setVisibility(View.INVISIBLE);  //隐藏
            addView(hold, index);
            //reset drag item and alert view
            mItemManager.findDragItem(hold);
            mCallback.setWindowViewByChild(mWindomHelper.getView(),
                    mItemManager.mDragItem.view, mDragState);
            sDebugger.i("onMove", "hold index = " + mItemManager.mDragItem.index);
        }
        return found;
    }

    private void releaseDragInternal(boolean notifyDragStateChange) {
        checkCallback();
        if (mItemManager.mDragItem != null) {
            mItemManager.mDragItem.view.setVisibility(View.VISIBLE);
            mCallback.setChildByDragState(mItemManager.mDragItem.view, mDragState);
        }
        mWindomHelper.releaseView();
        mDispatchToAlertWindow = false;
        mTouchChild = null;
        mDragState = DRAG_STATE_DRAGGABLE;
        if (notifyDragStateChange) {
            dispatchDragStateChange(DRAG_STATE_DRAGGABLE);
        }
        mRequestedDisallowIntercept = false;
    }

    private void checkCallback() {
        if (mCallback == null) {
            throw new IllegalStateException("you must call #setDragAdapter first.");
        }
    }

    /**
     * Find the topmost child under the given point within the parent view's coordinate system.
     *
     * @param x X position to test in the parent's coordinate system
     * @param y Y position to test in the parent's coordinate system
     * @return The topmost child view under (x, y) or null if none found.
     */
    public View findTopChildUnder(int x, int y) {
        checkCallback();
        final int childCount = getChildCount();
        for (int i = childCount - 1; i >= 0; i--) {
            final View child = getChildAt(i);
            if (ViewUtils.isViewIntersect(child, x, y))
                return child;
        }
        return null;
    }

    private boolean isViewUnderInScreen(View view, int x, int y, boolean log) {
        if (view == null) {
            return false;
        }
        int w = view.getWidth();
        int h = view.getHeight();
        view.getLocationOnScreen(mTempLocation);
        int viewX = mTempLocation[0];
        int viewY = mTempLocation[1];
        if (log) {
            sDebugger.i("isViewUnderInScreen", String.format(Locale.getDefault(),
                    "viewX = %d ,viewY = %d ,width = %d ,height = %d", viewX, viewY, w, h));
        }
        return x >= viewX && x < viewX + w
                && y >= viewY && y < viewY + h;
    }

    private void checkIfAutoReleaseDrag() {
        if (getChildCount() == 0) {
            final int oldState = this.mDragState;
            releaseDragInternal(false);
            mDragState = DRAG_STATE_IDLE;
            if (oldState != DRAG_STATE_IDLE) {
                dispatchDragStateChange(DRAG_STATE_IDLE);
            }
        }
    }

    //=================================== override method ===================================== //

    @Override
    public void setOnClickListener(View.OnClickListener l) {
        if (mDraggable) {
            throw new UnsupportedOperationException("you should use" +
                    " DragFlowLayout.OnItemClickListener instead..");
        } else {
            super.setOnClickListener(l);
        }
    }

    @Override
    public void removeViewWithoutNotify(View child) {
        super.removeView(child);
    }

    @Override
    public void addView(View child, int index, LayoutParams params) {
        super.addView(child, index, params);
        checkCallback();
        mItemManager.onAddView(child, index, params);
        mCallback.setChildByDragState(child, mDragState);
    }

    @Override
    public void removeViewAt(int index) {
        super.removeViewAt(index);
        mItemManager.onRemoveViewAt(index);
        checkIfAutoReleaseDrag();
    }

    @Override
    public void removeView(View view) {
        super.removeView(view);
        mItemManager.onRemoveView(view);
        checkIfAutoReleaseDrag();
    }

    @Override
    public void removeAllViews() {
        super.removeAllViews();
        mItemManager.onRemoveAllViews();
        checkIfAutoReleaseDrag();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        removeCallbacks(mCheckForDrag);
        removeCallbacks(mCheckForRelease);
    }

  /*  @Override
    protected Parcelable onSaveInstanceState() {
        System.err.println("onSaveInstanceState");  //屏幕旋转也会调用
        return super.onSaveInstanceState();
    }*/

    /* @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if(!mDraggable){
            return super.onInterceptTouchEvent(ev);
        }
        sDebugger.i("onInterceptTouchEvent", ev.toString());
         switch (ev.getAction()){
             case MotionEvent.ACTION_DOWN:

                 break;

             case MotionEvent.ACTION_MOVE:
                 break;

             case MotionEvent.ACTION_UP:
                 break;
         }
        return (mIntercepted = mGestureDetector.onTouchEvent(ev));
    }*/

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        sDebugger.i("onTouchEvent", event.toString());
        //sDebugger.i("onTouchEvent", "------> mDispatchToAlertWindow = " + mDispatchToAlertWindow +" ,mIsDragState = " + mIsDragState);
        if (!mDraggable) {
            return super.onTouchEvent(event);
        }
        final boolean handled = mGestureDetector.onTouchEvent(event);
        mCancelled = event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_UP;
        //解决ScrollView嵌套DragFlowLayout时,引起的事件冲突
        if (getParent() != null) {
            getParent().requestDisallowInterceptTouchEvent(mRequestedDisallowIntercept || mDragState != DRAG_STATE_IDLE);
        }
        if (mDispatchToAlertWindow) {
            mWindomHelper.getView().dispatchTouchEvent(event);
            if (mCancelled) {
                mDispatchToAlertWindow = false;
            }
        }
        return handled;
    }
    //=================================== end -- override method ===================================== //

    //================================================================================

    private class CheckForDrag implements Runnable {
        @Override
        public void run() {
            if (mTouchChild != null) {
                beginDragImpl(mTouchChild);
            }
        }
    }

    private class CheckForRelease implements Runnable {
        @Override
        public void run() {
            if (mCancelled) {
                releaseDragInternal(true);
            }
        }
    }

    private static class Item {
        int index;
        View view;

        @Override
        public String toString() {
            return "Item{" +
                    "index=" + index +
                    '}';
        }
    }

    private static class InternalItemHelper {
        final List<Item> mItems = new ArrayList<>();
        /**
         * 对应的拖拽item
         */
        Item mDragItem = null;
        List<IViewObserver> mListeners = new ArrayList<>();

        public void addViewObserver(IViewObserver l) {
            this.mListeners.add(l);
        }

        public void removeViewObserver(IViewObserver l) {
            this.mListeners.remove(l);
        }

        public void onAddView(View child, int index, LayoutParams params) {
            index = index != -1 ? index : mItems.size();
            sDebugger.d("onAddView", "index = " + index);
            Item item;
            for (int i = 0, size = mItems.size(); i < size; i++) {
                item = mItems.get(i);
                if (item.index >= index) {
                    item.index++;
                }
            }
            //add
            item = new Item();
            item.index = index;
            item.view = child;
            mItems.add(item);
            Collections.sort(mItems, sComparator);
            //debugWhenDebug("onAddView",mItems.toString());
            dispatchViewAdd(child, index);
        }

        private void dispatchViewAdd(View child, int index) {
            for (IViewObserver observer : mListeners) {
                observer.onAddView(child, index);
            }
        }

        private void dispatchViewRemove(View child, int index) {
            for (IViewObserver observer : mListeners) {
                observer.onRemoveView(child, index);
            }
        }

        public void onRemoveViewAt(int index) {
            sDebugger.d("onRemoveViewAt", "index = " + index);
            Item item;
            for (int i = 0, size = mItems.size(); i < size; i++) {
                item = mItems.get(i);
                if (item.index > index) {
                    item.index--;
                }
            }
            item = mItems.remove(index);
            Collections.sort(mItems, sComparator);
            // debugWhenDebug("onAddView",mItems.toString());
            dispatchViewRemove(item.view, index);
        }

        public void onRemoveView(View view) {
            Item item;
            int targetIndex = INVALID_INDXE;
            for (int i = 0, size = mItems.size(); i < size; i++) {
                item = mItems.get(i);
                if (item.view == view) {
                    targetIndex = item.index;
                    break;
                }
            }
            sDebugger.d("onRemoveView", "targetIndex = " + targetIndex);
            if (targetIndex == -1) {
                throw new IllegalStateException("caused by targetIndex == -1");
            }
            // -- index if need
            for (int i = 0, size = mItems.size(); i < size; i++) {
                item = mItems.get(i);
                if (item.index > targetIndex) {
                    item.index--;
                }
            }
            mItems.remove(targetIndex);
            Collections.sort(mItems, sComparator);
            //debugWhenDebug("onAddView",mItems.toString());
            dispatchViewRemove(view, targetIndex);
        }

        public void onRemoveAllViews() {
            if (mListeners.size() > 0) {
                for (int size = mItems.size(), i = size - 1; i >= 0; i--) {
                    dispatchViewRemove(mItems.get(i).view, i);
                }
            }
            mItems.clear();
        }

        public void findDragItem(View touchView) {
            Item item;
            for (int i = 0, size = mItems.size(); i < size; i++) {
                item = mItems.get(i);
                if (item.view == touchView) {
                    mDragItem = item;
                    break;
                }
            }
        }
    }

    private class GestureListenerImpl extends GestureDetector.SimpleOnGestureListener {

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            if (!mDispatchToAlertWindow && !mPendingDrag && mDragState != DRAG_STATE_IDLE
                    && mCallback.isChildDraggable(mTouchChild)) {
                mPendingDrag = true;
                checkForDrag(0, false);
            }
            return true;
        }

        @Override
        public boolean onDown(MotionEvent e) {
            mPendingDrag = false;
            removeCallbacks(mCheckForDrag);
            mTouchChild = findTopChildUnder((int) e.getX(), (int) e.getY());
            sDebugger.i("mGestureDetector_onDown", "----------------- > after find : mTouchChild = "
                    + mTouchChild);
            mReDrag = false;
            if (mTouchChild != null) {
                mWindomHelper.setTouchDownPosition((int) e.getRawX(), (int) e.getRawY());
               /* if(!mDispatchToAlertWindow && mDragState != DRAG_STATE_IDLE
                        && mCallback.isChildDraggable( mTouchChild ) ) {
                    mReDrag = true;
                    checkForDrag(DELAY_CHECK_CLICK, false);
                }*/
            }
            return mTouchChild != null;
        }

        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
            if (mClickListener == null) {
                return false;
            }
            //处理点击时,看起来有点怪异的感觉(控件偏离了点位置)
            removeCallbacks(mCheckForDrag);
            boolean performed = mClickListener.performClick(DragFlowLayout.this, mTouchChild, e, mDragState);
            // sDebugger.i("mGestureDetector_onSingleTapUp","----------------- > performed = " + performed);
            if (performed) {
                playSoundEffect(SoundEffectConstants.CLICK);
            } else if (mReDrag) {
                checkForDrag(0, true);
            }
            return performed;
        }

        @Override
        public void onLongPress(MotionEvent e) {
            //sDebugger.i("mGestureDetector_onLongPress","----------------- >");
            if (mDragState != DRAG_STATE_DRAGGING && mTouchChild != null
                    && mCallback.isChildDraggable(mTouchChild)) {
                sDebugger.w(TAG, "onLongPress");
                sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
                performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
                beginDragInternal(DRAG_STATE_DRAGGING);
            }
        }
    }

    /**
     * the drag item manager,
     */
    // @Deprecated <p> use {@link com.heaven7.android.dragflowlayout.DragItemManager} instead</p>
    public class DragItemManager {

        /**
         * get the item count
         *
         * @return the item count
         */
        public int getItemCount() {
            return getChildCount();
        }

        /**
         * get all items
         *
         * @param <T> the t
         * @return the items that not removed
         */
        public <T> List<T> getItems() {
            final DragAdapter adapter = getDragAdapter();
            List<T> list = new ArrayList<>();
            T t;
            for (int i = 0, size = getChildCount(); i < size; i++) {
                t = (T) adapter.getData(getChildAt(i));
                list.add(t);
            }
            return list;
        }

        /**
         * add order items to the last.
         *
         * @param datas the datas
         */
        public void addItems(Object... datas) {
            for (int i = 0, size = datas.length; i < size; i++) {
                addItem(datas[i]);
            }
        }

        /**
         * add order items to the last.
         *
         * @param list the list data
         * @param <T>  the t
         */
        public <T> void addItems(List<T> list) {
            for (int i = 0, size = list.size(); i < size; i++) {
                addItem(list.get(i));
            }
        }

        /**
         * add items  from target startIndex and data.
         *
         * @param startIndex the start index to add
         * @param data       the data.
         */
        public void addItems(int startIndex, Object... data) {
            if (startIndex > getItemCount()) {
                throw new IllegalArgumentException();
            }
            for (int i = 0, size = data.length; i < size; i++) {
                addItem(startIndex + i, data[i]);
            }
        }

        /**
         * add items  from target startIndex and data.
         *
         * @param startIndex the start index to add
         * @param data       the data.
         * @param <T>        the t
         */
        public <T> void addItems(int startIndex, List<T> data) {
            if (startIndex > getItemCount()) {
                throw new IllegalArgumentException();
            }
            for (int i = 0, size = data.size(); i < size; i++) {
                addItem(startIndex + i, data.get(i));
            }
        }

        /**
         * add a item to the DragFlowLayout
         *
         * @param data the data
         */
        public void addItem(Object data) {
            final DragAdapter mAdapter = getDragAdapter();
            final View view = mCallback.obtainItemView();
            mAdapter.onBindData(view, getDragState(), data);
            addView(view);
        }

        /**
         * add a item to the DragFlowLayout
         *
         * @param index the index , can be -1 if add last.
         * @param data  the data
         */
        public void addItem(int index, Object data) {
            if (index < -1) {
                throw new IllegalArgumentException("index can't < -1.");
            }
            final DragAdapter mAdapter = getDragAdapter();
            final View view = mCallback.obtainItemView();
            mAdapter.onBindData(view, getDragState(), data);
            addView(view, index);
        }

        /**
         * remove item by index
         *
         * @param index the index , you should be careful of the drag state.
         */
        public void removeItem(int index) {
            removeViewAt(index);
        }

        /**
         * remove item by child
         *
         * @param child the direct child of DragFlowLayout
         */
        public void removeItem(View child) {
            removeView(child);
        }

        /**
         * remove item by data
         *
         * @param data the data
         */
        public void removeItem(Object data) {
            final DragAdapter adapter = getDragAdapter();
            Object rawData;
            int index = INVALID_INDXE;
            for (int size = getChildCount(), i = size - 1; i >= 0; i--) {
                rawData = adapter.getData(getChildAt(i));
                if (rawData.equals(data)) {
                    index = i;
                    break;
                }
            }
            if (index >= 0) {
                removeViewAt(index);
            }
        }

        public <T> void replaceAll(List<T> list) {
            DragFlowLayout.this.removeAllViews();
            addItems(list);
        }

        public void clearItems() {
            DragFlowLayout.this.removeAllViews();
        }

        /**
         * update item by index and new data.
         *
         * @param index the index
         * @param data  the data
         */
        public void updateItem(int index, Object data) {
            final View view = getChildAt(index);
            getDragAdapter().onBindData(view, getDragState(), data);
        }

        /**
         * update item by previous data and new data.
         *
         * @param preData the previous data
         * @param newData the new data
         */
        public void updateItem(Object preData, Object newData) {
            final DragAdapter adapter = getDragAdapter();
            Object rawData;
            View view = null;
            boolean found = false;
            for (int size = getChildCount(), i = size - 1; i >= 0; i--) {
                view = getChildAt(i);
                rawData = adapter.getData(view);
                if (rawData.equals(preData)) {
                    found = true;
                    break;
                }
            }
            if (found) {
                adapter.onBindData(view, getDragState(), newData);
            }
        }
    }

}