package com.sagacloud.utils;
/*
 * Author: Jxing
 * Create Time: 2018/3/13
 */

import com.alibaba.fastjson.serializer.SerializerFeature;
import com.google.common.base.Strings;
import com.sagacloud.broadcast.MessageHandler;
import com.sagacloud.dict.EqStaticInfo;
//import com.sagacloud.json.JSONArray;
//import com.sagacloud.json.JSONObject;
import com.alibaba.fastjson.*;
import com.sagacloud.dict.InfosUtil;
import com.sagacloud.utils.objectLog.ActivityLogObject;
import com.sagacloud.utils.objectLog.ActivityLogRunnable;
import com.sagacloud.utils.objectLog.ActivityLogUtil;
import com.sagacloud.utils.objects.*;
import com.sagacloud.utils.jsonHelper.infoPoint.InfoPoint;
import com.sagacloud.utils.jsonHelper.property.PropertyRel;
import com.sagacloud.utils.qrCode.QRCodeObj;
import com.sagacloud.utils.qrCode.QRCodeRunnable;
import com.sagacloud.utils.relationUtils.PropertyRelUtil;
import com.sagacloud.utils.tables.HBaseTable;
import com.sagacloud.utils.tables.ObjInfosTable;
import com.sagacloud.utils.tables.ObjectTable;
import com.sagacloud.utils.tables.ProjectTable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

public class PropertyUtil {
    private static final Logger log = LoggerFactory.getLogger(PropertyUtil.class);

    // 添加资产
    public static String addObj(String pjId, JSONArray objects, JSONArray errors, JSONArray success, boolean skipErrorInfo, boolean isMonitor, String qrInnerImageUrl, Boolean genPersagyQr) {
//        JSONObject json = (JSONObject) JSONValue.parse("{\"QueryType\":\"insert\",\"Datatable\":\"objects\"}");
//        json.put("Database", OssUtils.getProperty("database.name"));
        if (objects.size() < 1)
            return null;
        // 验证种族正确性
        HashSet<String> families = filterFamilyCode(pjId, objects, errors);
        if (families.size() == 0 || objects.size() == 0)
            return null;
        HashMap<String, HashMap<String, InfoPoint>> infos = null;
        try {
            // 获取种族对应的信息点信息
            infos = InfoCodeUtil.innerGetFamilyInfos(pjId, families);
        } catch (Exception e) {
            return "OSS连接失败";
        }
        // 验证信息点和输入的信息点格式是否正确
        verifyFamilyInfos(objects, errors, infos, skipErrorInfo);
        verifyFamilyLinkEquipType(pjId, objects, errors);
        //为objects里面的对象生成id, 有id的也直接插入, id如果有重复的则获取失败返回, 移到errors里.
        if (objects.size() == 0)
            return null;
        HashMap<String, JSONObject> uuid2obj = new HashMap<>();
        for (int i = objects.size() - 1; i > -1; --i) {
            JSONObject single = (JSONObject) objects.get(i);
            if (single.get("id") != null) {
                if(!single.get("id").toString().startsWith("Pe") || !DpUtils.verifyAssignedObjID(single.get("id").toString())){
                    moveToError(objects, i, errors, "指定id不符合定义");
                } else {
                    if (!uuid2obj.containsKey(single.get("id").toString())) {
                        uuid2obj.put(single.get("id").toString(), single);
                    } else {
                        moveToError(objects, i, errors, "id重复");
                    }
                }
            } else {
                single.put("id", DpUtils.generateUUID(Const.OBJ_TYPE_PROPERTY, true));
                uuid2obj.put(single.get("id").toString(), single);
            }
        }
        JSONArray insertObjects = new JSONArray();
        String time = CommonUtil.getTime();
        Set<String> insertedId_Log = new HashSet<>();
        for (int i = 0; i < objects.size(); ++i) {
            JSONObject single = (JSONObject) objects.get(i);
            insertObjects.add(generateObject(pjId, Const.OBJ_TYPE_PROPERTY, single.get("family").toString(), single.get("id").toString(), time));
            if(isMonitor){
                if(!insertedId_Log.contains(single.get("id").toString()))
                    insertedId_Log.add(single.get("id").toString());
            }
        }
        JSONObject retObj = null;
        try {
            retObj = CommonUtil.insert(true, HBaseTable.objects, insertObjects);
        } catch (Exception e) {
            return String.join("", "服务器异常:", e.getMessage());
        }
        if (retObj.get("Result").equals("failure")) // 将数据库中已存在的id剔除.
        {
            JSONArray duplicate = (JSONArray) retObj.get("Content");
            HashSet<String> duplicateIds = new HashSet<>();
            for (int i = 0; i < duplicate.size(); ++i) {
                String id = String.join("", Const.OBJ_TYPE_PROPERTY, ((JSONObject) duplicate.get(i)).get(ObjectTable.KEY_OBJ_ID).toString());
                if (!duplicateIds.contains(id)) {
                    duplicateIds.add(id);
                }
            }
            for (int i = objects.size() - 1; i > -1; --i) {
                JSONObject single = (JSONObject) objects.get(i);
                if (duplicateIds.contains(single.get("id").toString())) {
                    // 将id重复的移除到errors里面.
                    moveToError(objects, i, errors, "id已存在");
                }
            }
            if(isMonitor){
                insertedId_Log.removeAll(duplicateIds);
            }
        }
        if(isMonitor){
            ArrayList<ActivityLogObject> logList = new ArrayList<>();
            for(String id : insertedId_Log){
                ActivityLogObject logObj = new ActivityLogObject();
                logObj.setId(id);
                logObj.setTime(time);
                logObj.setOt(ObjectType.property);
                logObj.setType(MQType.objectCreate);
                logObj.setProjectId(pjId);
                logList.add(logObj);
            }
            ActivityLogRunnable activityLog = new ActivityLogRunnable(logList);
            ActivityLogUtil.pool.submit(activityLog);
        }
        // 为插入成功的资产插入关系
        if (objects.size() == 0)
            return null;
        HashSet<String> peIdWithRelation = new HashSet<>(); // 保存带有关联岗位的peid
        ArrayList<PropertyRel> tmp = generateRelationArray(pjId, objects);
        PropertyRelUtil.modify(pjId, tmp);
        for(PropertyRel rel : tmp)
        {
            if(rel.objType != null && rel.objType.equals(Const.OBJ_TYPE_EQUIPMENT) && !peIdWithRelation.contains(rel.propertyId))
            {
                peIdWithRelation.add(rel.propertyId);
            }
        }
        // 为插入成功的资产插入信息点.
        Map<String, String> idFileKeyMap = new HashMap<>();
        try {
            putIDToInfos(objects, success);
            JSONCheckUtil.addQRCodeInfo(pjId, objects, "EquipQRCode", idFileKeyMap);
            for (int i = 0; i < objects.size(); ++i) {
                DpUtils.rewriteCriteria((JSONObject) objects.get(i), "id", false);
            }
            ObjectUtil.batchUpdate(pjId, objects, false, true, false, new CommonQueryCriteria());
        } catch (Exception e) {
            log.error(String.join(", ", e.getMessage(), objects.toString(), "信息点添加失败!"));
        }
        try{
            //生成二维码文件, 上传到文件服务器
            QRCodeRunnable qrCodeRunnable = new QRCodeRunnable(idFileKeyMap, qrInnerImageUrl, "Pj" + pjId, genPersagyQr);
            ActivityLogUtil.pool.submit(qrCodeRunnable);
        }catch (Exception ex){

        }
        try {
            // 补充信息点(同步岗位的信息点)
            complementInfos(pjId, peIdWithRelation);
        }catch (Exception ex)
        {}
        return null;
    }
    // 校验资产的种族和要关联的岗位的设备类型是否是从属关系
    private static void verifyFamilyLinkEquipType(String pjId, JSONArray objects, JSONArray errors) {
        Set<String> ids = new HashSet<>();
        for (int i = objects.size() - 1; i > -1; --i) {
            JSONObject single = (JSONObject) objects.get(i);
            JSONObject relation = (JSONObject) single.get("relation");
            if (relation == null)
                continue;
            String id = Strings.nullToEmpty((String) relation.get("link"));
            if(id.length() == 0)
                continue ;
            if(!DpUtils.checkId(id)) {
                moveToError(objects, i, errors, String.join("", "关联id:", id, "出错"));
                continue ;
            }
            ids.add(id);
        }
        EqStaticInfo eqStaticInfo = new EqStaticInfo(pjId);
        Map<String, String> idType = CommonUtil.getEquipCategory(pjId, ids, false);
        a:
        for (int i = objects.size() - 1; i > -1; --i) {
            JSONObject single = (JSONObject) objects.get(i);
            JSONObject relation = (JSONObject) single.get("relation");
            if (relation == null)
                continue;
            for (String key : relation.keySet()) {
                String str = relation.get(key).toString();
                if (key.equals("link")) {
                    try {
                        String type = idType.get(str);
                        if (eqStaticInfo.getEqToFamilyMap().containsKey(type) && eqStaticInfo.getEqToFamilyMap().get(type).equals(single.get("family").toString())) // 设备类型和种族匹配
                        {
                            continue;
                        }
                        moveToError(objects, i, errors, "资产种族和关联设备类型不匹配");
                        continue a;
                    } catch (Exception e) {
                        moveToError(objects, i, errors, "资产关联设备id错误");
                        continue a;
                    }
                }
            }
        }
    }

