package com.zaihuishou.databinding.expandablerecycleradapter.adapter;

import android.content.Context;
import android.databinding.DataBindingUtil;
import android.databinding.ObservableArrayList;
import android.databinding.ViewDataBinding;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.ViewGroup;

import com.zaihuishou.databinding.expandablerecycleradapter.observable.BaseExpandableObservable;
import com.zaihuishou.databinding.expandablerecycleradapter.util.AdapterItemUtil;
import com.zaihuishou.databinding.expandablerecycleradapter.viewholder.BindingViewHolder;

import java.util.ArrayList;
import java.util.List;

/**
 * * creater: zaihuishou
 * create time: 16-8-2
 * author email:[email protected]
 * desc: the data binding expandable recyclerview adapter
 */

public abstract class BaseExpandableAdapter extends RecyclerView.Adapter<BindingViewHolder>
        implements BaseExpandableObservable.ItemListExpandCollapseListener {

    protected List<Object> mDataList;

    private int mItemType;

    private AdapterItemUtil mUtil = new AdapterItemUtil();

    private BaseExpandableAdapter.ExpandCollapseListener mExpandCollapseListener;

    private LayoutInflater mLayoutInflater;

    private Context mContext;
    private ArrayList<RecyclerView> mRecyclerViews;

    public BaseExpandableAdapter(Context context, List<Object> dataList) {
        this.mContext = context;
        mDataList = dataList;
        mRecyclerViews = new ArrayList<>();
        mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        mRecyclerViews.add(recyclerView);
    }

    @Override
    public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
        super.onDetachedFromRecyclerView(recyclerView);
        mRecyclerViews.remove(recyclerView);
    }

    /**
     * update all item
     *
     * @param newDataList thw new data list
     */
    public void updateData(@NonNull List<Object> newDataList) {
        if (newDataList != null && !newDataList.isEmpty()) {
            mDataList = newDataList;
            notifyDataSetChanged();
        }
    }

    /**
     * update an exit item
     *
     * @param position
     * @param o
     */
    public void updateItem(int position, Object o) {
        if (!isDataListEmpty() && position >= 0 && position < mDataList.size()) {
            mDataList.set(position, o);
            notifyItemChanged(position);
        }
    }

    /**
     * inserted an item to position
     *
     * @param position the item will be add to position
     * @param o        item object
     */

    public void addItem(int position, Object o) {
        if (!isDataListEmpty() && position >= 0 && position <= mDataList.size()) {
            mDataList.add(position, o);
            notifyItemInserted(position);
        }
    }

    /**
     * delete an item by position
     *
     * @param position
     */
    public void deleteItem(int position) {
        if (!isDataListEmpty() && position >= 0 && position < mDataList.size()) {
            mDataList.remove(position);
            notifyItemRemoved(position);
        }
    }

    /**
     * @return {@link #mDataList} is empty
     */
    private boolean isDataListEmpty() {
        return (mDataList == null || mDataList.isEmpty()) ? true : false;
    }

    @Override
    public void onItemListCollapsed(BaseExpandableObservable baseExpandableObservable) {
        int indexOf = mDataList.indexOf(baseExpandableObservable);
        collapseListItem(indexOf, baseExpandableObservable, true);
    }

    @Override
    public void onItemListExpanded(BaseExpandableObservable baseExpandableObservable) {
        int indexOf = mDataList.indexOf(baseExpandableObservable);
        expandItem(indexOf, baseExpandableObservable, true);
    }

    /**
     * expand item
     *
     * @param parentIndex                       will be expand item index
     * @param baseExpandableObservable          the item Observable
     * @param expansionTriggeredByListItemClick
     */
    public void expandItem(int parentIndex, BaseExpandableObservable baseExpandableObservable, boolean expansionTriggeredByListItemClick) {
        if (parentIndex >= 0 && parentIndex < mDataList.size() && !baseExpandableObservable.isExpand.get()) {

            ObservableArrayList<Object> childList = baseExpandableObservable.getChildList();
            final int childSize = childList.size();
            for (int i = 0; i < childSize; i++) {
                Object o = childList.get(i);
                int newIndex = parentIndex + i + 1;
                mDataList.add(newIndex, o);
            }
            notifyItemRangeInserted(parentIndex + 1, childSize);
            int positionStart = parentIndex + childSize;
            if (parentIndex != mDataList.size() - 1)
                notifyItemRangeChanged(positionStart, mDataList.size() - positionStart);
            baseExpandableObservable.setIsExpand(true);
            if (!mRecyclerViews.isEmpty()) {
                BindingViewHolder viewHolderForAdapterPosition = (BindingViewHolder) mRecyclerViews.get(0).findViewHolderForAdapterPosition(parentIndex);
                baseExpandableObservable.onExpansionToggled(viewHolderForAdapterPosition, parentIndex, true);
            }
            if (expansionTriggeredByListItemClick && mExpandCollapseListener != null) {
                mExpandCollapseListener.onListItemExpanded(parentIndex);
            }
        }
    }


    /**
     * @param baseExpandableObservable         {@link BaseExpandableObservable}
     * @param parentIndex                      item index
     * @param collapseTriggeredByListItemClick
     */

    private void collapseListItem(int parentIndex, BaseExpandableObservable baseExpandableObservable, boolean collapseTriggeredByListItemClick) {
        if (baseExpandableObservable.isExpand.get()) {
            ObservableArrayList<Object> childItemList = baseExpandableObservable.getChildList();
            if (childItemList != null && !childItemList.isEmpty()) {
                int childListItemCount = childItemList.size();
                for (int i = childListItemCount - 1; i >= 0; i--) {
                    int index = parentIndex + i + 1;
                    Object o = mDataList.get(index);
                    if (o instanceof BaseExpandableObservable) {
                        BaseExpandableObservable parentListItem;
                        try {
                            parentListItem = (BaseExpandableObservable) o;
                            collapseListItem(index, parentListItem, false);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }

                    }
                    mDataList.remove(index);
                }

                notifyItemRangeRemoved(parentIndex + 1, childListItemCount);
                baseExpandableObservable.setIsExpand(false);
                if (!mRecyclerViews.isEmpty()) {
                    BindingViewHolder viewHolderForAdapterPosition = (BindingViewHolder) mRecyclerViews.get(0).findViewHolderForAdapterPosition(parentIndex);
                    baseExpandableObservable.onExpansionToggled(viewHolderForAdapterPosition, parentIndex, false);
                }
                notifyItemRangeChanged(parentIndex + 1, mDataList.size() - parentIndex - 1);
            }

            if (collapseTriggeredByListItemClick && mExpandCollapseListener != null) {
                int expandedCountBeforePosition = getExpandedItemCount(parentIndex);
                mExpandCollapseListener.onListItemCollapsed(parentIndex - expandedCountBeforePosition);
            }
        }
    }

    /**
     * Collapses all parents in the list.
     */
    public void collapseAllParents() {
        if (mDataList != null && !mDataList.isEmpty()) {
            ArrayList<Object> expandableListItems = getParents(true);
            if (expandableListItems != null && !expandableListItems.isEmpty()) {
                final int expandedItemSize = expandableListItems.size();
                if (expandedItemSize > 0) {
                    for (int i = 0; i < expandedItemSize; i++) {
                        Object o = expandableListItems.get(i);
                        int indexOf = mDataList.indexOf(o);
                        if (indexOf >= 0)
                            collapseListItem(indexOf, (BaseExpandableObservable) o, false);
                    }
                }
            }
        }
    }

    /**
     * @param isExpanded
     * @return return all expanded items
     */
    @NonNull
    private ArrayList<Object> getParents(boolean isExpanded) {
        final int size = mDataList.size();
        ArrayList<Object> expandableListItems = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            Object o = mDataList.get(i);
            if (o instanceof BaseExpandableObservable) {
                BaseExpandableObservable expandableListItem = (BaseExpandableObservable) o;
                if (isExpanded) {
                    if (expandableListItem.isExpand.get())
                        expandableListItems.add(o);
                } else {
                    if (!expandableListItem.isExpand.get())
                        expandableListItems.add(o);
                }
            }
        }
        return expandableListItems;
    }

    /**
     * Gets the number of expanded child list items before the specified position.
     *
     * @param position The index before which to return the number of expanded
     *                 child list items
     * @return The number of expanded child list items before the specified position
     */
    private int getExpandedItemCount(int position) {
        if (position == 0) {
            return 0;
        }

        int expandedCount = 0;
        for (int i = 0; i < position; i++) {
            Object listItem = getListItem(i);
            if (!(listItem instanceof BaseExpandableObservable)) {
                expandedCount++;
            }
        }
        return expandedCount;
    }

    /**
     * Gets the list item held at the specified adapter position.
     *
     * @param position The index of the list item to return
     * @return The list item at the specified position
     */
    protected Object getListItem(int position) {
        boolean indexInRange = position >= 0 && position < mDataList.size();
        if (indexInRange) {
            return mDataList.get(position);
        } else {
            return null;
        }
    }

    @Override
    public int getItemViewType(int position) {
        mItemType = getItemViewType(mDataList.get(position));
        return mUtil.getIntType(mItemType);
    }

    public int getItemViewType(Object t) {
        return -1;
    }

    /**
     * @param type item type
     * @return item layout
     */
    @LayoutRes
    @NonNull
    public abstract int getItemLayout(int type);

    @Override
    public BindingViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new BindingViewHolder(DataBindingUtil.inflate(mLayoutInflater, getItemLayout(mItemType), parent, false));
    }

    @Override
    public void onBindViewHolder(BindingViewHolder holder, int position) {
        Object o = mDataList.get(position);
        ViewDataBinding binding = holder.getBinding();
        binding.setVariable(getVariable(o, position), o);
        binding.executePendingBindings();
        if (o instanceof BaseExpandableObservable) {
            BaseExpandableObservable baseExpandableObservable = (BaseExpandableObservable) o;
            ObservableArrayList<Object> childList = baseExpandableObservable.getChildList();
            if (childList != null && !childList.isEmpty())
                baseExpandableObservable.setItemListExpandCollapseListener(this);
        }
    }

    /**
     * @param o     item object
     * @param index item index
     * @return item BR type
     */
    public abstract int getVariable(Object o, int index);

    /**
     * @return data list
     */
    public List<?> getDataList() {
        return mDataList;
    }

    @Override
    public int getItemCount() {
        return mDataList == null ? 0 : mDataList.size();
    }

    public interface ExpandCollapseListener {

        /**
         * Called when a list item is expanded.
         *
         * @param position The index of the item in the list being expanded
         */
        void onListItemExpanded(int position);

        /**
         * Called when a list item is collapsed.
         *
         * @param position The index of the item in the list being collapsed
         */
        void onListItemCollapsed(int position);
    }

    public void setExpandCollapseListener(ExpandCollapseListener expandCollapseListener) {
        mExpandCollapseListener = expandCollapseListener;
    }
}