package com.gh.gamecenter.common.baselist;

import android.annotation.SuppressLint;
import android.content.Context;

import com.gh.gamecenter.common.utils.ExtensionsKt;
import com.lightgame.adapter.BaseRecyclerAdapter;

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

import androidx.recyclerview.widget.DiffUtil;

/**
 * Created by khy on 10/11/17.
 */

public abstract class ListAdapter<DataType> extends BaseRecyclerAdapter {

    public static final int FOOTER_ITEM_COUNT = 1;
    public static final int TOP_ITEM_COUNT = 1;

    protected List<DataType> mEntityList = new ArrayList<>();

    protected boolean mIsOver;

    protected boolean mIsNetworkError;

    protected boolean mIsLoading;

    public ListAdapter(Context context) {
        super(context);
    }

    @SuppressLint("StaticFieldLeak")
    protected void setListData(List<DataType> updateData) {
        if (updateData == null) {
            mEntityList = new ArrayList<>();
            notifyDataSetChanged();
            return;
        }
        // 避免手动刷新列表时出现异常动画
        if (mEntityList != null && mEntityList.size() > updateData.size()) {
            mEntityList = new ArrayList<>(updateData);
            notifyDataSetChanged();
            return;
        }

        calculateDiff(updateData);
    }

    protected void calculateDiff(List<DataType> updateData) {
        // 若直接传 updateData 给 DiffUtils 可能因为其它地方操作 updateData 这个列表而出现 ArrayIndexOutOfBounds 异常
        // 这里用新的数组包裹原数据
        ArrayList<DataType> updateDataCopy = new ArrayList<>(updateData);

        ListExecutor.getWorkerExecutor().execute(() -> {
            try {
                DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffUtil.Callback() {
                    @Override
                    public int getOldListSize() {
                        return mEntityList == null ? 0 : mEntityList.size();
                    }

                    @Override
                    public int getNewListSize() {
                        return updateDataCopy.size();
                    }

                    @Override
                    public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
                        if (oldItemPosition >= mEntityList.size()) return false;
                        if (newItemPosition >= updateDataCopy.size()) return false;

                        DataType oldItem = ExtensionsKt.safelyGetInRelease(mEntityList, oldItemPosition);
                        DataType newItem = ExtensionsKt.safelyGetInRelease(updateDataCopy, newItemPosition);
                        return ListAdapter.this.areItemsTheSame(oldItem, newItem);
                    }

                    @Override
                    public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
                        if (oldItemPosition >= mEntityList.size()) return false;
                        if (newItemPosition >= updateDataCopy.size()) return false;

                        DataType oldItem = ExtensionsKt.safelyGetInRelease(mEntityList, oldItemPosition);
                        DataType newItem = ExtensionsKt.safelyGetInRelease(updateDataCopy, newItemPosition);
                        return ListAdapter.this.areContentsTheSame(oldItem, newItem);
                    }
                });

                ListExecutor.getUiExecutor().execute(() -> {
                    mEntityList = new ArrayList<>(updateDataCopy);
                    diffResult.dispatchUpdatesTo(ListAdapter.this);
                });
            } catch (ArrayIndexOutOfBoundsException exception) {
                // 新旧数据比较的时候数组越界了(大概是别处修改了数据源 :( )，直接 notifyDataSetChanged
                ListExecutor.getUiExecutor().execute(() -> {
                    exception.printStackTrace();
                    mEntityList = new ArrayList<>(updateData);
                    notifyDataSetChanged();
                });
            }
        });
    }

    protected boolean areItemsTheSame(DataType oldItem, DataType newItem) {
        return oldItem == newItem;
    }

    protected boolean areContentsTheSame(DataType oldItem, DataType newItem) {
        return oldItem == newItem;
    }

    public boolean isNetworkError() {
        return mIsNetworkError;
    }

    public void loadChange(LoadStatus status) {
        switch (status) {
            case INIT_OVER:
            case LIST_OVER:
                mIsOver = true;
                mIsNetworkError = false;
                mIsLoading = false;
                break;
            case LIST_FAILED:
                mIsOver = false;
                mIsNetworkError = true;
                mIsLoading = false;
                break;
            case LIST_LOADED:
                mIsNetworkError = false;
                mIsOver = false;
                mIsLoading = false;
                break;
            case LIST_LOADING:
                mIsNetworkError = false;
                mIsOver = false;
                mIsLoading = true;
                break;
            case INIT:
                if (mEntityList != null) mEntityList.clear();
                mIsNetworkError = false;
                mIsOver = false;
                mIsLoading = true;
                notifyDataSetChanged();
                return;
        }

        if (getItemCount() > 0 && status != LoadStatus.LIST_LOADED)
            notifyItemChanged(getItemCount() - 1);
    }

    public List<DataType> getEntityList() {
        return mEntityList;
    }
}