    public static void putIDToInfos(JSONArray objects, JSONArray success) {
        String currentTime = CommonUtil.getTime();
        for (int i = 0; i < objects.size(); ++i) {
            JSONObject single = (JSONObject) objects.get(i);
            JSONObject infos = (JSONObject) single.get("infos");
            String id = single.getString("id");
            String idInfoCode = InfosUtil.getInfoLocalId(id);
            String nameInfoCode = InfosUtil.getInfoLocalName(id);
            if (infos.get(idInfoCode) == null) {
                JSONArray equipId = new JSONArray();
                JSONObject value = new JSONObject();
                equipId.add(value);
                value.put("value", id);
                infos.put(idInfoCode, equipId);
            }
            JSONObject successObj = new JSONObject();
            successObj.put("id", id);
            success.add(successObj);
            JSONArray name = (JSONArray) infos.get(nameInfoCode);
            if (name != null) {
                JSONObject tmpValue = null;
                for (int j = 0; j < name.size(); ++j) {
                    JSONObject value = (JSONObject) name.get(j);
                    if (value.get("time") == null) {
                        successObj.put(nameInfoCode, value.get("value"));
                        break;
                    }
                    if (tmpValue == null)
                        tmpValue = value;
                    else {
                        String tmpTime = tmpValue.get("time").toString();
                        String valueTime = value.get("time").toString();

                        if (tmpTime.compareTo(valueTime) < 0 && valueTime.compareTo(currentTime) < 0) // 如果 tmp < value < 当前时间
                        {
                            tmpValue = value;
                        }
                    }
                }
                if (successObj.get(nameInfoCode) == null && tmpValue != null) {
                    successObj.put(nameInfoCode, tmpValue.get("value"));
                }
            }
        }
    }

    // 首先验证关联的id的存在性, 然后筛选出存在的id返回关联信息ArrayList
    public static ArrayList<PropertyRel> generateRelationArray(String pjId, JSONArray objects) {
        ArrayList<PropertyRel> relationList = new ArrayList<>();
        HashSet<String> relIds = new HashSet<>();
        for (int i = 0; i < objects.size(); ++i) {
            JSONObject single = (JSONObject) objects.get(i);
            JSONObject relation = (JSONObject) single.get("relation");
            if (relation == null)
                continue;
            for (String key : relation.keySet()) {
                if (key.equals("link") || key.equals("building") || key.equals("floor")) {
                    if (relation.get(key) != null && !relIds.contains(relation.get(key).toString())) {
                        relIds.add(relation.get(key).toString());
                    }
                }
            }
        }

        HashMap<String, PhysicalWorldObject> validIds = filterValidObjectIds(pjId, relIds);
        for (int i = 0; i < objects.size(); ++i) {
            JSONObject single = (JSONObject) objects.get(i);
            JSONObject relation = (JSONObject) single.get("relation");
            if (relation == null)
                continue;
            String toProject = null;
            if (relation.get("project") != null) {
                toProject = relation.get("project").toString();
                if(toProject.startsWith("Pj") && !toProject.equals(CommonMethods.getProject(pjId, true)))
                    relationList.add(generatePropertyRelation(single.get("id").toString(), null, null, toProject));
            }
            for (String key : relation.keySet()) {
                if (!(relation.get(key) == null && relation.containsKey(key)) && !validIds.containsKey(relation.get(key).toString()) )
                    continue;
                if (key.equals("link")) {
                    if(relation.get(key) == null || relation.get(key).equals("")){
                        relationList.add(generatePropertyRelation(single.get("id").toString(), null, Const.OBJ_TYPE_EQUIPMENT, toProject));
                    }else if((relation.get(key).toString().startsWith("Eq") || relation.get(key).toString().startsWith("Ec")))
                        relationList.add(generatePropertyRelation(single.get("id").toString(), relation.get(key).toString(), Const.OBJ_TYPE_EQUIPMENT, toProject));
                } else if (key.equals("building")) {
                    if(relation.get(key) == null)
                        relationList.add(generatePropertyRelation(single.get("id").toString(), null, Const.OBJ_TYPE_BUILDING, toProject));
                    else if(relation.get(key).toString().startsWith("Bd"))
                        relationList.add(generatePropertyRelation(single.get("id").toString(), relation.get(key).toString(), Const.OBJ_TYPE_BUILDING, toProject));
                } else if (key.equals("floor"))
                    if(relation.get(key) == null)
                        relationList.add(generatePropertyRelation(single.get("id").toString(), null, Const.OBJ_TYPE_FLOOR, toProject));
                    else if(relation.get(key).toString().startsWith("Fl"))
                        relationList.add(generatePropertyRelation(single.get("id").toString(), relation.get(key).toString(), Const.OBJ_TYPE_FLOOR, toProject));
            }
        }
        // 查询数据库来验证

        return relationList;
    }

    public static PropertyRel generatePropertyRelation(String peId, String objId, String objType, String toProject) {
        PropertyRel rel = new PropertyRel();
        rel.propertyId = peId;
        rel.objId = objId;
        rel.objType = objType;
        if (toProject != null)
            rel.toPjId = toProject;
        return rel;
    }

    private static HashMap<String, PhysicalWorldObject> filterValidObjectIds(String pjId, HashSet<String> relIds) {
        HashMap<String, PhysicalWorldObject> validIds = new HashMap<>();
        JSONArray arr = new JSONArray();
        for (String id : relIds) {
            JSONObject obj = new JSONObject();
            obj.put("id", id);
            try {
                CommonUtil.rewriteObjectTableCriteria(pjId, obj, "id", true); // 需要填pjId
                arr.add(obj);
            } catch (Exception e) {
            }
        }
        try {
            JSONObject validResult = CommonUtil.combine_query(HBaseTable.objects, arr);
            if (!validResult.get("Result").equals("success"))
                return null;
            JSONArray resultContent = (JSONArray) validResult.get("Content");
            for (int i = 0; i < resultContent.size(); ++i) {
                JSONObject singleObj = (JSONObject) resultContent.get(i);
                if (singleObj.get(Const.TABLE_KEY_NAME_VALID) != null && singleObj.get(Const.TABLE_KEY_NAME_VALID).equals(true)) {
                    String id = CommonUtil.combineId(singleObj.get(ObjectTable.KEY_PJ_ID).toString(), singleObj.get(ObjectTable.KEY_OBJ_TYPE).toString(), singleObj.get(ObjectTable.KEY_OBJ_ID).toString());
                    if (!validIds.containsKey(id)) {
                        PhysicalWorldObject pwo = new PhysicalWorldObject();
                        pwo.setProject_id(singleObj.get(ObjectTable.KEY_PJ_ID).toString());
                        pwo.setObj_type(singleObj.get(ObjectTable.KEY_OBJ_TYPE).toString());
                        pwo.setObj_id(singleObj.get(ObjectTable.KEY_OBJ_ID).toString());
                        pwo.setSub_type(Strings.nullToEmpty((String)singleObj.get(ObjectTable.COL_SUBTYPE)));
                        pwo.setCtime(Strings.nullToEmpty((String)singleObj.get(ObjectTable.KEY_CREATE_TIME)));
                        validIds.put(id, pwo);
                    }
                }
            }
        } catch (Exception e) {
            return validIds;
        }
        return validIds;
    }

    private static JSONObject generateObject(String pjId, String type, String family, String id, String time) {
        JSONObject object = new JSONObject();
        object.put(ObjectTable.KEY_PJ_ID, pjId);
        object.put(ObjectTable.KEY_OBJ_TYPE, type);
        object.put(ObjectTable.COL_SUBTYPE, family);
        object.put(ObjectTable.KEY_OBJ_ID, id.substring(2));
        object.put(ObjectTable.KEY_CREATE_TIME, time);
        object.put(ObjectTable.COL_VALID, true);
        return object;
    }


