package com.sagacloud.utils.relationUtils;

import com.sagacloud.broadcast.MessageHandler;
import com.sagacloud.endpoints.Relation;
//import com.sagacloud.json.JSONArray;
//import com.sagacloud.json.JSONObject;
//import com.sagacloud.json.JSONValue;
import com.alibaba.fastjson.*;
import com.sagacloud.utils.objectLog.ActivityLogUtil;
import com.sagacloud.utils.CommonMethods;
import com.sagacloud.utils.CommonUtil;
import com.sagacloud.utils.Const;
import com.sagacloud.utils.objectLog.ActivityLogRunnable;
import com.sagacloud.utils.jsonHelper.property.PropertyGraphEnum;
import com.sagacloud.utils.jsonHelper.property.PropertyRel;
import com.sagacloud.utils.tables.GraphInstanceTable;
import com.sagacloud.utils.tables.HBaseTable;
import com.sagacloud.utils.tables.RelTable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * Created by Xiaoyu on 2018/3/8
 */


//  {
//        "graph_type": "Pe2Eq",
//        "graph_name": "资产和岗位关系",
//        "Content": [
//        {
//        "rel_name": "",
//        "directional": true,
//        "target_objs": "资产id->设备或部件id",
//        "rel_type": "1",
//        "description": ""
//        }
//        ]
//  }
//{
//  "graph_type": "Subset",
//  "graph_name": "从属关系",
//  "Content": [
//      {
//        "rel_name": "建筑下的资产",
//        "directional": true,
//        "target_objs": "",
//        "rel_type": "6",
//        "description": ""
//      },
//      {
//        "rel_name": "楼层下的资产",
//        "directional": true,
//        "target_objs": "",
//        "rel_type": "7",
//        "description": ""
//      }
//  ]
//}
public class PropertyRelUtil {
    private static final Logger log = LoggerFactory.getLogger(PropertyRelUtil.class);


