package com.youzan.titan;

import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;

import com.youzan.titan.holder.EmptyViewHolder;
import com.youzan.titan.holder.HeaderViewHolder;
import com.youzan.titan.holder.LoadMoreViewHolder;
import com.youzan.titan.internal.ItemClickSupport;

import java.util.List;

/**
 * Created by monster on 15/11/30.
 */
public abstract class TitanAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder>
        implements View.OnClickListener, View.OnLongClickListener {

    final static int HEADER_TYPE = Integer.MIN_VALUE;
    final static int FOOTER_TYPE = Integer.MAX_VALUE - 1;
    final static int MORE_TYPE = Integer.MAX_VALUE;
    final static int EMPTY_TYPE = Integer.MAX_VALUE - 2;

    private View mCustomLoadMoreView;
    private View mHeaderView;
    private View mFooterView;
    private View mEmptyView;
    private View mDefaultView;
    private View mBadNetView;
    private View mEmptyHolderView;

    private boolean mIsHeadViewEmpty = false;
    private boolean mIsFootViewEmpty = false;
    private boolean mIsEmptyViewEnable = false;

    @LayoutRes
    private int mLoadMoreResId;

    private boolean mHasMore;
    private boolean mHasHeader;
    private boolean mHasFooter;

    protected List<T> mData;

    private ItemClickSupport mItemClickSupport;

    /**
     * Create view holder according to view types.
     *
     * @param parent   the parent view group
     * @param viewType the view type
     * @return the created view holder
     */
    protected abstract RecyclerView.ViewHolder createVHolder(ViewGroup parent, int viewType);

    /**
     * Show item view.
     *
     * @param holder   the view holder
     * @param position the position of the view holder
     */
    protected abstract void showItemView(RecyclerView.ViewHolder holder, int position);

    /**
     * Get adpater item id by position.
     *
     * @param position item position
     * @return adapter item id
     */
    public abstract long getAdapterItemId(int position);

    /**
     * Get the count of adapter items.
     *
     * @return the count of adapter items
     */
    public int getAdapterItemCount() {
        return null != mData ? mData.size() : 0;
    }


    /**
     * {@inheritDoc}
     */
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        RecyclerView.ViewHolder holder;
        switch (viewType) {
            case MORE_TYPE:
                holder = getMoreViewHolder(parent);
                break;
            case HEADER_TYPE:
                holder = getHeaderViewHolder(parent);
                break;
            case FOOTER_TYPE:
                holder = new EmptyViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_default_empty_view, parent, false));
                break;
            case EMPTY_TYPE:
                holder = new EmptyViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_default_empty_view, parent, false));
                break;
            default:
                holder = createVHolder(parent, viewType);
                break;
        }
        return holder;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

        int viewType = holder.getItemViewType();
        switch (viewType) {
            case MORE_TYPE:
                holder.itemView.setVisibility(getItemCount() > getCustomsNum() && mHasMore ? View.VISIBLE : View.GONE);
                break;
            case HEADER_TYPE:
                holder.itemView.setVisibility(mHasHeader ? View.VISIBLE : View.GONE);
                break;
            case FOOTER_TYPE:
                if (mHasFooter && !mHasMore) {
                    ((EmptyViewHolder) holder).container.removeAllViews();
                    ((EmptyViewHolder) holder).container.getLayoutParams().height = mFooterView.getLayoutParams().height;
                    ((EmptyViewHolder) holder).container.getLayoutParams().width = mFooterView.getLayoutParams().width;
                    ((EmptyViewHolder) holder).container.addView(mFooterView);
                } else {
                    ((EmptyViewHolder) holder).container.removeAllViews();
                    ((EmptyViewHolder) holder).container.getLayoutParams().height = 0;
                    ((EmptyViewHolder) holder).container.getLayoutParams().width = 0;
                }
                break;
            case EMPTY_TYPE:
                ((EmptyViewHolder) holder).container.removeAllViews();
                if (null == mEmptyHolderView.getLayoutParams()) {
                    mEmptyHolderView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
                }
                ((EmptyViewHolder) holder).container.getLayoutParams().height = mEmptyHolderView.getLayoutParams().height;
                ((EmptyViewHolder) holder).container.getLayoutParams().width = mEmptyHolderView.getLayoutParams().width;
                ((EmptyViewHolder) holder).container.addView(mEmptyHolderView);
                break;
            default:
                holder.itemView.setOnClickListener(this);
                holder.itemView.setOnLongClickListener(this);
                showItemView(holder, mHasHeader ? position - 1 : position);
                break;
        }
    }

    @Override
    public int getItemCount() {
        int customTypeCount = 0;
        if (mHasMore && null != mData && 0 < mData.size()) {
            customTypeCount++;
        }
        if (mHasHeader) {
            customTypeCount++;
        }
        if (mHasFooter) {
            customTypeCount++;
        }

        if ((null == mData || 0 == mData.size()) && mIsEmptyViewEnable) {
            customTypeCount = 1;
            if (!mIsFootViewEmpty && mHasFooter) {
                customTypeCount++;
            }
            if (!mIsHeadViewEmpty && mHasHeader) {
                customTypeCount++;
            }
        }

        return getAdapterItemCount() + customTypeCount;
    }

    @Override
    public long getItemId(int position) {
        if (mHasMore && 0 != position && getItemCount() - 1 == position) {
            return -1;
        }
        return getAdapterItemId(mHasHeader ? position - 1 : position);
    }

    public T getItem(int position) {
        return null != mData && position <= mData.size() - 1 ? mData.get(position) : null;
    }

    @Override
    public int getItemViewType(int position) {

        boolean isDataEmpty = null == mData || 0 == mData.size();

        if (mIsEmptyViewEnable && isDataEmpty) {

            if (!mIsHeadViewEmpty && mHasHeader && 0 == position) {
                return HEADER_TYPE;
            }

            if (!mIsFootViewEmpty && mHasFooter && getItemCount() - 1 == position) {
                return FOOTER_TYPE;
            }

            return EMPTY_TYPE;
        } else {

            if (mHasHeader && 0 == position) {
                return HEADER_TYPE;
            }

            if (mHasFooter && getItemCount() - 1 == position) {
                return FOOTER_TYPE;
            }

            if (mHasMore && !isDataEmpty) {
                if (!mHasFooter && getItemCount() - 1 == position) {
                    return MORE_TYPE;
                } else if (mHasFooter && getItemCount() - 2 == position) {
                    return MORE_TYPE;
                }
            }
        }

        return getAttackItemViewType(mHasHeader ? position - 1 : position);
    }

    /**
     * 设置emptyView
     * 优先级 default empty badNet
     *
     * @param isHeadViewEmpty is headerview empty that show emptyview
     * @param isFootViewEmpty is footerview empty that show emptyview
     * @param emptyView       custom empty view
     */
    public void setEmptyView(boolean isHeadViewEmpty, boolean isFootViewEmpty, View emptyView) {
        mEmptyView = emptyView;
        initEmptyParams(isHeadViewEmpty, isFootViewEmpty);
    }

    /**
     * 设置emptyView isHeadViewEmpty and isFootViewEmpty is true
     * 优先级 default empty badNet
     *
     * @param emptyView
     */
    public void setEmptyView(View emptyView) {
        setEmptyView(true, true, emptyView);
    }

    public View getEmptyView() {
        return mEmptyView;
    }

    public void showEmptyView() {
        mEmptyHolderView = mEmptyView;
        notifyDataSetChanged();
    }

    /**
     * 设置defaultView
     * 优先级 default empty badNet
     *
     * @param isHeadViewEmpty
     * @param isFootViewEmpty
     * @param defaultView
     */
    public void setDefaultView(boolean isHeadViewEmpty, boolean isFootViewEmpty, View defaultView) {
        mDefaultView = defaultView;
        initEmptyParams(isHeadViewEmpty, isFootViewEmpty);
    }

    /**
     * 设置defaultView
     * 优先级 default empty badNet
     *
     * @param defaultView
     */
    public void setDefaultView(View defaultView) {
        setDefaultView(true, true, defaultView);
    }

    public View getDefaultView() {
        return mDefaultView;
    }

    public void showDefaultView() {
        mEmptyHolderView = mDefaultView;
        notifyDataSetChanged();
    }

    /**
     * 设置badNetView
     * 优先级 default empty badNet
     *
     * @param isHeadViewEmpty
     * @param isFootViewEmpty
     * @param badNetView
     */
    public void setBadNetView(boolean isHeadViewEmpty, boolean isFootViewEmpty, View badNetView) {
        mBadNetView = badNetView;
        initEmptyParams(isHeadViewEmpty, isFootViewEmpty);
    }

    /**
     * 设置badNetView
     * 优先级 default empty badNet
     *
     * @param badNetView
     */
    public void setBadNetView(View badNetView) {
        setBadNetView(true, true, badNetView);
    }

    public View getBadNetView() {
        return mBadNetView;
    }

    public void showBadNetView() {
        mEmptyHolderView = mBadNetView;
        notifyDataSetChanged();
    }

    private void initEmptyParams(boolean isHeadViewEmpty, boolean isFootViewEmpty) {
        mIsHeadViewEmpty = isHeadViewEmpty;
        mIsFootViewEmpty = isFootViewEmpty;
        mIsEmptyViewEnable = true;
        initEmptyHolderView();
    }

    private void initEmptyHolderView() {
        if (null != mDefaultView) {
            mEmptyHolderView = mDefaultView;
        } else if (null != mEmptyView) {
            mEmptyHolderView = mEmptyView;
        } else if (null != mBadNetView) {
            mEmptyHolderView = mBadNetView;
        }
    }

    /**
     * 使用TitanRecyclerView专用getItemViewType
     *
     * @param position
     * @return
     */
    protected int getAttackItemViewType(int position) {
        return 0;
    }

    protected RecyclerView.ViewHolder getMoreViewHolder(ViewGroup parent) {
        if (null != mCustomLoadMoreView) {
            return new LoadMoreViewHolder(mCustomLoadMoreView);
        }
        if (0 != mLoadMoreResId) {
            mCustomLoadMoreView = LayoutInflater.from(parent.getContext()).inflate(mLoadMoreResId, parent, false);
        } else {
            mCustomLoadMoreView = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_default_more_view, parent, false);
        }
        return new LoadMoreViewHolder(mCustomLoadMoreView);
    }

    protected RecyclerView.ViewHolder getHeaderViewHolder(ViewGroup parent) {

        if (null == mHeaderView.getLayoutParams()) {
            setViewGroupLp(mHeaderView);
        }
        return new HeaderViewHolder(mHeaderView);
    }

    public void setCustomLoadMoreView(View customView) {
        mLoadMoreResId = 0;
        mCustomLoadMoreView = customView;
    }

    public void setCustomLoadMoreView(int resourceId) {
        mCustomLoadMoreView = null;
        mLoadMoreResId = resourceId;
    }

    /**
     * use TitanRecyclerView as root(ViewGroup)
     *
     * @param headerView
     */
    public void setHeaderView(View headerView) {
        this.mHeaderView = headerView;
        this.mHasHeader = true;
    }

    public void removeHeaderView() {
        this.mHeaderView = null;
        this.mHasHeader = false;
        notifyDataSetChanged();
    }

    /**
     * use TitanRecyclerView as root(ViewGroup)
     *
     * @param footerView
     */
    public void setFooterView(View footerView) {
        setFooterView(footerView, LinearLayout.VERTICAL);
    }

    public void setFooterView(View footerView, int orientation) {
        if (null == footerView.getLayoutParams()) {
            if (LinearLayout.VERTICAL == orientation) {
                footerView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
            } else {
                footerView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT));
            }
        }
        this.mFooterView = footerView;
        this.mHasFooter = true;
    }

    public void removeFooterView() {
        this.mFooterView = null;
        this.mHasFooter = false;
        notifyDataSetChanged();
    }

    public void setHasMore(boolean hasMore) {
        this.mHasMore = hasMore;
        notifyDataSetChanged();
    }

    public boolean hasMore() {
        return this.mHasMore;
    }

    /**
     * 获取自定义的数量
     * notifyItemInserted等一系列的操作
     * 都是针对recyclerview的所有item
     * 只针对mData操作时需考虑自定义数
     *
     * @return
     */
    public int getCustomHeaderNum() {
        int customHeaderNum = 0;
        if (mHasHeader) {
            ++customHeaderNum;
        }
        return customHeaderNum;
    }

    /**
     * 获取所有自定义数量
     *
     * @return
     */
    private int getCustomsNum() {
        int customs = 0;
        customs = mHasFooter ? ++customs : customs;
        customs = mHasHeader ? ++customs : customs;
        customs = mHasMore ? ++customs : customs;
        if ((null == mData || 0 == mData.size()) && mIsEmptyViewEnable) {
            customs = 1;
            customs = mHasFooter && !mIsFootViewEmpty ? ++customs : customs;
            customs = mHasHeader && !mIsHeadViewEmpty ? ++customs : customs;
        }
        return customs;
    }

    public void addDataEnd(T data) {
        if (data != null && null != mData) {
            int startIndex = this.mData.size();
            this.mData.add(data);
            filterData(this.mData);
            notifyItemInserted(startIndex + getCustomHeaderNum());
        }
    }

    public void addData(T data, int index) {
        if (null != data && null != mData && 0 <= index && getAdapterItemCount() >= index) {
            this.mData.add(index, data);
            filterData(mData);
            notifyItemInserted(index + getCustomHeaderNum());
        }
    }

    public void addDataEnd(List<T> data) {
        if (null == mData) {
            setData(data);
        } else if (data != null && data.size() > 0 && data != this.mData) {
            int startIndex = this.mData.size();
            this.mData.addAll(data);
            filterData(this.mData);
            notifyItemRangeInserted(startIndex + getCustomHeaderNum(), data.size());
        }
    }

    public void addDataTop(List<T> data) {
        if (null == mData) {
            setData(data);
        } else if (null != data && 0 < data.size() && data != this.mData) {
            this.mData.addAll(0, data);
            filterData(mData);
            notifyItemRangeInserted(getCustomHeaderNum(), data.size());
        }
    }

    public void addDataTop(T data) {
        if (null != data && null != mData) {
            this.mData.add(0, data);
            filterData(mData);
            notifyItemInserted(getCustomHeaderNum());
        }
    }

    public void remove(T data) {
        if (data != null && null != mData) {
            if (mData.contains(data)) {
                int startIndex = this.mData.indexOf(data);
                mData.remove(data);
                if (startIndex != -1) {
                    filterData(this.mData);
                    notifyItemRemoved(startIndex + getCustomHeaderNum());
                }

            }
        }
    }

    public void remove(int index) {
        if (null != mData && 0 <= index && getAdapterItemCount() >= index) {
            mData.remove(index);
            filterData(mData);
            notifyItemRemoved(index + getCustomHeaderNum());
        }
    }

    public void update(T data) {
        if (data != null && null != mData) {
            int startIndex = this.mData.indexOf(data);
            if (startIndex != -1) {
                mData.set(startIndex, data);
            }
            notifyItemChanged(startIndex + getCustomHeaderNum());
        }
    }

    public void update(int index, T data) {
        if (null != mData && index >= 0 && index < mData.size() && data != null) {
            mData.set(index, data);
            filterData(this.mData);
            notifyItemChanged(index + getCustomHeaderNum());
        }
    }

    public List<T> getData() {
        return mData;
    }

    public void setData(@NonNull List<T> data) {
        mData = data;
        notifyDataSetChanged();
    }

    public void clearData() {
        if (null != mData) {
            this.mData.clear();
            notifyDataSetChanged();
        }
    }

    public void setItemClickSupport(ItemClickSupport itemClickSupport) {
        this.mItemClickSupport = itemClickSupport;
    }

    public boolean hasHeader() {
        return mHasHeader;
    }

    public boolean hasFooter() {
        return mHasFooter;
    }

    public void filterData(List<T> data) {
        // Dummy
    }

    @Override
    public void onClick(View v) {
        if (null != mItemClickSupport) {
            mItemClickSupport.onItemClick(v);
        }
    }

    @Override
    public boolean onLongClick(View v) {
        if (null != mItemClickSupport) {
            return mItemClickSupport.onItemLongClick(v);
        }
        return false;
    }

    private void setViewGroupLp(View view) {
        ViewGroup.LayoutParams layoutParams = new ViewGroup.
                LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        view.setLayoutParams(layoutParams);
    }
}