    private static void verifyFamilyInfos(JSONArray objects, JSONArray errors, HashMap<String, HashMap<String, InfoPoint>> infos, boolean skipErrorInfo) {
        for (int i = objects.size() - 1; i > -1; --i) {
            JSONObject single = (JSONObject) objects.get(i);
            //String info = verifyFamilyInfos((JSONObject) single.get("infos"), infos.get(single.get("family").toString()), infos.get(Const.EQ_GENERAL));
            ArrayList<String> info = getErrorFamilyInfos((JSONObject) single.get("infos"), infos.get(single.get("family").toString()), infos.get(Const.EQ_GENERAL));
            if (info.size() > 0) {
                if(skipErrorInfo){
                    for(String infoCode : info){
                        single.getJSONObject("infos").remove(infoCode);
                    }
                }else
                    moveToError(objects, i, errors, String.join("", "信息点", info.get(0), "不属于种族", single.get("family").toString(), "或该信息点的填写格式错误"));
            }
        }
    }
    private static ArrayList<String> getErrorFamilyInfos(JSONObject infos, HashMap<String, InfoPoint> infosMap, HashMap<String, InfoPoint> general) {
        ArrayList<String> errorInfos = new ArrayList<>();
        for (String info : infos.keySet()) {
            if (!infosMap.containsKey(info) && !(general.containsKey(info)) && !EqStaticInfo.propertyGeneralInfos.contains(info)) {
                errorInfos.add(info);
            } else {
                try {
                    JSONArray obj = (JSONArray) infos.get(info);
                    for (int i = 0; i < obj.size(); ++i) {
                        JSONObject item = (JSONObject) obj.get(i);
                        if (item.keySet().contains("time")) {
                            String time = item.get("time").toString();
                            if (!DpUtils.isValidDate(time)) {
                                errorInfos.add(info);
                            }
                        }
                        if (!item.keySet().contains("value")) {
                            errorInfos.add(info);
                        }
                    }
                } catch (Exception ex) {
                    errorInfos.add(info);
                }
            }
        }
        return errorInfos;
    }
    private static String verifyFamilyInfos(JSONObject infos, HashMap<String, InfoPoint> infosMap, HashMap<String, InfoPoint> general) {
        for (String info : infos.keySet()) {
            if (!infosMap.containsKey(info) && !(general.containsKey(info)) && !EqStaticInfo.propertyGeneralInfos.contains(info)) {
                return info;
            } else {
                try {
                    JSONArray obj = (JSONArray) infos.get(info);
                    for (int i = 0; i < obj.size(); ++i) {
                        JSONObject item = (JSONObject) obj.get(i);
                        if (item.keySet().size() > 2)
                            return info;
                        if (item.keySet().contains("time")) {
                            String time = item.get("time").toString();
                            if (!DpUtils.isValidDate(time)) {
                                return info;
                            }
                        }
                        if (!item.keySet().contains("value")) {
                            return info;
                        }
                    }
                } catch (Exception ex) {
                    return info;
                }
            }
        }
        return null;
    }

    //检查种族是否正确和存在, 不存在则从objects里面移到errors里面. 同时保存一个种族set
    private static HashSet<String> filterFamilyCode(String pjId, JSONArray objects, JSONArray errors) {
        HashSet<String> families = new HashSet<String>();
        EqStaticInfo eqStaticInfo = new EqStaticInfo(pjId);
        for (int i = objects.size() - 1; i > -1; --i) {
            JSONObject single = (JSONObject) objects.get(i);
            if (eqStaticInfo.getFamilySet().contains(single.get("family").toString())) {
                if (!families.contains(single.get("family").toString()))
                    families.add(single.get("family").toString());
            } else {
                moveToError(objects, i, errors, String.join("", "错误的种族编码:", single.get("family").toString()));
            }
        }
        return families;
    }

    public static void moveToError(JSONArray objects, int i, JSONArray errors, String errorMsg) {
        if (errors == null)
            return;
        JSONObject tmp = (JSONObject) objects.get(i);
        objects.remove(i);
        errors.add(tmp);
        tmp.put("error", errorMsg);
    }
    
    public static void moveToError(JSONArray objects, int i, JSONArray errors, String errorMsg, String id) {
        if (errors == null)
            return;
        JSONObject tmp = (JSONObject) objects.get(i);
        objects.remove(i);
        errors.add(tmp);
        tmp.put("error", errorMsg);
        tmp.put("repeat_id", id);
    }
    
    public static String returnOnCreation(JSONArray successContent, JSONArray errorContent, String returnMSG) {
        JSONObject ret = new JSONObject();
        if (returnMSG != null && returnMSG.length() != 0) {
            ret.put("Result", "failure");
            ret.put("ResultMsg", returnMSG);
        } else {
            ret.put("Result", successContent.size() > 0 ? "success" : "failure");
            ret.put("ResultMsg", "");
            if (errorContent.size() > 0)
                ret.put("Error", errorContent);
            if (successContent.size() > 0)
                ret.put("Content", successContent);
        }
        return JSON.toJSONString(ret, SerializerFeature.WriteMapNullValue, SerializerFeature.DisableCircularReferenceDetect);
    }

    public static String returnJSON(boolean success, JSONArray successContent, JSONArray errorContent, String returnMSG) {
        JSONObject ret = new JSONObject();
        ret.put("Result", success ? "success" : "failure");
        if (successContent != null && successContent.size() > 0) {
            ret.put("Content", successContent);
        }
        if (errorContent != null && errorContent.size() > 0) {
            ret.put("Error", errorContent);
        }
        ret.put("ResultMsg", returnMSG);
        return JSON.toJSONString(ret, SerializerFeature.WriteMapNullValue, SerializerFeature.DisableCircularReferenceDetect);
    }

    public static void deletePropertyObjects(String pjId, JSONArray objects, JSONArray errors) throws Exception {
        ArrayList<String> disableObj = new ArrayList<>(); // 保存需要作废的id
        ArrayList<String> deleteObj = new ArrayList<>(); // 保存需要删除的id
        for (int i = objects.size() - 1; i > -1; --i) {
            JSONObject single = (JSONObject) objects.get(i);
            String id = single.getString("id");
            if (id == null || !DpUtils.checkId(id)) { // 如果id == null 或者不是合法id 就忽略
                PropertyUtil.moveToError(objects, i, errors, "id不合法");
                continue;
            }
            if (single.getBooleanValue("flag")) // 保存将要删除的id
            {
                deleteObj.add(single.get("id").toString());
            } else // 作废的id
            {
                disableObj.add(single.get("id").toString());
            }
        }
        //作废对象
        if(disableObj.size() > 0)
            disablePropertyObjects(pjId, disableObj, true);
        //删除对象
        if(deleteObj.size() > 0)
            deletePropertyObjects(pjId, deleteObj, true);
        if(disableObj.size() + deleteObj.size() > 0)
            MessageHandler.publish(String.format("{\"type\":8,\"projectId\":\"%s\",\"name\":\"relationChange\",\"description\":\"\"}", CommonMethods.getProject(pjId,true)));
    }
    public static void deleteObjects(String pjId, JSONArray objects, JSONArray errors, ArrayList<String> disableObj, ArrayList<String> deleteObj) throws Exception {
//        ArrayList<String> disableObj = new ArrayList<>(); // 保存需要作废的id
//        ArrayList<String> deleteObj = new ArrayList<>(); // 保存需要删除的id
        for (int i = objects.size() - 1; i > -1; --i) {
            JSONObject single = (JSONObject) objects.get(i);
            String id = single.getString("id");
            if (id == null || !DpUtils.checkId(id)) { // 如果id == null 或者不是合法id 就忽略
                PropertyUtil.moveToError(objects, i, errors, "id不合法");
                continue;
            }
            if (single.getBooleanValue("flag")) // 保存将要删除的id
            {
                deleteObj.add(single.get("id").toString());
            } else // 作废的id
            {
                disableObj.add(single.get("id").toString());
            }
        }
        //作废对象
        if(disableObj.size() > 0)
            disableObjects(pjId, disableObj, true);
        //删除对象以及信息点, 并未删除关系
        if(deleteObj.size() > 0)
            deleteObjects(pjId, deleteObj, true);
    }

    // 删除对象
    private static void deletePropertyObjects(String pjId, ArrayList<String> deleteObj, boolean isMonitor) throws Exception {
        if(deleteObj.size() == 0)
            return;
        // 删除Objects表中的数据
        deleteObjAndInfos(pjId, deleteObj);
        // 删除跟该对象相关的关系
        PropertyRelUtil.deleteAllRel(pjId, deleteObj);
        if(isMonitor){
            String time = CommonUtil.getTime();
            ArrayList<ActivityLogObject> logList = new ArrayList<>();
            for(String id : deleteObj){
                ActivityLogObject logObj = new ActivityLogObject();
                logObj.setId(id);
                logObj.setTime(time);
                logObj.setOt(ObjectUtil.getObjectTypeById(id));
                logObj.setProjectId(pjId);
                logObj.setType(MQType.realDelete);
                logList.add(logObj);
            }
            ActivityLogRunnable activityLog = new ActivityLogRunnable(logList);
            ActivityLogUtil.pool.submit(activityLog);
        }
    }

