/* Tencent is pleased to support the open source community by making Hippy available.
 * Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.recyclerview.widget;

import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import android.view.View;
import android.view.ViewGroup;
import com.tencent.mtt.hippy.uimanager.RenderManager;
import com.tencent.renderer.node.RenderNode;
import com.tencent.mtt.hippy.views.hippylist.HippyRecyclerViewHolder;
import com.tencent.mtt.hippy.views.hippylist.NodePositionHelper;
import com.tencent.renderer.NativeRender;
import com.tencent.renderer.NativeRendererManager;

/**
 * Created on 2021/1/4. Description
 *
 * 继承RecycledViewPool，主要用于renderNode节点的精确命中，不能从RecycledViewPool里面随意取一个node
 * 所有重写了getRecycledView方法；putRecycledView也需要检测缓存是否会抛弃viewHolder，如果抛弃需要把
 * 事件同步给RenderManager进行相应的节点删除。
 */
public class HippyRecyclerPool extends RecyclerView.RecycledViewPool {

    private final View recyclerView;
    private final HippyRecyclerExtension viewCacheExtension;
    private final NodePositionHelper nodePositionHelper;
    private HippyViewHolderAbandonListener viewHolderListener;

    public HippyRecyclerPool(View recyclerView, HippyRecyclerExtension viewCacheExtension, NodePositionHelper nodePositionHelper) {
        this.nodePositionHelper = nodePositionHelper;
        this.recyclerView = recyclerView;
        this.viewCacheExtension = viewCacheExtension;
    }

    public void setViewHolderAbandonListener(HippyViewHolderAbandonListener viewHolderListener) {
        this.viewHolderListener = viewHolderListener;
    }

    /**
     * 从缓存池里面获取ViewHolder进行复用 1、精确命中相同的renderNode 2、命中相同Type的ViewHolder，并且对应的RenderNode是没有被前端删除的
     * 如果renderNode.isDelete为true,说明前端删除了RenderNode， 此时会调用 RenderManager框架的deleteChild,
     * 所以view也不会存在了。 即使找到了相同type的Holder，也不能复用了。
     */
    @Override
    public ViewHolder getRecycledView(int viewType) {
        ScrapData scrapData = mScrap.get(viewType);
        if (scrapData == null) {
            return null;
        }
        ViewHolder delegateHolder = null;
        for (ViewHolder holder : scrapData.mScrapHeap) {
            if (isTheSameRenderNode((HippyRecyclerViewHolder) holder)) {
                scrapData.mScrapHeap.remove(holder);
                delegateHolder = holder;
                break;
            }
        }
        //没有精确命中，再看看缓存池里面有没有相同类型的viewType
        if (delegateHolder == null) {
            delegateHolder = super.getRecycledView(viewType);
        }
        //检测对应的节点是否被删除
        if (delegateHolder instanceof HippyRecyclerViewHolder
                && ((HippyRecyclerViewHolder) delegateHolder).isRenderDeleted()) {
            return null;
        }
        return delegateHolder;
    }

    @Override
    public void putRecycledView(ViewHolder scrap) {
        checkViewHolderAbandoned(scrap);
        super.putRecycledView(scrap);
    }

    private void checkViewHolderAbandoned(ViewHolder scrap) {
        int viewType = scrap.getItemViewType();
        ScrapData scrapData = this.mScrap.get(viewType);
        if (scrapData != null && scrapData.mScrapHeap.size() >= scrapData.mMaxScrap) {
            viewHolderListener.onViewHolderAbandoned((HippyRecyclerViewHolder) scrap);
        }
    }

    /**
     * 是否是节点完全相等
     *
     * @param scrapHolder 缓存池里面的Holder
     */
    private boolean isTheSameRenderNode(HippyRecyclerViewHolder scrapHolder) {
        NativeRender nativeRender = NativeRendererManager.getNativeRenderer(recyclerView.getContext());
        if (scrapHolder.bindNode == null || nativeRender == null) {
            return false;
        }
        RenderNode nodeForCurrent = RenderManager.getRenderNode(recyclerView)
                .getChildAt(nodePositionHelper.getRenderNodePosition(viewCacheExtension.getCurrentPosition()));
        return scrapHolder.bindNode.equals(nodeForCurrent);
    }
}