    /**
     * @param originPjId 项目id
     * @param input      需要插入或修改的关系
     * @return false:zillion报错
     */
    public static boolean modify(String originPjId, ArrayList<PropertyRel> input) {
        if (input == null)
            return true;
        if (originPjId.startsWith(Const.OBJ_TYPE_PROJECT))
            originPjId = originPjId.substring(2);

        try {
            filterConflict(input);
            JSONArray insertObjs = new JSONArray();
            JSONArray deleteObjs = new JSONArray();
            classifyModifyArr(originPjId, input, insertObjs, deleteObjs);
            Set<String> gIds = extractGraphIds(insertObjs, deleteObjs);
            if (deleteObjs.size() != 0)
                CommonUtil.delete(true, HBaseTable.rel_btw_objs, deleteObjs);
            if (insertObjs.size() != 0)
                CommonUtil.insert(true, HBaseTable.rel_btw_objs, insertObjs);
            sendMsgForProperty(insertObjs,deleteObjs, input);
            //发类型为14的广播
            String pjId = originPjId;
            gIds.forEach(gId -> {
                if(!gId.isEmpty()){
                    MessageHandler.publish(String.format("{\"type\":14,\"projectId\":\"%s\",\"graphId\":\"%s\",\"description\":\"\"}", CommonMethods.getProject(pjId, true), gId));
                }
            });
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**提取图实例id
     * @param insertObjs
     * @param deleteObjs
     * @return
     */
    private static Set<String> extractGraphIds(JSONArray insertObjs, JSONArray deleteObjs){
        Set<String> graphIds = new HashSet<>();
        for (int i = 0; i < insertObjs.size(); i++) {
            if(insertObjs.getJSONObject(i).getString(RelTable.KEY_GRAPH_ID) != null){
                graphIds.add(insertObjs.getJSONObject(i).getString(RelTable.KEY_GRAPH_ID));
            }
        }
        for (int i = 0; i < deleteObjs.size(); i++) {
            if(deleteObjs.getJSONObject(i).getString(RelTable.KEY_GRAPH_ID) != null){
                graphIds.add(deleteObjs.getJSONObject(i).getString(RelTable.KEY_GRAPH_ID));
            }
        }
        return graphIds;
    }

    /**
     * @param pjId   项目id
     * @param idList 资产id列表 e.g. Pe+uuid
     * @return
     */
    public static boolean deleteAllRel(String pjId, ArrayList<String> idList) {
        if (pjId.startsWith(Const.OBJ_TYPE_PROJECT))
            pjId = pjId.substring(2);
        try {
            Map<String, String> graphMap = getGraphs(pjId, false);
            JSONArray deleteObjs = new JSONArray();
            for (int i = 0; i < idList.size(); ++i) {
                String propertyId = idList.get(i);
                JSONObject deleteNode = genRelNode(pjId, propertyId, null, graphMap.get(PropertyGraphEnum.Pe2Eq.name()), "1");
                deleteObjs.add(deleteNode);//删除资产和设备的关联关系
                deleteNode = genRelNode(pjId, null, propertyId, graphMap.get(PropertyGraphEnum.Subset.name()), "6");
                deleteObjs.add(deleteNode);//删除资产和建筑的关系
                deleteNode = genRelNode(pjId, null, propertyId, graphMap.get(PropertyGraphEnum.Subset.name()), "7");
                deleteObjs.add(deleteNode);//删除资产和楼层的关系
            }
            Set<String> gIds = extractGraphIds(new JSONArray(), deleteObjs);
            CommonUtil.delete(true, HBaseTable.rel_btw_objs, deleteObjs);
            String proId = pjId;
            //发类型为14的广播
            gIds.forEach(gId -> {
                if(!gId.isEmpty()){
                    MessageHandler.publish(String.format("{\"type\":14,\"projectId\":\"%s\",\"graphId\":\"%s\",\"description\":\"\"}", CommonMethods.getProject(proId, true), gId));
                }
            });
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 根据设备或部件id，删除与资产的关联关系
     * @param pjId
     * @param eqList 设备或部件id
     * @return
     */
    public static boolean deleteLinkByEq(String pjId, Set<String> eqList){
        if (pjId.startsWith(Const.OBJ_TYPE_PROJECT))
            pjId = pjId.substring(2);
        try {
            Map<String, String> graphMap = getGraphs(pjId, false);
            JSONArray deleteObjs = new JSONArray();
            for (String eqId : eqList) {
                JSONObject deleteNode = genRelNode(pjId, null, eqId, graphMap.get(PropertyGraphEnum.Pe2Eq.name()), "1");
                deleteObjs.add(deleteNode);//删除资产和设备的关联关系
            }
            CommonUtil.delete(true, HBaseTable.rel_btw_objs, deleteObjs);
            MessageHandler.publish(String.format("{\"type\":8,\"projectId\":\"%s\",\"name\":\"relationChange\",\"description\":\"\"}", CommonMethods.getProject(pjId,true)));
            if(graphMap.get(PropertyGraphEnum.Pe2Eq.name()) != null){
                MessageHandler.publish(String.format("{\"type\":14,\"projectId\":\"%s\",\"graphId\":\"%s\",\"description\":\"\"}", CommonMethods.getProject(pjId, true), graphMap.get(PropertyGraphEnum.Pe2Eq.name())));
            }
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * @param pjId   项目id
     * @param idList propertyId(Pe)
     * @return propertyId -> ["Bd","Fl","Eq","Ec"]
     */
    public static HashMap<String, HashSet<String>> queryByPropertyId(String pjId, HashSet<String> idList) {
        if (pjId.startsWith(Const.OBJ_TYPE_PROJECT))
            pjId = pjId.substring(2);
        HashMap<String, HashSet<String>> result = new HashMap<>();
        HashMap<String, String> fl2Bd;
        for (String id : idList) {//初始化result
            result.put(id, new HashSet<>());
        }
        try {
            Map<String, String> graphMap = getGraphs(pjId, false);

            JSONArray subsetQuery = new JSONArray();
            subsetQuery.add(genRelNode(pjId, null, null, graphMap.get(PropertyGraphEnum.Subset.name()), "1"));//查询建筑下的楼层
            subsetQuery.add(genRelNode(pjId, null, null, graphMap.get(PropertyGraphEnum.Subset.name()), "6"));//查询建筑下的资产
            subsetQuery.add(genRelNode(pjId, null, null, graphMap.get(PropertyGraphEnum.Subset.name()), "7"));//查询楼层下的资产
            JSONObject json = CommonUtil.combine_query(HBaseTable.rel_btw_objs, subsetQuery);
            JSONArray subSetContent = (JSONArray) json.get(Const.JSON_FIELD_Content);//Bd->Fl,Bd->Pe,Fl->Pe
            fl2Bd = getFl2BdMap(subSetContent);
            for (int i = 0; i < subSetContent.size(); ++i) {//返回结果中添加位置关系
                JSONObject obj = (JSONObject) subSetContent.get(i);
                String fromId = CommonUtil.combineId(obj.get(RelTable.KEY_PJ_ID).toString(),
                        obj.get(RelTable.KEY_FROM_OBJ_TYPE).toString(), obj.get(RelTable.KEY_FROM_OBJ_ID).toString());
                String toId = CommonUtil.combineId(pjId, obj.get(RelTable.KEY_TO_OBJ_TYPE).toString(), obj.get(RelTable.KEY_TO_OBJ_ID).toString());
                if (!toId.startsWith(Const.OBJ_TYPE_PROPERTY))
                    continue;
                if (result.containsKey(toId)) {
                    result.get(toId).add(fromId);
                    if (fromId.startsWith(Const.OBJ_TYPE_FLOOR) && fl2Bd.containsKey(fromId)) {//如果是楼层，再把建筑加进来
                        result.get(toId).add(fl2Bd.get(fromId));
                    }
                }
            }

            JSONArray pe2eqQuery = new JSONArray();
            pe2eqQuery.add(genRelNode(pjId, null, null, graphMap.get(PropertyGraphEnum.Pe2Eq.name()), "1"));
            json = CommonUtil.combine_query(HBaseTable.rel_btw_objs, pe2eqQuery);
            JSONArray pe2eqContent = (JSONArray) json.get(Const.JSON_FIELD_Content);
            for (int i = 0; i < pe2eqContent.size(); ++i) {//查询资产和设备关系
                JSONObject obj = (JSONObject) pe2eqContent.get(i);
                String fromId = CommonUtil.combineId(obj.get(RelTable.KEY_PJ_ID).toString(),
                        obj.get(RelTable.KEY_FROM_OBJ_TYPE).toString(), obj.get(RelTable.KEY_FROM_OBJ_ID).toString());//资产id
                String toId = CommonUtil.combineId(pjId, obj.get(RelTable.KEY_TO_OBJ_TYPE).toString(), obj.get(RelTable.KEY_TO_OBJ_ID).toString());//设备或部件id
                if (result.containsKey(fromId)) {
                    result.get(fromId).add(toId);
                }
            }

        } catch (Exception e) {

        }
        return result;
    }

    /**
     * @param pjId   项目id
     * @param idList objectId
     * @return objecctId -> PeId
     */
    public static HashMap<String, HashSet<String>> queryByObjId(String pjId, HashSet<String> idList) {
        HashMap<String, HashSet<String>> result = new HashMap<>();
        HashMap<String, HashSet<String>> tempResult = new HashMap<>();
        HashMap<String, Set<String>> bd2FlMap;
        if (pjId.startsWith(Const.OBJ_TYPE_PROJECT))
            pjId = pjId.substring(2);
        for (String id : idList) {//初始化result
            result.put(id, new HashSet<>());
        }
        try {
            Map<String, String> graphMap = getGraphs(pjId, false);

            JSONArray subsetQuery = new JSONArray();
            subsetQuery.add(genRelNode(pjId, null, null, graphMap.get(PropertyGraphEnum.Subset.name()), "1"));//查询建筑下的楼层
            subsetQuery.add(genRelNode(pjId, null, null, graphMap.get(PropertyGraphEnum.Subset.name()), "6"));//查询建筑下的资产
            subsetQuery.add(genRelNode(pjId, null, null, graphMap.get(PropertyGraphEnum.Subset.name()), "7"));//查询楼层下的资产
            JSONObject json = CommonUtil.combine_query(HBaseTable.rel_btw_objs, subsetQuery);
            JSONArray subSetContent = (JSONArray) json.get(Const.JSON_FIELD_Content);//Bd->Fl,Bd->Pe,Fl->Pe
            bd2FlMap = getBd2FlMap(subSetContent);
            for (int i = 0; i < subSetContent.size(); ++i) {//查询建筑、楼层下的资产
                JSONObject obj = (JSONObject) subSetContent.get(i);
                String fromId = CommonUtil.combineId(obj.get(RelTable.KEY_PJ_ID).toString(),
                        obj.get(RelTable.KEY_FROM_OBJ_TYPE).toString(), obj.get(RelTable.KEY_FROM_OBJ_ID).toString());
                String toId = CommonUtil.combineId(pjId, obj.get(RelTable.KEY_TO_OBJ_TYPE).toString(), obj.get(RelTable.KEY_TO_OBJ_ID).toString());
                if (!toId.startsWith(Const.OBJ_TYPE_PROPERTY))
                    continue;
                if (!tempResult.containsKey(fromId)) {
                    tempResult.put(fromId, new HashSet<>());
                }
                Set<String> properties = tempResult.get(fromId);
                properties.add(toId);
            }
            //2.查询设备、部件关联的资产
            JSONArray pe2eqQuery = new JSONArray();
            pe2eqQuery.add(genRelNode(pjId, null, null, graphMap.get(PropertyGraphEnum.Pe2Eq.name()), "1"));
            json = CommonUtil.combine_query(HBaseTable.rel_btw_objs, pe2eqQuery);
            JSONArray pe2eqContent = (JSONArray) json.get(Const.JSON_FIELD_Content);//Pe->Eq
            for (int i = 0; i < pe2eqContent.size(); ++i) {//查询资产和设备关系
                JSONObject obj = (JSONObject) pe2eqContent.get(i);
                String fromId = CommonUtil.combineId(obj.get(RelTable.KEY_PJ_ID).toString(),
                        obj.get(RelTable.KEY_FROM_OBJ_TYPE).toString(), obj.get(RelTable.KEY_FROM_OBJ_ID).toString());//资产id
                String toId = CommonUtil.combineId(pjId, obj.get(RelTable.KEY_TO_OBJ_TYPE).toString(), obj.get(RelTable.KEY_TO_OBJ_ID).toString());//设备或部件id
                if (!tempResult.containsKey(toId)) {
                    tempResult.put(toId, new HashSet<>());
                }
                Set<String> properties = tempResult.get(toId);
                properties.add(fromId);
            }
            //2.end

            for (String objId : result.keySet()) {
                if (tempResult.containsKey(objId))
                    result.get(objId).addAll(tempResult.get(objId));
                if (objId.startsWith(Const.OBJ_TYPE_BUILDING) && bd2FlMap.containsKey(objId)) {//如果objId是建筑，将属于该建筑的楼层中的资产也添加到建筑id下
                    for (String floor : bd2FlMap.get(objId)) {
                        if(tempResult.containsKey(floor)){
                            result.get(objId).addAll(tempResult.get(floor));
                        }
                    }
                }
            }

        } catch (Exception e) {
        }
        return result;
    }

    /**
     * 获取设备或部件关联的资产，或者资产关联的设备部件
     *
     * @param pjId
     * @param idList Peid,Eqid,Ecid
     * @return
     */
    public static HashMap<String, String> getLinks(String pjId, Set<String> idList) {
        if (pjId.startsWith(Const.OBJ_TYPE_PROJECT))
            pjId = pjId.substring(2);
        HashMap<String, String> result = new HashMap<>();
        try {
            Map<String, String> graphMap = getGraphs(pjId, false);
            String graphId = graphMap.get(PropertyGraphEnum.Pe2Eq.name());
            if (graphId == null) {
                return result;
            }
            JSONArray criterias = new JSONArray();
            JSONObject criteria = genRelNode(pjId, null, null, graphId, null);
            criterias.add(criteria);
            JSONObject re = CommonUtil.combine_query(HBaseTable.rel_btw_objs, criterias);//比query快
            JSONArray content = (JSONArray) re.get(Const.JSON_FIELD_Content);
            Hashtable<String, String> pe2EqMap = new Hashtable<>();
            Hashtable<String, String> eq2PeMap = new Hashtable<>();
            for (int i = 0; i < content.size(); ++i) {
                JSONObject obj = (JSONObject) content.get(i);
                String fromId = CommonUtil.combineId(obj.get(RelTable.KEY_PJ_ID).toString(),
                        obj.get(RelTable.KEY_FROM_OBJ_TYPE).toString(), obj.get(RelTable.KEY_FROM_OBJ_ID).toString());
                String toId = CommonUtil.combineId(pjId, obj.get(RelTable.KEY_TO_OBJ_TYPE).toString(), obj.get(RelTable.KEY_TO_OBJ_ID).toString());
                pe2EqMap.put(fromId, toId);
                eq2PeMap.put(toId, fromId);
            }
            for (String id : idList) {
                if (pe2EqMap.containsKey(id)) {
                    result.put(id, pe2EqMap.get(id));
                } else if (eq2PeMap.containsKey(id)) {
                    result.put(id, eq2PeMap.get(id));
                }
            }
            return result;
        } catch (Exception e) {
            log.error(e.getMessage());
            return result;
        }
    }

    /**
     * @param pjId
     * @return graphType -> graphId
     */
    private static Map<String, String> getGraphs(String pjId, boolean createWhenNotExist) throws Exception {
        Map<String, String> map = new Hashtable<>();
        if (pjId.startsWith(Const.OBJ_TYPE_PROJECT))
            pjId = pjId.substring(2);
        Set<String> set = new HashSet<>();
        set.add(pjId);
        Map<String, Map<String, String>> tempResult = getGraphs(set, createWhenNotExist);
        return tempResult.get(pjId);
    }

    /**
     * @param pjIds e.g. 1101080001
     * @return 1101080001 -> (Pe2Eq -> GtPe2Eq001)
     */
    private static Map<String, Map<String, String>> getGraphs(Set<String> pjIds, boolean createWhenNotExist) throws Exception {
//        Map<String, Map<String, String>> result = new Hashtable<>();
//        for (String pjId : pjIds) {
//            Map<String, String> graphMap = getGraphs(pjId, createWhenNotExist);
//            result.put(pjId, graphMap);
//        }
//        return result;
        Map<String, Map<String, String>> result = new Hashtable<>();
        //1.查询数据库中的图实例
        String time = CommonUtil.getTime();
        JSONArray criterias = new JSONArray();
        for (String pj : pjIds) {
            result.put(pj, new HashMap<>());//初始化result
            for (PropertyGraphEnum graphType : PropertyGraphEnum.values()) {
                JSONObject criteria = new JSONObject();
                criteria.put(GraphInstanceTable.KEY_PJ_ID, pj);
                criteria.put(GraphInstanceTable.KEY_GRAPH_TYPE, graphType.name());
                JSONObject range = new JSONObject();
                range.put("$gte", "0");
                range.put("$lte", time);
                criteria.put(GraphInstanceTable.KEY_BEGIN_TIME, range);
                range = new JSONObject();
                range.put("$gte", time);
                range.put("$lte", "9");
                criteria.put(GraphInstanceTable.KEY_END_TIME, range);
                criterias.add(criteria);
            }
        }
        JSONObject re = CommonUtil.batch_select_merge(HBaseTable.rel_btw_graph_period, criterias);
//        JSONObject re = CommonUtil.combine_query(HBaseTable.rel_btw_graph_period, criterias);
        JSONArray content = (JSONArray) re.get(Const.JSON_FIELD_Content);
        //1.end
        //2.将所有图实例id放到map中
        for (int i = 0; i < content.size(); ++i) {
            JSONObject graph = (JSONObject) content.get(i);
            Map<String, String> singleMap = result.get(graph.get(GraphInstanceTable.KEY_PJ_ID).toString());
            String graphType = graph.get(GraphInstanceTable.KEY_GRAPH_TYPE).toString();
            String sequencyId = graph.get(GraphInstanceTable.KEY_SEQUENCE_ID).toString();
            String graphId = Const.OBJ_TYPE_GRAPH + graphType + sequencyId;

            if (singleMap.containsKey(graphType)) {//如果同时起作用的图实例存在多张，则操作序号小的
                String existedId = singleMap.get(graphType);
                String tempSequencyId = existedId.substring(existedId.length() - 3);
                if (RelationUtil.compareId(sequencyId, tempSequencyId) > 0) {
                    sequencyId = tempSequencyId;
                    singleMap.put(graphType, Const.OBJ_TYPE_GRAPH + graphType + sequencyId);
                }
            } else {
                singleMap.put(graphType, graphId);
            }
        }
        //2.end
        if (createWhenNotExist) {
            for (String pj : pjIds) {
                Map<String, String> singleMap = result.get(pj);
                for (PropertyGraphEnum graphType : PropertyGraphEnum.values()) {
                    if (!singleMap.containsKey(graphType.name())) {
                        String graphId = createGraph(pj, graphType.name(), time);
                        singleMap.put(graphType.name(), graphId);
                    }
                }
            }
        }
        return result;
    }

    /**
     * @param pjId      e.g. 1101080001
     * @param graphType
     * @return 图实例id
     */
    public static String createGraph(String pjId, String graphType, String time) throws Exception {
        JSONObject createNode = new JSONObject();
        createNode.put(GraphInstanceTable.KEY_GRAPH_TYPE, graphType);
        createNode.put("periods", new JSONArray());
        JSONArray periods = (JSONArray) createNode.get("periods");
        JSONObject period = new JSONObject();
        period.put("begin_time", time);
        periods.add(period);
        String reStr = new Relation().createGraphInstance(Const.OBJ_TYPE_PROJECT + pjId, createNode.toString());
        JSONObject result = JSONObject.parseObject(reStr);
        if (!result.get(Const.JSON_FIELD_RESULT).toString().equals("success")) {
            throw new Exception("图实例创建失败");
        }
        return result.get("graph_id").toString();
    }


//    Peid	ObjId	objType	toPjId	作用
//    有	null	null	有	    删除原有项目中的所有资产关系
//    有	有	    有	    有	    在新项目下添加/修改关系，不对原有项目下关系进行操作（如果一个资产同时指定楼层和建筑，只添加楼层）	绑定同一岗位时，后者覆盖前者
//    有	null	有	    有	    忽略(尝试删除新项目关系)
//    有	有	    有 	    null	旧项目下添加/修改关系（如果一个资产同时指定楼层和建筑，只填加楼层）	绑定同一岗位时，后者覆盖前者
//    有	null	有	    null	删除旧项目中的指定objType的关系

    private static void classifyModifyArr(String originPjId, ArrayList<PropertyRel> input, JSONArray insertion, JSONArray deletion) throws Exception {
        //1.获取每个项目下的图实例id
        Set<String> pjIds = new HashSet<>();
        pjIds.add(originPjId);
        for (PropertyRel p : input) {
            if (p.toPjId != null)
                pjIds.add(p.toPjId);
        }
        Map<String, Map<String, String>> graphMap = getGraphs(pjIds, true);
        //1.end
        for (PropertyRel p : input) {
            if (p.toPjId != null) {//资产改变所属项目
                if (p.objId == null && p.objType == null) {//删除原项目下所有关系
                    JSONObject deleteNode = genRelNode(originPjId, p.propertyId, null, graphMap.get(originPjId).get(PropertyGraphEnum.Pe2Eq.name()), "1");
                    deletion.add(deleteNode);//删除资产和设备的关联关系
                    deleteNode = genRelNode(originPjId, null, p.propertyId, graphMap.get(originPjId).get(PropertyGraphEnum.Subset.name()), "6");
                    deletion.add(deleteNode);//删除资产和建筑的关系
                    deleteNode = genRelNode(originPjId, null, p.propertyId, graphMap.get(originPjId).get(PropertyGraphEnum.Subset.name()), "7");
                    deletion.add(deleteNode);//删除资产和楼层的关系
                } else if (p.objId != null && p.objType != null) {//增加新项目下的关系
                    if ((p.objId.startsWith(Const.OBJ_TYPE_EQUIPMENT) || p.objId.startsWith(Const.OBJ_TYPE_EQUIPMENT_COMPONENT))) {//关联设备或部件
                        JSONObject deleteNode = genRelNode(p.toPjId, null, p.objId, graphMap.get(p.toPjId).get(PropertyGraphEnum.Pe2Eq.name()), "1");
                        deletion.add(deleteNode);//删除新项目中，该设备部件所对应的资产
                        JSONObject insertNode = genRelNode(p.toPjId, p.propertyId, p.objId, graphMap.get(p.toPjId).get(PropertyGraphEnum.Pe2Eq.name()), "1");
                        insertion.add(insertNode);//添加新项目中，该资产对应的设备部件
                    } else if (p.objId.startsWith(Const.OBJ_TYPE_BUILDING)) {//关联建筑时
                        JSONObject insertNode = genRelNode(p.toPjId, p.objId, p.propertyId, graphMap.get(p.toPjId).get(PropertyGraphEnum.Subset.name()), "6");
                        insertion.add(insertNode);//增加资产和建筑关系
                    } else if (p.objId.startsWith(Const.OBJ_TYPE_FLOOR)) {//关联楼层时
                        JSONObject insertNode = genRelNode(p.toPjId, p.objId, p.propertyId, graphMap.get(p.toPjId).get(PropertyGraphEnum.Subset.name()), "7");
                        insertion.add(insertNode);//添加新楼层
                    }
                }
            } else {//资产不改变所属项目
                if (p.objId != null && p.objType != null) {//增加、修改资产和某个指定对象的关系
                    if (p.objId.startsWith(Const.OBJ_TYPE_BUILDING)) {
                        //建筑：删除旧建筑、楼层关系，增加新建筑关系
                        JSONObject deleteNode = genRelNode(originPjId, null, p.propertyId, graphMap.get(originPjId).get(PropertyGraphEnum.Subset.name()), "6");
                        deletion.add(deleteNode);//删除旧建筑
                        deleteNode = genRelNode(originPjId, null, p.propertyId, graphMap.get(originPjId).get(PropertyGraphEnum.Subset.name()), "7");
                        deletion.add(deleteNode);//删除旧楼层
                        JSONObject insertNode = genRelNode(originPjId, p.objId, p.propertyId, graphMap.get(originPjId).get(PropertyGraphEnum.Subset.name()), "6");
                        insertion.add(insertNode);//添加新建筑
                    } else if (p.objId.startsWith(Const.OBJ_TYPE_FLOOR)) {
                        //楼层：删除旧建筑、楼层关系，增加新建筑、楼层关系
                        JSONObject deleteNode = genRelNode(originPjId, null, p.propertyId, graphMap.get(originPjId).get(PropertyGraphEnum.Subset.name()), "6");
                        deletion.add(deleteNode);//删除旧建筑
                        deleteNode = genRelNode(originPjId, null, p.propertyId, graphMap.get(originPjId).get(PropertyGraphEnum.Subset.name()), "7");
                        deletion.add(deleteNode);//删除旧楼层
                        JSONObject insertNode = genRelNode(originPjId, p.objId, p.propertyId, graphMap.get(originPjId).get(PropertyGraphEnum.Subset.name()), "7");
                        insertion.add(insertNode);//添加新楼层
                    } else if (p.objId.startsWith(Const.OBJ_TYPE_EQUIPMENT) || p.objId.startsWith(Const.OBJ_TYPE_EQUIPMENT_COMPONENT)) {
                        //设备、部件：删除旧设备部件的关系，增加新设备部件的关系->删除此资产和设备部件的关系，删除此设备部件和其他资产的关系
                        JSONObject deleteNode = genRelNode(originPjId, p.propertyId, null, graphMap.get(originPjId).get(PropertyGraphEnum.Pe2Eq.name()), "1");
                        deletion.add(deleteNode);//删除该资产对应的旧设备部件
                        deleteNode = genRelNode(originPjId, null, p.objId, graphMap.get(originPjId).get(PropertyGraphEnum.Pe2Eq.name()), "1");
                        deletion.add(deleteNode);//删除该设备、部件对应的资产
                        JSONObject insertNode = genRelNode(originPjId, p.propertyId, p.objId, graphMap.get(originPjId).get(PropertyGraphEnum.Pe2Eq.name()), "1");
                        insertion.add(insertNode);//添加新设备、部件
                    }
                } else if (p.objId == null && p.objType != null) {//删除某个图类型下的关系
                    if (p.objType.equals(Const.OBJ_TYPE_BUILDING)) {
                        JSONObject deleteNode = genRelNode(originPjId, null, p.propertyId, graphMap.get(originPjId).get(PropertyGraphEnum.Subset.name()), "6");
                        deletion.add(deleteNode);
                    } else if (p.objType.equals(Const.OBJ_TYPE_FLOOR)) {
                        JSONObject deleteNode = genRelNode(originPjId, null, p.propertyId, graphMap.get(originPjId).get(PropertyGraphEnum.Subset.name()), "7");
                        deletion.add(deleteNode);
                    } else if (p.objType.equals(Const.OBJ_TYPE_EQUIPMENT) || p.objType.equals(Const.OBJ_TYPE_EQUIPMENT_COMPONENT)) {
                        JSONObject deleteNode = genRelNode(originPjId, p.propertyId, null, graphMap.get(originPjId).get(PropertyGraphEnum.Pe2Eq.name()), "1");
                        deletion.add(deleteNode);
                    }
                }
            }
        }
    }

    /**
     * @param pjId    e.g. 1101080001
     * @param fromId  Eq...等物理对象id
     * @param toId    Sp...等物理对象id
     * @param graphId Gt...001
     * @param relType 1
     * @return
     * @throws Exception
     */
    private static JSONObject genRelNode(String pjId, String fromId, String toId, String graphId, String relType) throws Exception {
        JSONObject json = new JSONObject();
        json.put(Const.JSON_FIELD_FROM_ID, fromId);
        RelationUtil.rewriteCriteria(json, Const.JSON_FIELD_FROM_ID);
        json.put(Const.JSON_FIELD_TO_ID, toId);
        RelationUtil.rewriteCriteria(json, Const.JSON_FIELD_TO_ID);
        json.put(RelTable.KEY_PJ_ID, pjId);
        json.put(RelTable.KEY_GRAPH_ID, graphId);
        json.put(RelTable.KEY_REL_TYPE, relType);
        List<String> keySetCopy = new ArrayList<>();
        keySetCopy.addAll(json.keySet());
        for (String key : keySetCopy) {
            if (json.get(key) == null)
                json.remove(key);
        }
        return json;
    }

    /**
     * 过滤输入，当两个资产指向同一个设备时，后出现的覆盖之前的。对toPjId进行校验，去掉Pj开头
     * 如果一个资产同时指定了楼层和建筑，将建筑id过滤掉
     *
     * @param input
     */

//    Peid	ObjId	objType	toPjId	作用
//    有	null	null	有	    删除原有项目中的所有资产关系
//    有	有	    有	    有	    在新项目下添加/修改关系，不对原有项目下关系进行操作（如果一个资产同时指定楼层和建筑，只填加楼层）	绑定同一岗位时，后者覆盖前者
//    有	null	有	    有	    忽略(尝试删除新项目关系)
//    有	有	    有 	    null	旧项目下添加/修改关系（如果一个资产同时指定楼层和建筑，只填加楼层）	绑定同一岗位时，后者覆盖前者
//    有	null	有	    null	删除旧项目中的指定graphType的关系
    private static void filterConflict(ArrayList<PropertyRel> input) {
        Set<String> visitedEqSet = new HashSet<>();
        Set<String> hasFloorSet = new HashSet<>();//set里存储指定了设备部件id的资产id
        for (int i = input.size() - 1; i >= 0; --i) {
            PropertyRel p = input.get(i);
            if (p.toPjId != null && p.toPjId.startsWith(Const.OBJ_TYPE_PROJECT)) {
                p.toPjId = p.toPjId.substring(2);
            }
            //当p.objId是设备或部件时
            if (p.objId != null && (p.objId.startsWith(Const.OBJ_TYPE_EQUIPMENT) || p.objId.startsWith(Const.OBJ_TYPE_EQUIPMENT_COMPONENT))) {
                if (visitedEqSet.contains(p.objId)) {
                    input.remove(i);
                    continue;
                } else {
                    visitedEqSet.add(p.objId);
                }
            }
        }
        //1.资产同时绑定建筑和楼层时，只进行绑定楼层操作
        for (PropertyRel p : input) {
            if (p.objId != null && p.objId.startsWith(Const.OBJ_TYPE_FLOOR)) {
                hasFloorSet.add(p.propertyId);
            }
        }
        for (int i = input.size() - 1; i >= 0; --i) {
            PropertyRel p = input.get(i);
            if (p.objId != null && p.objId.startsWith(Const.OBJ_TYPE_BUILDING) && hasFloorSet.contains(p.propertyId)) {
                input.remove(i);
            }
        }
        //1.end
    }

    /**
     * @param content 从zillion查询返回的结果
     * @return
     */
    private static HashMap<String, String> getFl2BdMap(JSONArray content) {
        HashMap<String, String> result = new HashMap<>();
        for (int i = 0; i < content.size(); ++i) {
            JSONObject obj = (JSONObject) content.get(i);
            String fromId = CommonUtil.combineId(obj.get(RelTable.KEY_PJ_ID).toString(), obj.get(RelTable.KEY_FROM_OBJ_TYPE).toString(), obj.get(RelTable.KEY_FROM_OBJ_ID).toString());
            String toId = CommonUtil.combineId(obj.get(RelTable.KEY_PJ_ID).toString(), obj.get(RelTable.KEY_TO_OBJ_TYPE).toString(), obj.get(RelTable.KEY_TO_OBJ_ID).toString());
            if (fromId.startsWith(Const.OBJ_TYPE_BUILDING) && toId.startsWith(Const.OBJ_TYPE_FLOOR)) {
                result.put(toId, fromId);
            }
        }
        return result;
    }

    private static HashMap<String, Set<String>> getBd2FlMap(JSONArray content) {
        HashMap<String, Set<String>> result = new HashMap<>();
        for (int i = 0; i < content.size(); ++i) {
            JSONObject obj = (JSONObject) content.get(i);
            String fromId = CommonUtil.combineId(obj.get(RelTable.KEY_PJ_ID).toString(), obj.get(RelTable.KEY_FROM_OBJ_TYPE).toString(), obj.get(RelTable.KEY_FROM_OBJ_ID).toString());
            String toId = CommonUtil.combineId(obj.get(RelTable.KEY_PJ_ID).toString(), obj.get(RelTable.KEY_TO_OBJ_TYPE).toString(), obj.get(RelTable.KEY_TO_OBJ_ID).toString());
            if (fromId.startsWith(Const.OBJ_TYPE_BUILDING) && toId.startsWith(Const.OBJ_TYPE_FLOOR)) {
                if (!result.containsKey(fromId)) {
                    result.put(fromId, new HashSet<>());
                }
                Set<String> floors = result.get(fromId);
                floors.add(toId);
            }
        }
        return result;
    }

    /**
     * 发送资产改变关系的消息
     * @param insertObjs
     * @param deleteObjs
     * @param input
     */
    private static void sendMsgForProperty(JSONArray insertObjs, JSONArray deleteObjs, ArrayList<PropertyRel> input){
        Map<String,Set<String>> propertyIdMap = new HashMap<>();//projectId->Set<propertyId>
        Set<String> projectIdSet = new HashSet<>();
        JSONArray tempArr = new JSONArray();
        tempArr.addAll(insertObjs);
        tempArr.addAll(deleteObjs);
        Map<String, String> peIdToLinkId = new HashMap<>();
        for(int i = 0; i < input.size(); ++i){
            PropertyRel pr = input.get(i);
            if(pr.objType != null && pr.objType.equals(PropertyGraphEnum.Pe2Eq.name())){
                if(!peIdToLinkId.containsKey(pr.propertyId)){
                    peIdToLinkId.put(pr.propertyId, pr.objId);
                }
            }
        }
        for(int i = 0; i< tempArr.size(); ++i){
            JSONObject node = (JSONObject) tempArr.get(i);
            String graphId = node.get(RelTable.KEY_GRAPH_ID).toString();
            String projectId = Const.OBJ_TYPE_PROJECT + node.get(RelTable.KEY_PJ_ID).toString();
            //如果是资产和设备的关联关系，记录项目id和资产id
            if(graphId.substring(2).startsWith(PropertyGraphEnum.Pe2Eq.name())){
                if(node.get(RelTable.KEY_FROM_OBJ_TYPE)==null)
                    continue;
                String propertyId = CommonUtil.combineId(node.get(RelTable.KEY_PJ_ID).toString(), node.get(RelTable.KEY_FROM_OBJ_TYPE).toString(), node.get(RelTable.KEY_FROM_OBJ_ID).toString());
                if(!propertyIdMap.containsKey(projectId)){
                    propertyIdMap.put(projectId,new HashSet<>());
                }
                Set<String> idSet = propertyIdMap.get(projectId);
                idSet.add(propertyId);
            }else{//只记录项目id
                projectIdSet.add(projectId);
            }
        }

        //开始发送消息
        ActivityLogRunnable activityLog = new ActivityLogRunnable();
        //资产改变关联岗位时，消息包含资产id
        for(String projectId : propertyIdMap.keySet()){
            Set<String> idSet = propertyIdMap.get(projectId);
            for(String propertyId : idSet){
                activityLog.setRelationChangeMsg_8(CommonMethods.getProject(projectId,true), propertyId, peIdToLinkId.get(projectId), "property-equipment link changed");
            }
        }
        for(String projectId : projectIdSet){
            activityLog.setRelationChangeMsg_8(CommonMethods.getProject(projectId,true), null, null, "");
        }
        ActivityLogUtil.pool.submit(activityLog);
    }
}