    // 删除对象和信息点, 未删除相关关系
    private static void deleteObjects(String pjId, ArrayList<String> deleteObj, boolean isMonitor) throws Exception {
        if(deleteObj.size() == 0)
            return;
        // 删除Objects表中的数据
        deleteObjAndInfos(pjId, deleteObj);
        if(isMonitor){
            String time = CommonUtil.getTime();
            ArrayList<ActivityLogObject> logList = new ArrayList<>();
            for(String id : deleteObj){
                ActivityLogObject logObj = new ActivityLogObject();
                logObj.setId(id);
                logObj.setTime(time);
                logObj.setOt(ObjectUtil.getObjectTypeById(id));
                logObj.setProjectId(pjId);
                logObj.setType(MQType.realDelete);
                logList.add(logObj);
            }
            ActivityLogRunnable activityLog = new ActivityLogRunnable(logList);
            ActivityLogUtil.pool.submit(activityLog);
        }
    }

    private static void deleteObjAndInfos(String pjId, ArrayList<String> deleteObj) throws Exception {
        JSONArray delete = new JSONArray();
        for (int i = 0; i < deleteObj.size(); ++i) {
            JSONObject obj = new JSONObject();
            obj.put("id", deleteObj.get(i));
            CommonUtil.rewriteObjectTableCriteria(pjId, obj, "id", true);
            delete.add(obj);
        }
        CommonUtil.combine_delete(HBaseTable.objects, delete);
        // 删除infos表中的数据
        for (int i = 0; i < delete.size(); ++i) {
            JSONObject tmp = (JSONObject) delete.get(i);
            if(Const.OBJ_TYPE_PROPERTY.equals(tmp.getString(ObjectTable.KEY_OBJ_TYPE))){
                tmp.put(ObjectTable.KEY_PJ_ID, null);
            }
        }
        CommonUtil.combine_delete(HBaseTable.obj_infos, delete);
    }

    // 作废对象
    private static void disableObjects(String pjId, ArrayList<String> disableObj, boolean isMonitor) throws Exception {
        if(disableObj.size() == 0)
            return;
        JSONArray disable = new JSONArray();
        for (int i = 0; i < disableObj.size(); ++i) {
            JSONObject obj = new JSONObject();
            obj.put("id", disableObj.get(i));
            CommonUtil.rewriteObjectTableCriteria(pjId, obj, "id", true);
            disable.add(obj);
        }
        JSONObject set = new JSONObject();
        set.put(Const.TABLE_KEY_NAME_VALID, false);
        set.put(ObjectTable.KEY_DEL_TIME, CommonUtil.getTime());
        CommonUtil.combine_update(HBaseTable.objects, disable, set);
        if(isMonitor){
            String time = CommonUtil.getTime();
            ArrayList<ActivityLogObject> logList = new ArrayList<>();
            for(String id : disableObj){
                ActivityLogObject logObj = new ActivityLogObject();
                logObj.setId(id);
                logObj.setTime(time);
                logObj.setOt(ObjectUtil.getObjectTypeById(id));
                logObj.setType(MQType.objectDelete);
                logObj.setProjectId(pjId);
                logList.add(logObj);
            }
            ActivityLogRunnable activityLog = new ActivityLogRunnable(logList);
            ActivityLogUtil.pool.submit(activityLog);
        }
    }

    // 作废对象
    private static void disablePropertyObjects(String pjId, ArrayList<String> disableObj, boolean isMonitor) throws Exception {
        if(disableObj.size() == 0)
            return;
        JSONArray disable = new JSONArray();
        for (int i = 0; i < disableObj.size(); ++i) {
            JSONObject obj = new JSONObject();
            obj.put("id", disableObj.get(i));
            CommonUtil.rewriteObjectTableCriteria(pjId, obj, "id", true);
            disable.add(obj);
        }
        JSONObject set = new JSONObject();
        set.put(Const.TABLE_KEY_NAME_VALID, false);
        set.put(ObjectTable.KEY_DEL_TIME, CommonUtil.getTime());
        CommonUtil.combine_update(HBaseTable.objects, disable, set);
        if(isMonitor){
            String time = CommonUtil.getTime();
            ArrayList<ActivityLogObject> logList = new ArrayList<>();
            for(String id : disableObj){
                ActivityLogObject logObj = new ActivityLogObject();
                logObj.setId(id);
                logObj.setTime(time);
                logObj.setOt(ObjectUtil.getObjectTypeById(id));
                logObj.setType(MQType.objectDelete);
                logObj.setProjectId(pjId);
                logList.add(logObj);
            }
            ActivityLogRunnable activityLog = new ActivityLogRunnable(logList);
            ActivityLogUtil.pool.submit(activityLog);
        }
    }
    public static HashMap<String, PhysicalWorldObject> getValidIds(String pjId, JSONArray objects, JSONArray errors) {
        HashMap<String, PhysicalWorldObject> idToFamily = new HashMap<>();
        HashSet<String> validIds = new HashSet<>();
        JSONArray criteria = new JSONArray();
        for (int i = objects.size() - 1; i > -1; --i) {
            JSONObject id = (JSONObject) objects.get(i);
            String idStr = id.get("id").toString();
            if (!DpUtils.checkId(idStr)) {
                moveToError(objects, i, errors, "id不合法");
                continue;
            }
            if (validIds.contains(idStr)) {
                moveToError(objects, i, errors, "id重复");
                continue;
            }
            validIds.add(idStr);
        }
        idToFamily = filterValidObjectIds(pjId, validIds);
        // 将不存在的移除
        for (int i = objects.size() - 1; i > -1; --i) {
            JSONObject id = (JSONObject) objects.get(i);
            if (!idToFamily.containsKey(id.get("id").toString()))
                moveToError(objects, i, errors, "资产不存在或不在该项目的权限内");
        }
        return idToFamily;
    }

    // 查看要修改的建筑, 楼层和项目是否冲突.
    public static void validateIsIdsSameProject(String pjId, JSONArray objects, JSONArray errors) throws Exception {
        JSONArray projArr = new JSONArray();
        HashSet<String> pjIdSet = new HashSet<>();
        for (int i = objects.size() - 1; i > -1; --i) {
            JSONObject single = (JSONObject) objects.get(i);
            JSONObject relation = (JSONObject) single.get("relation");
            if (relation != null && relation.keySet().size() > 0) {
                if (relation.get("project") != null && relation.get("project").toString().length() > 0) {
                    String updatePjId = relation.get("project").toString();
                    if (relation.get("project").toString().startsWith("Pj") && DpUtils.checkId(relation.get("project").toString())) {
                        JSONObject pjObj = new JSONObject();
                        pjObj.put(ProjectTable.KEY_PJ_ID, updatePjId.substring(2));
                        projArr.add(pjObj);
                    } else {
                        moveToError(objects, i, errors, "项目编号错误");
                    }
                }
            }
        }
        if(projArr.size() > 0) {
            JSONObject result = CommonUtil.combine_query(HBaseTable.projects, projArr);
            JSONArray content = (JSONArray) result.get("Content");
            for (int i = 0; i < content.size(); ++i) {
                JSONObject project = (JSONObject) content.get(i);
                if (project.get(ProjectTable.COL_VALID) != null && project.get(ProjectTable.COL_VALID).equals(true)) {
                    pjIdSet.add(Strings.nullToEmpty((String) project.get(ProjectTable.KEY_PJ_ID)));
                }
            }
        }
        for (int i = objects.size() - 1; i > -1; --i) {
            JSONObject single = (JSONObject) objects.get(i);
            JSONObject relation = (JSONObject) single.get("relation");
            if (relation != null && relation.keySet().size() > 0) {
                String projectId = pjId;
                if (relation.get("project") != null && relation.get("project").toString().length() > 0) {
                    String updatePjId = relation.get("project").toString();
                    if(pjIdSet.contains(updatePjId.substring(2))){
                        projectId = updatePjId.substring(2);
                    } else {
                        moveToError(objects, i, errors, "项目不存在");
                        continue;
                    }
                }
                for (String key : relation.keySet()) {
                    if(key.equals("link") && relation.get(key) == null)
                        continue;
                    if (!DpUtils.checkId(projectId, relation.get(key).toString())) {
                        moveToError(objects, i, errors, String.join("", "项目Pj", projectId, "与", relation.get(key).toString(), "不在一个项目内"));
                        break;
                    }
                }
            }
        }
    }


