/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.weex.ui.component.list.template;


import android.support.v4.util.ArrayMap;
import android.text.TextUtils;

import com.alibaba.fastjson.JSONArray;
import org.apache.weex.WXEnvironment;
import org.apache.weex.bridge.WXBridgeManager;
import org.apache.weex.utils.WXUtils;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by furture on 2018/1/23.
 * template data manager, manage data and render state
 */
public class CellDataManager {

    private static final String VIRTUAL_COMPONENT_SEPRATOR = "@";

    public static final String SUB_COMPONENT_TEMPLATE_ID = "@templateId";

    public static final String VIRTUAL_COMPONENT_ID = "@virtualComponentId";

    /**
     * template list data, update should vis data manager interface, this field is read only
     * */
    JSONArray listData;

    /**
     * current template list
     * */
    public final WXRecyclerTemplateList templateList;

    /**
     * data render state for position, itemId、dirty、and other info
     * */
    private Map<Integer, CellRenderState> renderStates = new ArrayMap<>();


    /**
     * list virtual component data, rendet state
     * key virtual component id, value is cell render state.
     * */
    private Map<String, CellRenderState> virtualComponentRenderStates;



    /**
     * template list data manager
     * */
    public CellDataManager(WXRecyclerTemplateList templateList) {
        this.templateList = templateList;
    }

    /**
     * get current render state for current position, never return null.
     * */
    public CellRenderState getRenderState(int position){
        CellRenderState renderState = renderStates.get(position);
        if(renderState == null){
            renderState = new CellRenderState();
            renderState.position = position;
            renderStates.put(position, renderState);
        }
        if(position != renderState.position) {
            renderState.position = position;
            renderState.hasPositionChange = true;
        }
        return renderState;
    }

    /**
     * @param  virutalComponentId virutalComponentId
     * @param  data   update    virutalComponent's data
     * update virtual component data
     * */
    public void updateVirtualComponentData(String virutalComponentId, Object data){
        if(virtualComponentRenderStates != null){
            CellRenderState cellRenderState = virtualComponentRenderStates.get(virutalComponentId);
            if(cellRenderState != null){
                cellRenderState.getVirtualComponentDatas().put(virutalComponentId, data);
                cellRenderState.hasVirtualCompoentUpdate = true;
            }else{
                if(WXEnvironment.isApkDebugable()) {
                    throw new IllegalArgumentException("virtualComponentDatas illegal state empty render state" + virutalComponentId);
                }
            }
        }else{
            if(WXEnvironment.isApkDebugable()){
                throw  new IllegalArgumentException("virtualComponentDatas illegal state " + virutalComponentId);
            }
        }
    }

    /**
     * @param  position  current position virtual component
     * @param  virutalComponentId virutalComponentId
     * @param  data   update    virutalComponent's data
     * create virtual component data
     * */
    public void createVirtualComponentData(int position, String virutalComponentId, Object data){
        if(virtualComponentRenderStates == null){
            virtualComponentRenderStates = new HashMap<>(8);
        }
        CellRenderState renderState = renderStates.get(position);
        renderState.getVirtualComponentDatas().put(virutalComponentId, data);
        virtualComponentRenderStates.put(virutalComponentId, renderState);

    }




    /**
     * @param  listData setList data
     * clear render state for current cell, and virtual component data
     * */
    public void setListData(JSONArray listData) {
        if(this.listData != listData) {
            if(this.listData != null){
                if(WXUtils.getBoolean(templateList.getAttrs().get("exitDetach"), true)){
                    for(int i=0; i<this.listData.size(); i++){
                        cleanRenderState(renderStates.remove(i));
                    }
                }
            }
            this.listData = listData;
            renderStates.clear();
            if (virtualComponentRenderStates != null) {
                virtualComponentRenderStates.clear();
            }
        }
    }

    /**
     * @param index insert index
     * @param  data  data object
     * insert data at current index
     * */
    public boolean insertData(int index, Object data){
        listData.add(index, data);
        boolean renderStateChange = false;
        for(int i=listData.size(); i>= index; i--){
            CellRenderState state = renderStates.remove(i);
            if(state != null){
                renderStates.put(i + 1, state);
                renderStateChange = true;
            }
        }
        return renderStateChange;
    }

    /**
     *
     * @param index insert index
     * @param  data  data object
     * insert data at current index
     * */
    public boolean insertRange(int index, JSONArray data){
        listData.addAll(index, data);
        boolean renderStateChange = false;
        for(int i = listData.size()-1; i >= index; i--){
            CellRenderState state = renderStates.remove(i);
            if(state != null){
                renderStates.put(i + 1, state);
                renderStateChange = true;
            }
        }
        return renderStateChange;
    }

    /**
     * @param  data  data object
     * @param index insert index
     * update data, reset new render state,
     * return true if only data changed, false if viewType changed
     * */
    public boolean  updateData(Object data, int index){
        boolean onlyDataChange = TextUtils.equals(templateList.getTemplateKey(index), templateList.getTemplateKey(data));
        listData.set(index, data);
        if(!onlyDataChange){
            //structure changed, reset render state
            cleanRenderState(renderStates.remove(index));
        }else{
            CellRenderState renderState = renderStates.get(index);
            if(renderState != null){
                renderState.hasDataUpdate = true;
            }
        }
        return onlyDataChange;
    }

    /**
     * @param index
     * remove data, and its render state  and  virtual's data
     * */
    public void  removeData(Integer index){
        listData.remove((int)index); //remove by index
        cleanRenderState(renderStates.remove(index));
        int count = listData.size() + 1;
        for(int i=index + 1; i < count; i++){
            CellRenderState state = renderStates.remove(i);
            if(state != null){
                renderStates.put(i-1, state);
            }
        }
    }

    /**
     *  clean render state, if has virtual component, call virtual component detach lifecycle
     * */
    private void  cleanRenderState(CellRenderState renderState){
        if(renderState == null){
            return;
        }
        if(renderState.hasVirtualComponents()){
            Collection<String> virtualComponentIds =  renderState.getVirtualComponentIds().values();
            for(String virtualComponentId : virtualComponentIds){
                if(virtualComponentRenderStates != null){
                    virtualComponentRenderStates.remove(virtualComponentId);
                }
                WXBridgeManager.getInstance().asyncCallJSEventVoidResult(WXBridgeManager.METHD_COMPONENT_HOOK_SYNC,
                        templateList.getInstanceId(),
                        null,
                        virtualComponentId, VirtualComponentLifecycle.LIFECYCLE, VirtualComponentLifecycle.DETACH, null);

            }
        }
    }


    /**
     * create virtualComponentId
     * */
    public static String createVirtualComponentId(String listRef, String viewTreeKey, long itemId){
        return  listRef + VIRTUAL_COMPONENT_SEPRATOR  + viewTreeKey + VIRTUAL_COMPONENT_SEPRATOR + itemId;
    }

    /**
     * get list ref from virtualComponentId
     * */
    public static String getListRef(String virtualComponentId){
        if(virtualComponentId == null){
            return null;
        }
        return virtualComponentId.split(VIRTUAL_COMPONENT_SEPRATOR)[0];
    }
    
}