    /**
     * 验证种族和信息点(此处的验证信息点仅仅是要修改的种族和信息点是否对应).
     * @param objects  所有对象
     * @param errors  错误的对象
     * @param skipErrorInfo 是否跳过信息点的错误
     * @return
     * @throws Exception
     */
    public static Map<String, String> validateFamilyAndInfosSkipErrorInfo(String pjId, JSONArray objects, JSONArray errors, boolean skipErrorInfo) throws Exception{
        EqStaticInfo eqStaticInfo = new EqStaticInfo(pjId);
        Set<String> equipIdSet = new HashSet<>();
        a:
        for (int i = objects.size() - 1; i > -1; --i) {
            JSONObject single = (JSONObject) objects.get(i);
            if (single.get("family") == null) {
                if(single.get("relation") != null && ((JSONObject)single.get("relation")).get("link")!= null)
                {
                    String objId = ((JSONObject) single.get("relation")).get("link").toString();
                    if(!equipIdSet.contains(objId))
                        equipIdSet.add(objId);
                }
                continue;
            }
            String family = single.get("family").toString();
            if (!eqStaticInfo.getFamilySet().contains(family)) {
                moveToError(objects, i, errors, String.join("", "不存在的种族类型", family));
                continue;
            }
            HashMap<String, InfoPoint> allInfos = InfoCodeUtil.getWholeCommonInfosMap(pjId, family);
            JSONObject infos = (JSONObject) single.get("infos");
            ArrayList<String> skipErrorInfos = new ArrayList<>();
            for (String key : infos.keySet()) {
                if (!allInfos.containsKey(key) &&  !EqStaticInfo.propertyGeneralInfos.contains(key)) {
                    if(skipErrorInfo){
                        skipErrorInfos.add(key);
                    }else {
                        moveToError(objects, i, errors, String.join("", "信息点", key, "不属于种族", family));
                        continue a;
                    }
                }
            }
            if(skipErrorInfo)
                for(String infoCode : skipErrorInfos){
                    infos.remove(infoCode);
                }
            if(single.get("relation") != null && ((JSONObject)single.get("relation")).get("link")!= null)
            {
                String objId = ((JSONObject) single.get("relation")).get("link").toString();
                if(!equipIdSet.contains(objId))
                    equipIdSet.add(objId);
            }
        }
        Map<String, String> equipToDetailType = CommonUtil.getEquipCategory(pjId, equipIdSet, false);
        for (int i = objects.size() - 1; i > -1; --i) {
            JSONObject single = (JSONObject) objects.get(i);
            if (single.get("family") == null)
                continue;
            String family = single.get("family").toString();
            if(single.get("relation") != null && ((JSONObject)single.get("relation")).get("link")!= null)
            {
                String objId = ((JSONObject) single.get("relation")).get("link").toString();
                // 验证新关联的岗位是否匹配资产的种族
                if( equipToDetailType.get(objId) != null && eqStaticInfo.getEqToFamilyMap().get(equipToDetailType.get(objId)) != null && !eqStaticInfo.getEqToFamilyMap().get(equipToDetailType.get(objId)).equals(family))
                {
                    moveToError(objects, i, errors, String.join("", "link:", objId, "不属于种族", family));
                }
            }
        }
        return equipToDetailType;
    }



    public static HashMap<String, PhysicalWorldObject> validateExistence(String pjId, JSONArray objects, JSONArray errors) throws Exception {
        return getValidIds(pjId, objects, errors);
    }

    /**
     * 更新种族和项目
     * @param originalPjId 原项目id
     * @param validProperty
     * @param objects
     * @param errors 错误列表
     * @param equipToDetailType
     * @param skipErrorInfo
     * @return 返回种族改变的资产id集合
     * @throws Exception
     */
    public static Set<String> updateFamilyAndProject(String originalPjId, HashMap<String, PhysicalWorldObject> validProperty, JSONArray objects, JSONArray errors, Map<String, String> equipToDetailType, boolean skipErrorInfo) throws Exception {
        JSONArray objectAddCriteria = new JSONArray();
        JSONArray objectDeleteCriteria = new JSONArray();
        ActivityLogRunnable activityLog = new ActivityLogRunnable();
        Set<String> peIdSet = new HashSet<>();
        EqStaticInfo eqStaticInfo = new EqStaticInfo(originalPjId);
        a:for (int i = 0; i < objects.size(); ++i) {
            JSONObject single = (JSONObject) objects.get(i);
            String id = single.get("id").toString();
            JSONObject addObj = new JSONObject();
//            if(single.get("relation")!= null){
//                JSONObject relation = (JSONObject) single.get("relation");
//                if(relation.containsKey("link"))
//            }
            if (single.get("family") != null) // 修改种族
            {
                String family = single.get("family").toString();
                // 如果原种族和新种族不一致则更新种族
                if (validProperty.get(id) != null && !validProperty.get(id).getSub_type().equals(family)) {
                    addObj.put(ObjectTable.KEY_OBJ_TYPE, "Pe");
                    addObj.put(ObjectTable.COL_SUBTYPE, family);
                    if(!peIdSet.contains(id))
                        peIdSet.add(id);
                }
            } else { // 如果不修改种族, 则验证信息点是否属于原种族
                HashMap<String, InfoPoint> allInfos = InfoCodeUtil.getWholeCommonInfosMap(originalPjId, validProperty.get(id).getSub_type());
                JSONObject infos = (JSONObject) single.get("infos");
                if(single.get("relation") != null && ((JSONObject)single.get("relation")).get("link")!= null) {
                    String objId = ((JSONObject) single.get("relation")).get("link").toString();
                    if(!eqStaticInfo.getEqToFamilyMap().get(equipToDetailType.get(objId)).equals(validProperty.get(id).getSub_type())){
                        moveToError(objects, i, errors, String.join("", "link:", objId, "不属于种族", validProperty.get(id).getSub_type()));
                        continue ;
                    }
                }
                ArrayList<String> skipErrorInfoArr = new ArrayList<>();
                for (String key : infos.keySet()) {
                    if (!allInfos.containsKey(key) &&  !EqStaticInfo.propertyGeneralInfos.contains(key)) {
                        if(skipErrorInfo){
                            skipErrorInfoArr.add(key);
                        }else {
                            moveToError(objects, i, errors, String.join("", "信息点", key, "不属于种族", validProperty.get(id).getSub_type()));
                            continue a;
                        }
                    }
                }
                if(skipErrorInfo){
                    for(String errorInfoCode : skipErrorInfoArr){
                        infos.remove(errorInfoCode);
                    }
                }
            }
            JSONObject relation = (JSONObject) single.get("relation");
            if (relation != null && relation.get("project") != null) // 修改项目
            {
                String projectId = relation.get("project").toString();
                if (!projectId.startsWith(originalPjId, 2)) {
                    addObj.put(ObjectTable.KEY_PJ_ID, projectId.substring(2));
                }
            }
            if (addObj.keySet().size() == 0)
                continue;
            JSONObject delObj = new JSONObject();
            delObj.put(ObjectTable.KEY_PJ_ID, originalPjId);
            delObj.put(ObjectTable.KEY_OBJ_TYPE, validProperty.get(id).getObj_type());
            delObj.put(ObjectTable.KEY_OBJ_ID, validProperty.get(id).getObj_id());
            delObj.put(ObjectTable.COL_SUBTYPE, validProperty.get(id).getSub_type());
            objectDeleteCriteria.add(delObj);
            objectAddCriteria.add(addObj);

            fillUpdateObject(addObj, delObj, validProperty.get(id), activityLog);
        }
        if(objectDeleteCriteria.size() != 0)
            CommonUtil.combine_delete(HBaseTable.objects, objectDeleteCriteria);
        if(objectAddCriteria.size() != 0)
            CommonUtil.insert(true, HBaseTable.objects, objectAddCriteria);
        ActivityLogUtil.pool.submit(activityLog);
        return peIdSet;
    }

    private static void fillUpdateObject(JSONObject addObj, JSONObject delObj, PhysicalWorldObject physicalWorldObject, ActivityLogRunnable activityLog) {
        String newProjectId = null, newFamily = null, peId = null;
        if (addObj.get(ObjectTable.KEY_PJ_ID) == null)
            addObj.put(ObjectTable.KEY_PJ_ID, delObj.get(ObjectTable.KEY_PJ_ID));
        else{// 如果改项目, 则发送MQ消息
            newProjectId = addObj.get(ObjectTable.KEY_PJ_ID).toString();
        }
        if (addObj.get(ObjectTable.KEY_OBJ_TYPE) == null)
            addObj.put(ObjectTable.KEY_OBJ_TYPE, delObj.get(ObjectTable.KEY_OBJ_TYPE));
        if (addObj.get(ObjectTable.COL_SUBTYPE) == null)
            addObj.put(ObjectTable.COL_SUBTYPE, delObj.get(ObjectTable.COL_SUBTYPE));
        else{ // 如果改种族, 则记录新种族, 发送MQ消息
            newFamily = addObj.get(ObjectTable.COL_SUBTYPE).toString();
        }
        if (addObj.get(ObjectTable.KEY_OBJ_ID) == null)
            addObj.put(ObjectTable.KEY_OBJ_ID, delObj.get(ObjectTable.KEY_OBJ_ID));
        if (addObj.get(ObjectTable.KEY_CREATE_TIME) == null)
            addObj.put(ObjectTable.KEY_CREATE_TIME, physicalWorldObject.getCtime());
        addObj.put(ObjectTable.COL_VALID, true);
        peId = String.join("", Const.OBJ_TYPE_PROPERTY, addObj.get(ObjectTable.KEY_OBJ_ID).toString());
        if(newProjectId != null){
            activityLog.setUpdateProjectMsg_10(peId, delObj.get(ObjectTable.KEY_PJ_ID).toString(), newProjectId, null);
        }
        if(newFamily != null){
            activityLog.setUpdateFamilyMsg_9(peId, delObj.get(ObjectTable.COL_SUBTYPE).toString(), newFamily, delObj.get(ObjectTable.KEY_PJ_ID).toString(), null);
        }
    }

    public static void updateNewFamilyInfos(String pjId, JSONArray objects, HashMap<String, PhysicalWorldObject> validProperty, boolean isCoverInfos) throws Exception {
        JSONArray delInfo = new JSONArray();
        ActivityLogRunnable activityLog = new ActivityLogRunnable();
        HashMap<String, String> deletedInfosPeId = new HashMap<>(); // peId --> pjId
        HashMap<String, HashSet<String>> coverInfos = new HashMap<>();
        for (int i = 0; i < objects.size(); ++i) {
            JSONObject single = (JSONObject) objects.get(i);
            String id = single.get("id").toString();
            if(isCoverInfos){ // 覆盖关联对象的信息点
                if(coverInfos.containsKey(id))
                    continue;
                HashSet<String> infos = new HashSet<>();
                JSONObject infosNode = (JSONObject) single.get("infos");
                for(String infoCode : infosNode.keySet())
                {
                    if(!infos.contains(infoCode))
                        infos.add(infoCode);
                }
                coverInfos.put(id, infos);
            }
            if (single.get("family") == null)
                continue;
            // 如果该资产改变了种族, 则需要 1. 删除原种族独有的信息点. 2. update它的新信息点.
            if (!validProperty.get(id).getSub_type().startsWith(single.get("family").toString())) // 如果资产的种族改变了
            {
                HashSet<String> exclusiveInfos = (HashSet<String>) InfoCodeUtil.getDistinctInfos(pjId, validProperty.get(id).getSub_type(), single.get("family").toString());
                for (String key : exclusiveInfos) {
                    JSONObject del = new JSONObject();
                    del.put(ObjInfosTable.KEY_PJ_ID, null);
                    del.put(ObjInfosTable.KEY_OBJ_TYPE, Const.OBJ_TYPE_PROPERTY);
                    del.put(ObjInfosTable.KEY_OBJ_ID, id.substring(2));
                    del.put(ObjInfosTable.KEY_INFO_ID, key);
                    delInfo.add(del);
                }
            }
        }
        if(delInfo.size() != 0) {
            JSONObject result = CommonUtil.delete(true, HBaseTable.obj_infos, delInfo);
            if(result.get("Result") != null && result.get("Result").equals("success")){
                JSONArray arr = (JSONArray) result.get("Content");
                boolean hasDeleted = false;
                for(int i = 0; i < arr.size(); ++i){
                    JSONObject count = (JSONObject) arr.get(i);
                    if(count.get("Count") != null && count.getLongValue("Count") > 0l)
                    {
                        hasDeleted = true;
                        break;
                    }
                }
                if(hasDeleted){
                    activityLog.setUpdateFamilyDeleteInfosMsg_11(null, pjId, null);
                    ActivityLogUtil.pool.submit(activityLog);
                }
            }
        }
        ObjectUtil.batchUpdate(pjId, objects, false, false, true, new CommonQueryCriteria());
        if(isCoverInfos)
            PropertyUtil.syncInfos(pjId, coverInfos);
    }

    public static void getPropertyRelation(String pjId, JSONArray result) {
        HashSet<String> idList = new HashSet<>();
        for (int i = 0; i < result.size(); ++i) {
            idList.add(((JSONObject) result.get(i)).get("id").toString());
        }
        HashMap<String, HashSet<String>> relation = PropertyRelUtil.queryByPropertyId(pjId, idList);
        for (int i = 0; i < result.size(); ++i) {
            JSONObject single = (JSONObject) result.get(i);
            HashSet<String> singleRelation = relation.get(single.get("id").toString());
            if (singleRelation == null || singleRelation.size() == 0)
                continue;
            JSONObject relationNode = new JSONObject();
            for (String id : singleRelation) {
                if (id.startsWith("Bd"))
                    relationNode.put("building", id);
                else if (id.startsWith("Ec") || id.startsWith("Eq"))
                    relationNode.put("link", id);
                else if (id.startsWith("Fl"))
                    relationNode.put("floor", id);
            }
            single.put("relation", relationNode);
        }
    }

    // 获取 criteria中 指定id 所关联的所有资产id, 并按照种族筛选
    public static HashSet<String> queryByRelation(String pjId, JSONObject criteria) throws Exception{
        String id = null;
        if (criteria.get("id") == null)
            id = String.join("", "Pj", pjId);
        else
            id = criteria.get("id").toString();
        HashSet<String> familySet = new HashSet<>();
        JSONArray arr = (JSONArray) criteria.get("family");
        if (arr != null) {
            for (int i = 0; i < arr.size(); ++i) {
                familySet.add(arr.get(i).toString());
            }
        }
        HashSet<String> objId = new HashSet<>();
        objId.add(id);
        HashSet<String> result = new HashSet<>();
        JSONArray criterias = new JSONArray();
        if (!id.startsWith(Const.OBJ_TYPE_PROJECT)) {
            HashMap<String, HashSet<String>> objIdPropertyIds = PropertyRelUtil.queryByObjId(pjId, objId);
            HashSet<String> propertyIds = objIdPropertyIds.get(id);

            if (propertyIds == null || propertyIds.size() == 0)
                return result;
            for (String propertyId : propertyIds) {
                JSONObject obj = new JSONObject();
                obj.put(ObjectTable.KEY_PJ_ID, pjId);
                obj.put(ObjectTable.KEY_OBJ_TYPE, Const.OBJ_TYPE_PROPERTY);
                obj.put(ObjectTable.KEY_OBJ_ID, propertyId.substring(2));
                criterias.add(obj);
            }
        }else
        {
            if(familySet.size() == 0) {
                JSONObject obj = new JSONObject();
                obj.put(ObjectTable.KEY_PJ_ID, pjId);
                obj.put(ObjectTable.KEY_OBJ_TYPE, Const.OBJ_TYPE_PROPERTY);
                criterias.add(obj);
            }else {
                for(String family : familySet)
                {
                    JSONObject obj = new JSONObject();
                    obj.put(ObjectTable.KEY_PJ_ID, pjId);
                    obj.put(ObjectTable.KEY_OBJ_TYPE, Const.OBJ_TYPE_PROPERTY);
                    obj.put(ObjectTable.COL_SUBTYPE, family);
                    criterias.add(obj);
                }
            }
        }
        try {
            JSONObject ret = CommonUtil.combine_query(HBaseTable.objects, criterias);
            if (!ret.get("Result").equals("success"))
                return result;
            JSONArray retArr = (JSONArray) ret.get("Content");
            for (int i = 0; i < retArr.size(); ++i) {
                JSONObject single = (JSONObject) retArr.get(i);
                if(familySet.size() > 0) {
                    if(!familySet.contains(single.getString(ObjectTable.COL_SUBTYPE)))
                        continue;
                }
                result.add(String.join("", Const.OBJ_TYPE_PROPERTY, single.get(ObjectTable.KEY_OBJ_ID).toString()));
            }
            return result;
        } catch (Exception e) {
            throw e;
        }
    }
    // 同步peids里面所有id 关联的岗位的信息点.(此处同步为补全彼此没有的信息点的值, 并不会相互覆盖信息点的值)
    // 调用者必须确保peids里面的id是存在且可用的对象
    public static void complementInfos(String pjId, HashSet<String> peids)
    {
        // 1. 获取peids 对应的设备或部件id
        HashMap<String, String> bindIds = PropertyRelUtil.getLinks(pjId, peids); // key --> Peid, value --> Eqid, Ecid
        HashMap<String, JSONObject> idInfoMap = new HashMap<>();
        // 2. 获取所有对象的信息点, 并整理出每个id的每个信息点的当前值
        HashSet<String> allId = new HashSet<>(peids);
        for(String key : bindIds.keySet())
        {
            if(!allId.contains(bindIds.get(key)))
                allId.add(bindIds.get(key));
        }
        JSONArray idArray = new JSONArray();
        JSONArray idInfos = null;
        for(String id : allId)
        {
            JSONObject obj = new JSONObject();
            obj.put("id", id);
            idArray.add(obj);
        }
        try {
            EqStaticInfo eqStaticInfo = new EqStaticInfo(pjId);
            idInfos = CommonUtil.batchQuerySelfInfo(pjId, idArray, new CommonQueryCriteria(), eqStaticInfo);
        } catch (Exception e) {
            return;
        }
        for(int i = 0; i < idInfos.size(); ++i)
        {
            JSONObject obj = (JSONObject) idInfos.get(i);
            if(obj.get("id") != null && !idInfoMap.containsKey(obj.get("id").toString()))
            {
                idInfoMap.put(obj.get("id").toString(), obj);
            }
        }
        // 3. 依次对比对应两id的所有信息点, 并判断该信息点是不是属于当前资产或设备的类型,
        // 属于 --> 则判断自己有没有该信息点的值, 没有则添加, 有则忽略该信息点
        // 不属于 --> 忽略该信息点
        JSONArray update = new JSONArray();
        for(String id : bindIds.keySet())
        {
            if(idInfoMap.get(id) == null || idInfoMap.get(id).get("infos") == null || idInfoMap.get(bindIds.get(id)) == null || idInfoMap.get(bindIds.get(id)).get("infos") == null)
                continue;
            JSONObject obj1 = new JSONObject();
            obj1.put("id", id);
            JSONObject obj2 = new JSONObject();
            obj2.put("id", bindIds.get(id));
            getUpdateJSONObjects(pjId, obj1, obj2, (JSONObject) idInfoMap.get(id).get("infos"), (JSONObject) idInfoMap.get(bindIds.get(id)).get("infos"),
                    idInfoMap.get(id).get("family").toString(), CommonUtil.getEquipCategory(bindIds.get(id), false));
            if(obj1.keySet().size() > 0)
                update.add(obj1);
            if(obj2.keySet().size() > 0)
                update.add(obj2);
        }
        try {
            ObjectUtil.batchUpdate(pjId, update, false, false, true, new CommonQueryCriteria());
        } catch (Exception e) {
            e.printStackTrace();
        }
        //更新update.
    }

    private static void getUpdateJSONObjects(String pjId, JSONObject obj1, JSONObject obj2, JSONObject infos1, JSONObject infos2, String family, String eqType) {
        JSONObject updateInfos1 = new JSONObject();
        obj1.put("infos", updateInfos1);
        JSONObject updateInfos2 = new JSONObject();
        obj2.put("infos", updateInfos2);
        complementInfos(pjId, updateInfos1, infos1, infos2, family);
        complementInfos(pjId, updateInfos2, infos2, infos1, eqType);
    }

    private static void complementInfos(String pjId, JSONObject updateInfos, JSONObject infos1, JSONObject infos2, String type) {
        String time = CommonUtil.getTime();
        for(String infoKey : infos2.keySet())
        {
            if(infoKey.endsWith("_his"))
                continue;
            if(!infos1.keySet().contains(infoKey) && InfoCodeUtil.getWholeInfosWithCustomMap(pjId, type).get(type).containsKey(infoKey)) // 需要添加的信息点
            {
                // to-do
                // 只更新现在和以后起作用的信息点值
                JSONArray pairs = getCurrentFutureEffectiveInfos((JSONArray) infos2.get(String.join("", infoKey, "_his")), time);
                updateInfos.put(infoKey, pairs);// _his
            }
        }
    }
    //
    public static JSONArray getCurrentFutureEffectiveInfos(JSONArray singleInfo, String currentTime)
    {
        JSONArray currentFuture = new JSONArray();
        for(int i = singleInfo.size() - 1; i > -1; --i)
        {
            JSONObject pair = (JSONObject) singleInfo.get(i);
            currentFuture.add(pair);
            if(pair.get("time").toString().compareTo(currentTime) <= 0)
            {
                break;
            }
        }
        return currentFuture;
    }
    // 同步信息点, idInfos信息点 key --> 主id, value --> 修改的信息点
    public static void syncInfos(String pjId, HashMap<String, HashSet<String>> idInfos)
    {
        // 获取peids 对应的设备或部件id
        HashMap<String, String> bindIds = PropertyRelUtil.getLinks(pjId, idInfos.keySet()); // key --> 主id, value --> 从id
        // 剔除掉 从id 中被删除或废除的设备
        if(bindIds.size() == 0)
            return;
        HashMap<String, String> idType = new HashMap<>(); // key --> 主id + 从id , value --> 4位设备类型, 3位编码类型, 6位部件类型
        try {
            removeInvalidSubId(pjId, bindIds, idType);
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
        // 检查数据字典中 从id 的设备或资产类型是否存在需要被覆盖的信息点
        try {
            filterValidInfos(pjId, idInfos, bindIds, idType);
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
        if(idInfos.size() == 0 || idType.size() == 0)
            return;
        // 查询所有id需要覆盖和被覆盖的信息点
        HashMap<String, HashMap<String, JSONArray>> allInfos = getAllInfos(idInfos, bindIds);
        if(allInfos == null)
            return;
        // 对比信息点值(当前起作用, 以及将来起作用的值, 不比较过期的历史值)
        // 如果不一致, 删除 从id 的该信息点(当前起作用的信息点和未来起作用的信息点), 记录当前起作用的time
        // 添加 主id 当前起作用和未来生效的信息点到 从id(当前起作用的时间为刚刚记录的time).
        JSONArray delete = new JSONArray();
        JSONArray insert = new JSONArray();
        compareSync(idInfos, bindIds, allInfos, delete, insert);

        // 旧删除信息点, 添加新信息点
        try {
            if(delete.size() > 0)
                CommonUtil.delete(true, HBaseTable.obj_infos, delete);
            if(insert.size() > 0)
                CommonUtil.insert(true, HBaseTable.obj_infos, insert);
        } catch (Exception e) {
            return;
        }
    }
    // 比较, 然后返回需要同步信息点的值
    private static void compareSync(HashMap<String, HashSet<String>> idInfos, HashMap<String, String> bindIds, HashMap<String, HashMap<String, JSONArray>> allInfos, JSONArray delete, JSONArray insert) {
        String time = CommonUtil.getTime();
        for(String id : idInfos.keySet())
        {
            HashMap<String, JSONArray> sInfos = allInfos.get(id);
            HashMap<String, JSONArray> mInfos = allInfos.get(bindIds.get(id));
            Object mProjectId = null;
            Object mObjectType = null;
            Object mObjectId = null;
            JSONObject idJSON = new JSONObject();
            idJSON.put("id", bindIds.get(id));
            try {
                DpUtils.rewriteCriteria(idJSON, "id", false);
                mProjectId = idJSON.get(ObjInfosTable.KEY_PJ_ID);
                mObjectType = idJSON.get(ObjInfosTable.KEY_OBJ_TYPE);
                mObjectId = idJSON.get(ObjInfosTable.KEY_OBJ_ID);
            } catch (Exception e) {
                e.printStackTrace();
            }
            // 主id     从id
            //  无       有        删除从id的该信息点值
            //  有       有        先删除 从id 该信息点的值, 再添加上主信息点的值
            //  有       无        直接添加主信息点的值
            if(mInfos != null) {
                for (String infoCode : mInfos.keySet()) {
                    JSONArray m = mInfos.get(infoCode);
                    JSONArray s = sInfos == null? null : sInfos.get(infoCode);
                    // 主id 中改信息点已经删除, 则删除 从id 中的该信息点的所有值.
                    if (s == null || s.size() == 0)     // 无 有
                    {
                        for (int i = 0; i < m.size(); ++i)
                            delete.add(m.get(i));
                    } else   // 有    有
                    {
                        String sTime = getTimeOfEffectiveOrFutureValue(s, time);
                        String mTime = getTimeOfEffectiveOrFutureValue(m, time);

                        for (int i = 0; i < m.size(); ++i) {
                            JSONObject singleValue = (JSONObject) m.get(i);
                            if (mTime.compareTo(singleValue.get(ObjInfosTable.KEY_TIME).toString()) <= 0)
                                delete.add(singleValue);
                        }
                        for (int i = 0; i < s.size(); ++i) {
                            JSONObject singleValue = (JSONObject) s.get(i);
                            if (sTime.compareTo(singleValue.get(ObjInfosTable.KEY_TIME).toString()) <= 0) {
                                singleValue.put(ObjInfosTable.KEY_PJ_ID, mProjectId);
                                singleValue.put(ObjInfosTable.KEY_OBJ_TYPE, mObjectType);
                                singleValue.put(ObjInfosTable.KEY_OBJ_ID, mObjectId);
                                if (sTime.compareTo(singleValue.get(ObjInfosTable.KEY_TIME).toString()) == 0)
                                    singleValue.put(ObjInfosTable.KEY_TIME, mTime);
                                insert.add(singleValue);
                            }
                        }
                    }
                }
            }
            if(sInfos != null) {
                for (String infoCode : sInfos.keySet()) {
                    if (mInfos != null && mInfos.containsKey(infoCode))
                        continue;
                    JSONArray sArray = sInfos.get(infoCode);
                    String sTime = getTimeOfEffectiveOrFutureValue(sArray, time);
                    for (int i = 0; i < sArray.size(); ++i) {
                        JSONObject singleValue = (JSONObject) sArray.get(i);
                        if (sTime.compareTo(singleValue.get(ObjInfosTable.KEY_TIME).toString()) <= 0) {
                            singleValue.put(ObjInfosTable.KEY_PJ_ID, mProjectId);
                            singleValue.put(ObjInfosTable.KEY_OBJ_TYPE, mObjectType);
                            singleValue.put(ObjInfosTable.KEY_OBJ_ID, mObjectId);
                            insert.add(singleValue);
                        }
                    }
                }
            }
        }
    }
    // 获取该信息点上当前起作用的值, 如果没有, 则取离现在最近的未来值
    private static String getTimeOfEffectiveOrFutureValue(JSONArray m, String currentTime) {
        String[] times = new String[m.size()];
        String leastEffectiveTime = null;
        for(int i = 0; i < m.size(); ++i)
        {
            JSONObject single = (JSONObject) m.get(i);
            String time = single.get(ObjInfosTable.KEY_TIME).toString();
            times[i] = time;
        }
        // 找到当前起作用的time  --> leastEffectiveTime
        Arrays.sort(times);
        for(int i = times.length - 1; i > -1; --i) {
            if(times[i].compareTo(currentTime) <= 0) {
                leastEffectiveTime = times[i];
                break;
            }
        }
        if(leastEffectiveTime == null)
            leastEffectiveTime = times[0];
        return leastEffectiveTime;
    }

    private static HashMap<String, HashMap<String, JSONArray>> getAllInfos(HashMap<String, HashSet<String>> idInfos, HashMap<String, String> bindIds) {
        HashMap<String, HashMap<String, JSONArray>> infos = new HashMap<>();
        JSONArray infosCriteria = new JSONArray();
        for(String id : idInfos.keySet())
        {
            HashSet<String> infoSet = idInfos.get(id);
            JSONObject sid = new JSONObject();
            sid.put("id", id);
            JSONObject mid = new JSONObject();
            mid.put("id", bindIds.get(id));
            try {
                DpUtils.rewriteCriteria(sid, "id", true);
                DpUtils.rewriteCriteria(mid, "id", true);
            } catch (Exception e) {
                e.printStackTrace();
                continue;
            }
            for(String infoCode : infoSet)
            {
                JSONObject singleObjs = new JSONObject();
                JSONObject singleObjm = new JSONObject();
                singleObjs.put(ObjInfosTable.KEY_PJ_ID, sid.get(ObjInfosTable.KEY_PJ_ID));
                singleObjs.put(ObjInfosTable.KEY_OBJ_TYPE, sid.get(ObjInfosTable.KEY_OBJ_TYPE));
                singleObjs.put(ObjInfosTable.KEY_OBJ_ID, sid.get(ObjInfosTable.KEY_OBJ_ID));
                singleObjs.put(ObjInfosTable.KEY_INFO_ID, infoCode);
                singleObjm.put(ObjInfosTable.KEY_PJ_ID, mid.get(ObjInfosTable.KEY_PJ_ID));
                singleObjm.put(ObjInfosTable.KEY_OBJ_TYPE, mid.get(ObjInfosTable.KEY_OBJ_TYPE));
                singleObjm.put(ObjInfosTable.KEY_OBJ_ID, mid.get(ObjInfosTable.KEY_OBJ_ID));
                singleObjm.put(ObjInfosTable.KEY_INFO_ID, infoCode);
                infosCriteria.add(singleObjs);
                infosCriteria.add(singleObjm);
            }
        }
        try {
            JSONObject result = CommonUtil.combine_query(HBaseTable.obj_infos, infosCriteria);
            JSONArray content = (JSONArray) result.get("Content");
            for(int i = 0; i < content.size(); ++i)
            {
                JSONObject singleValue = (JSONObject) content.get(i);
                String id = CommonUtil.combineId((String)singleValue.get(ObjInfosTable.KEY_PJ_ID), (String)singleValue.get(ObjInfosTable.KEY_OBJ_TYPE), (String)singleValue.get(ObjInfosTable.KEY_OBJ_ID));
                String infoCode = singleValue.get(ObjInfosTable.KEY_INFO_ID).toString();
                HashMap<String, JSONArray> idInfoMap = null;
                if(infos.containsKey(id))
                    idInfoMap = infos.get(id);
                else {
                    idInfoMap = new HashMap<>();
                    infos.put(id, idInfoMap);
                }
                JSONArray timeRange = null;
                if(idInfoMap.containsKey(infoCode))
                    timeRange = idInfoMap.get(infoCode);
                else
                    timeRange = new JSONArray();
                timeRange.add(singleValue);
                idInfoMap.put(infoCode, timeRange);
            }
            return infos;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private static void filterValidInfos(String pjId, HashMap<String, HashSet<String>> idInfos, HashMap<String, String> bindIds, HashMap<String, String> idType) throws Exception {
        for(String sid : idInfos.keySet())
        {
            String mid = bindIds.get(sid);
            HashSet<String> updateInfos = idInfos.get(sid);
            HashMap<String, InfoPoint> infos = InfoCodeUtil.getWholeInfosWithCustomMap(pjId, idType.get(mid)).get(idType.get(mid));
            HashSet<String> typeInfo = null;
            if(infos != null)
                typeInfo = new HashSet<>(infos.keySet());
            else
                throw new Exception("数据库连接异常");
            for(String sType : updateInfos)
            {
                // 从id 类型中不包含此信息点, 则不同步该信息点的值
                if(!typeInfo.contains(sType))
                    updateInfos.remove(sType);
            }
            if(updateInfos.size() == 0)
            {
                idInfos.remove(sid);
                bindIds.remove(sid);
                idType.remove(sid);
            }
        }
    }

    private static void removeInvalidSubId(String pjId, HashMap<String, String> bindIds, HashMap<String, String> idType) throws Exception {
        JSONArray criterias = new JSONArray();
        for(String keyId : bindIds.keySet())
        {
              JSONObject singleObj = new JSONObject();
              singleObj.put("id", bindIds.get(keyId));
            try {
                CommonUtil.rewriteObjectTableCriteria(pjId, singleObj, "id", true);
                criterias.add(singleObj);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        JSONObject result = CommonUtil.combine_query(HBaseTable.objects, criterias);
        if(!result.get("Result").equals("success"))
            throw new Exception("查询失败");
        JSONArray objects = (JSONArray) result.get("Content");
        HashSet<String> validIds = new HashSet<>();
        for(int i = 0; i < objects.size(); ++i)
        {
            JSONObject single = (JSONObject) objects.get(i);
            if(!single.get(Const.TABLE_KEY_NAME_VALID).equals(true))
                continue;
            String id = CommonUtil.combineId(pjId, single.get(ObjectTable.KEY_OBJ_TYPE).toString(), single.get(ObjectTable.KEY_OBJ_ID).toString());
            if(!validIds.contains(id)) {
                validIds.add(id);
                String type ;
                if(id.startsWith("Pe"))
                    type = single.get(ObjectTable.COL_SUBTYPE).toString();
                else
                    type = single.get(ObjectTable.COL_SUBTYPE).toString();
                idType.put(id, type);
            }
        }
        for(String keyId : bindIds.keySet())
        {
            if(!validIds.contains(bindIds.get(keyId)))
            {
                bindIds.remove(keyId);
            }
        }
    }

    public static String projectQuery(JSONArray criteriasNode) throws Exception {
        JSONArray criterias = new JSONArray();
        for(int i = criteriasNode.size() - 1; i > -1; --i){
            JSONObject single = criteriasNode.getJSONObject(i);
            if(single == null || single.getString("id") == null ||
                    !single.getString("id").startsWith("Pe") || single.getString("id").length() <3){
                criteriasNode.remove(i);
                continue;
            }
            String id = single.getString("id");
            JSONObject singleCriteria = new JSONObject();
            singleCriteria.put(ObjectTable.KEY_OBJ_ID, id.substring(2));
            singleCriteria.put(ObjectTable.KEY_OBJ_TYPE, Const.OBJ_TYPE_PROPERTY);
            criterias.add(singleCriteria);
        }
        JSONObject queryResult = CommonUtil.combine_query(HBaseTable.objects, criterias);
        JSONArray content = queryResult.getJSONArray("Content");
        Map<String, JSONObject> resultMap = new HashMap<>(); //
        for(int i = 0; i < content.size(); ++i){
            JSONObject single = content.getJSONObject(i);
            String objId = single.getString(ObjectTable.KEY_OBJ_ID);
            resultMap.put(objId, single);
        }
        for(int i = criteriasNode.size() - 1; i > -1; --i){
            JSONObject single = criteriasNode.getJSONObject(i);
            String objId = single.getString("id").substring(2);
            if(!resultMap.containsKey(objId)){
                criteriasNode.remove(i);
                continue;
            }
            JSONObject singleResult = resultMap.get(objId);
            single.put("projId", CommonMethods.getProject(singleResult.getString(ObjectTable.KEY_PJ_ID), true));
            single.put("valid", singleResult.get(ObjectTable.COL_VALID));
        }
        return DpUtils.returnJSONArray(criteriasNode, "");
    }

}
























