package com.sagacloud.endpoints;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Strings;
import com.sagacloud.dict.EqStaticInfo;
import com.sagacloud.dto.OrderSetDTO;
import com.sagacloud.exception.ExpressionException;
import com.sagacloud.utils.Calculator;
import com.sagacloud.utils.CommonUtil;
import com.sagacloud.utils.Const;
import com.sagacloud.utils.DpUtils;
import com.sagacloud.utils.OssUtils;
import com.sagacloud.utils.UdpMsgUtil;
import com.sagacloud.utils.ZillionAgentProvider;
import com.sagacloud.utils.objects.CommonQueryCriteria;

/**
 * Created by wushengzong on 2017/4/21.
 */
@Path("parameter")
public class StatusData {
    private static final Logger log = LoggerFactory.getLogger(StatusData.class);

    @Path("/set")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String set(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {
        String msg = DpUtils.validateJsonSchema(jsonStr, "jsonSchema/parameterSetSchema.json");
        if (!msg.isEmpty()) {
            return DpUtils.errorJsonMsg(msg);
        }

        JSONObject jsonObj = JSONObject.parseObject(jsonStr);
        String buildingId = jsonObj.get("building_id").toString();
        String meter = jsonObj.get("meter").toString();
        long funId = jsonObj.getLongValue("function_id");
        Object vObj = jsonObj.get("value");
		/*double value;
		if (vObj.getClass().equals(Long.class)) {
			value = ((Long) vObj).doubleValue();
		} else {
			value = (double) vObj;
		}*/

        JSONObject re = new JSONObject();
        re.put("Result", UdpMsgUtil.setData(buildingId, meter, funId, vObj));
        re.put("Input", jsonObj);

        return re.toString();
    }

    @Path("/bupdate")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String bUpdate(@QueryParam(Const.PROJECT_ID) String projectId, @QueryParam("debug") String debug, String jsonStr) throws Exception {
        JSONObject inputObj = JSONObject.parseObject(jsonStr);
        JSONArray arr = (JSONArray) inputObj.get("items");
        JSONArray sets = new JSONArray();
        JSONArray criterias = new JSONArray();
        String nowStr = CommonUtil.getTime();
        for (int i = 0; i < arr.size(); i++) {
            JSONObject item = (JSONObject) arr.get(i);
            JSONObject setObj = new JSONObject();
            JSONObject criteria = new JSONObject();
            criteria.put("building", projectId.substring(2));
            criteria.put("meter", item.get("meter"));
            criteria.put("funcid", item.get("funcid"));
            criterias.add(criteria);
            setObj.put("receivetime", nowStr);
            setObj.put("data", item.get("data"));
            sets.add(setObj);

        }
        JSONObject json = JSONObject.parseObject("{\"QueryType\":\"batch_update\",\"Datatable\":\"original_present\"}");
        json.put("Criterias", criterias);
        json.put("Database", OssUtils.getProperty("fjd.database.name"));
        json.put("Sets", sets);
        log.info(json.toString());
        JSONObject result = ZillionAgentProvider.GetAgent().Query(json);
        JSONArray content = (JSONArray) result.get("Content");
        JSONArray inserObjs = new JSONArray();
        for (int i = 0; i < content.size(); i++) {
            if (((JSONObject) content.get(i)).getLongValue("Count") == 0) {
                JSONObject insertObj = (JSONObject) criterias.get(i);
                JSONObject valueObj = (JSONObject) sets.get(i);
                insertObj.put("data", valueObj.get("data"));
                insertObj.put("receivetime", valueObj.get("receivetime"));
                inserObjs.add(insertObj);
            }
        }
        if (inserObjs.size() > 0) {
            json = JSONObject.parseObject("{\"QueryType\":\"batch_insert\",\"Datatable\":\"original_present\"}");
            json.put("InsertObjects", inserObjs);
            json.put("Database", OssUtils.getProperty("fjd.database.name"));
            log.info(json.toString());
            json = ZillionAgentProvider.GetAgent().Query(json);
            log.info(json.toString());
        }
        return "{\"Result\":\"success\", \"ResultMsg\":\"\"}\n";
    }

    @Path("/setting")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String setting(@QueryParam(Const.PROJECT_ID) String projectId, @QueryParam("debug") String debug, String jsonStr) {
        long before = System.currentTimeMillis();
        String msg = DpUtils.validateJsonSchema(jsonStr, "jsonSchema/parameterSettingSchema.json");
        if (!msg.isEmpty()) {
            return DpUtils.errorJsonMsg(msg);
        }
        try {
            JSONObject jsonObj = JSONObject.parseObject(jsonStr);
            String id = jsonObj.get("id").toString();
            String code = jsonObj.get("code").toString();
            Object vObj = jsonObj.get("value");

            String metFun = (String) CommonUtil.getInfoById(projectId.substring(2, 12), id, code);
            if (metFun == null) {
                return DpUtils.errorJsonMsg("Assigned code does not exist");
            }

            String meter = metFun.substring(0, metFun.lastIndexOf("-"));
            long funId = Long.parseLong(metFun.substring(metFun.lastIndexOf("-") + 1));

            JSONObject re = JSONObject.parseObject("{}");
            long timeQ = System.currentTimeMillis() - before;
            before = System.currentTimeMillis();
            re.put("Result", UdpMsgUtil.setData(projectId.substring(2), meter, funId, vObj));
            long timeS = System.currentTimeMillis() - before;
            log.info(String.join(" ", "Take", String.valueOf(timeQ), "ms for pre-setting ", jsonObj.toString()));
            log.info(String.join(" ", "Take", String.valueOf(timeS), "ms for setting ", jsonObj.toString()));
            re.put("Input", jsonObj);
            if (debug != null) {
                JSONObject timeNode = new JSONObject();
                timeNode.put("pre-set", timeQ);
                timeNode.put("set", timeS);
                re.put("Time", timeNode);
            }
            return re.toString();
        } catch (Exception ex) {
            ex.printStackTrace();
            return DpUtils.errorJsonMsg(Arrays.toString(ex.getStackTrace()));
        }
    }
    
    @Path("/sendCommand")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String sendCommand(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {
        try {
            JSONObject jsonObj = JSONObject.parseObject(jsonStr);
            String metFun = jsonObj.getString("meterFuncId");
            Object setValue = jsonObj.get("value");
            
            String project = null;
            if (projectId == null || setValue == null || metFun == null || !metFun.contains("-")) {
                return DpUtils.errorJsonMsg("params error");
            }else {
            	project = projectId.substring(2);
            }

            String meter = metFun.substring(0, metFun.lastIndexOf("-"));
            String funId = metFun.substring(metFun.lastIndexOf("-") + 1);

            JSONObject re = new JSONObject();
            re.put("Result", "success");
            
            OrderSetDTO paramDTO = new OrderSetDTO(project, meter, funId, setValue);
            
            re.put("exeFeedback", UdpMsgUtil.setData(paramDTO));
            
            return re.toString();
        } catch (Exception ex) {
            ex.printStackTrace();
            return DpUtils.errorJsonMsg(Arrays.toString(ex.getStackTrace()));
        }
    }
    
    @Path("/sendCommandBatch")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String sendCommandBatch(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {
    	try {
    		JSONObject jsonObj = JSONObject.parseObject(jsonStr);
            JSONArray dataArray = jsonObj.getJSONArray("data");
            
            String project = null;
            if (projectId == null || dataArray == null || dataArray.isEmpty()) {
                return DpUtils.errorJsonMsg("params error");
            }else {
            	project = projectId.substring(2);
            }
            
            if(dataArray.size() > 300) {
            	return DpUtils.errorJsonMsg("data size too long");
            }
            
            List<OrderSetDTO> commands = new ArrayList<>();
            JSONObject jsonCommand = null;
            String meterFuncId = null;
            for(int i=0; i<dataArray.size(); i++) {
            	jsonCommand = dataArray.getJSONObject(i);
            	
            	meterFuncId = jsonCommand.getString("meterFuncId");
            	
            	commands.add(new OrderSetDTO(project, meterFuncId.substring(0, meterFuncId.lastIndexOf("-")), 
            			meterFuncId.substring(meterFuncId.lastIndexOf("-")+1), jsonCommand.get("value")));
            }
            
            boolean sendCommandsOK = UdpMsgUtil.setDataBatch(commands);
            
            
            if(sendCommandsOK) {
            	JSONObject re = new JSONObject();
            	re.put("Result", "success");
            	return re.toString();
            	
            }else {
            	return DpUtils.errorJsonMsg("send error");
            }
            
    	} catch (Exception ex) {
            ex.printStackTrace();
            return DpUtils.errorJsonMsg(Arrays.toString(ex.getStackTrace()));
        }
    }
    
    @Path("/commandExeResult")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String commandExeResult(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {
    	try {
    		JSONObject jsonObj = JSONObject.parseObject(jsonStr);
            String orderSeqNum = jsonObj.getString("orderSeqNum");
            
            if (projectId == null || orderSeqNum == null) {
                return DpUtils.errorJsonMsg("params error");
            }
            
    		JSONObject re = new JSONObject();
            re.put("Result", "success");
            re.put("exeFeedback", UdpMsgUtil.checkExe(orderSeqNum));
            
            return re.toString();
    	} catch (Exception ex) {
            ex.printStackTrace();
            return DpUtils.errorJsonMsg(Arrays.toString(ex.getStackTrace()));
        }
    }

    @Path("/query")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String query(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {

        if (Strings.nullToEmpty(jsonStr).trim().isEmpty()) { // 需要修改
            jsonStr = "{}";
        }
        JSONObject bodyNode = JSONObject.parseObject(jsonStr);
        JSONObject criteriaNode = (JSONObject) bodyNode.get("criteria");
        JSONObject json = JSONObject.parseObject("{\"QueryType\":\"select\",\"Datatable\":\"originalset_present\"}");
        json.put("Criteria", criteriaNode);
        json.put("Database", OssUtils.getProperty("fjd.database.name"));
        try {
            log.info(json.toString());
            JSONObject re = ZillionAgentProvider.GetAgent().Query(json);
            if (!re.get("Result").toString().equalsIgnoreCase("success") || re.getLongValue("Count") == 0) {
                log.warn("First query failed, going to query in the other table.");
                json.put("Datatable", "original_present");
                log.info(json.toString());
                JSONObject anotherRe = ZillionAgentProvider.GetAgent().Query(json);
                if (anotherRe.get("Result").toString().equalsIgnoreCase("success")) {
                    return anotherRe.toString();
                }
            }
            return re.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return DpUtils.errorJsonMsg(Arrays.toString(e.getStackTrace()));
        }

    }

    @Path("/queryparam")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String queryParam(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {
        JSONObject json = null;
        try {
            json = JSONObject.parseObject(jsonStr);
            json = (JSONObject) json.get("criteria");
            if (json.get("id") == null || json.get("code") == null)
                throw new Exception("json格式不正确.");
        } catch (Exception ex) {
            return DpUtils.errorJsonMsg(ex.getMessage() + Arrays.toString(ex.getStackTrace()));
        }
        JSONArray arr = new JSONArray();
        JSONObject obj = new JSONObject();
        obj.put("criterias", arr);
        arr.add(json);
        return fit(batchQueryParam(projectId, null, obj.toJSONString()));
    }

    private String fit(String returnData) {
        try {
            JSONObject json = JSONObject.parseObject(returnData);
            JSONArray arr = (JSONArray) json.get("Content");
            JSONObject ret = (JSONObject) arr.get(0);
            if (ret.get("error") != null) {
                json.put("Result", "failure");
                return json.toJSONString();
            } else
                return returnData;
        } catch (Exception ex) {
            return returnData;
        }
    }

    @Path("/queryConnection")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String queryConnection(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {
        try {
            JSONObject json = JSONObject.parseObject(jsonStr);
            JSONArray criterias = (JSONArray) json.get("criterias");
            for (int i = 0; i < criterias.size(); i++) {
                JSONObject inputItem = (JSONObject) criterias.get(i);
                if (inputItem.get("id") == null || inputItem.get("code") == null) {
                    throw new Exception("json格式不正确, Index = " + i);
                }
            }
            Map<String, JSONObject> objMap = queryObjects(projectId.substring(2), criterias);//objId -> JSONObject(id + infos)
            Map<String, String> idToMeterFunc = new HashMap<>();// id + code -> meter-funcid
            JSONArray queryPointJson = new JSONArray();//查询ps_virtual_point_status
            //检验信息点格式，拼接查询条件
            for (int i = 0; i < criterias.size(); ++i) {
                String id = criterias.getJSONObject(i).getString("id");
                String code = criterias.getJSONObject(i).getString("code");
                if (!objMap.containsKey(id)) {
                    criterias.getJSONObject(i).put("error", "没有此对象");
                    continue;
                }
                JSONObject infosNode = objMap.get(id).getJSONObject("infos");
                if (!infosNode.containsKey(code)) {
                    criterias.getJSONObject(i).put("error", "没有此信息点");
                    continue;
                }
                if (infosNode.get(code) instanceof String) {
                    String metFun = infosNode.getString(code);
                    try {
                        String meter = metFun.substring(0, metFun.lastIndexOf("-"));
                        long funId = Long.parseLong(metFun.substring(metFun.lastIndexOf("-") + 1));
                        idToMeterFunc.put(id + code, metFun);
                        //拼接查询条件
                        JSONObject queryNode = new JSONObject();
                        queryNode.put("project", projectId.substring(2));
                        queryNode.put("meter", meter);
                        queryNode.put("funcid", funId);
                        queryNode.put("updown", "up");
                        queryPointJson.add(queryNode);
                    } catch (Exception e) {
                        criterias.getJSONObject(i).put("error", "该信息点格式不支持查询");
                        continue;
                    }
                } else {
                    criterias.getJSONObject(i).put("error", "该信息点格式不支持查询");
                }
            }
            //查询ps_virtual_point_status
            JSONArray content = new JSONArray();
            if (queryPointJson.size() > 0) {
                JSONObject qJson = JSONObject.parseObject("{\"QueryType\":\"combine_select\",\"Datatable\":\"ps_virtual_point_status\"}");
                qJson.put(Const.JSON_FIELD_CRITERIAS, queryPointJson);
                qJson.put("Database", OssUtils.getProperty("fjd.database.name"));
                JSONObject re = ZillionAgentProvider.GetAgent().Query(qJson);
                content = re.getJSONArray("Content");
            }
            Map<String, JSONObject> meterFuncToData = new HashMap<>();//meter+funcid -> JsonObject
            for (int i = 0; i < content.size(); ++i) {
                JSONObject one = content.getJSONObject(i);
                String key = String.join("-", one.getString("meter"), one.getString("funcid"));
                meterFuncToData.put(key, one);
            }

            for (int i = 0; i < criterias.size(); ++i) {
                JSONObject one = criterias.getJSONObject(i);
                if (one.containsKey("error")) {
                    continue;
                }
                String idCode = one.getString("id") + one.getString("code");
                JSONObject dataNode = meterFuncToData.get(idToMeterFunc.get(idCode));
                if (dataNode != null) {
                    dataNode.keySet().forEach(key -> {
                        one.put(key, dataNode.get(key));
                    });
                }
            }
            return DpUtils.returnJSONArray(criterias, "");

        } catch (Exception e) {
            e.printStackTrace();
            return DpUtils.errorJsonMsg(Arrays.toString(e.getStackTrace()));
        }
    }

    @Path("/batch_query_param")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String batchQueryParam(@QueryParam(Const.PROJECT_ID) String projectId, @QueryParam("debug") String debug, String jsonStr) {
        try {
            JSONObject json = JSONObject.parseObject(jsonStr);
            JSONArray criterias = (JSONArray) json.get("criterias");
            for (int i = 0; i < criterias.size(); i++) {
                JSONObject inputItem = (JSONObject) criterias.get(i);
                if (inputItem.get("id") == null || inputItem.get("code") == null) {
                    throw new Exception("json格式不正确, Index = " + i);
                }
            }

            Map<String, JSONObject> objsMap = queryObjects(projectId.substring(2), criterias);    // id  --> JSONObject(id + infos)
            Map<String, JSONObject> varsMap = new HashMap<>();      // id  --> JSONObject(id + infos)
            HashMap<String, JSONObject> exprMap = new HashMap<>();     //  id + code --> exprJSON(包含表达式, 公式, 以及最后计算表达式之后的结果("data")))
            JSONArray varsCriteria = new JSONArray();
            JSONArray keys = new JSONArray();
            HashMap<String, String> idToMeterFunc = new HashMap<>();
            processInfoCode(projectId, objsMap, exprMap, varsCriteria, keys, criterias, idToMeterFunc, true, "-");
            if (varsCriteria.size() > 0) {  // 查询变量的信息点值
                varsMap = queryObjects(projectId.substring(2), varsCriteria);
                processInfoCode(projectId, varsMap, null, null, keys, varsCriteria, idToMeterFunc, false, "-");
            }
            JSONObject qJson = JSONObject.parseObject("{\"QueryType\":\"batch_get\",\"Datatable\":\"original_present\"}");
            qJson.put("Keys", keys);
            qJson.put("Database", OssUtils.getProperty("fjd.database.name"));
            log.info(qJson.toString());
            JSONObject re = new JSONObject();
            if(keys.size() == 0){
                re.put("Content", new JSONArray());
            }else
                re = ZillionAgentProvider.GetAgent().Query(qJson);
            Map<String, JSONObject> map = new HashMap<>();
            JSONArray contents = (JSONArray) re.get("Content");
            for (int i = 0; i < contents.size(); i++) {
                JSONObject item = (JSONObject) contents.get(i);
                String key = item.get("meter").toString() + "-" + item.get("funcid").toString();
                if (!map.keySet().contains(key)) {
                    map.put(key, item);
                }
            }
            if (exprMap.size() > 0) // 计算表达式
            {
                for (String idCode : exprMap.keySet()) {
                	Calculator calc = new Calculator();
                    JSONObject exprJSON = exprMap.get(idCode);
                    try {
                        JSONObject varsJSON = (JSONObject) exprJSON.get(Const.VARIABLES);
                        String[] varsList = calc.readExpression(exprJSON.get(Const.EXPRESSION).toString());
                        for (int i = 0; i < varsList.length; ++i)
                            if (!varsJSON.containsKey(varsList[i]))
                                throw new Exception("变量中不存在" + varsList[i]);
                        ArrayList<Double> varValues = new ArrayList<>();
                        String timeStr = "";
                        for (int i = 0; i < varsList.length; ++i) {
                            JSONObject varIdCodeJSON = (JSONObject) varsJSON.get(varsList[i]);
                            String metFunc = null;
                            if(varIdCodeJSON.containsKey("id")) {
                                String varIdCodeStr = String.join("", varIdCodeJSON.get("id").toString(), varIdCodeJSON.get("code").toString());
                                metFunc = idToMeterFunc.get(varIdCodeStr);
                            }else if(varIdCodeJSON.getString("meterfunc") != null){
                                metFunc = varIdCodeJSON.getString("meterfunc");
                            }
                            Double data = (Double) map.get(metFunc).get("data");
                            if(timeStr.compareTo(map.get(metFunc).getString("receivetime")) < 0){
                                timeStr = map.get(metFunc).getString("receivetime");
                            }
                            varValues.add(data);
                        }
                        Double resultData = calc.compute(varsList, varValues);
                        exprJSON.fluentPut("data", resultData).put("receivetime", timeStr);
                    } catch (Exception ex) {
                        exprJSON.put("error", ex.getMessage());
                        ex.printStackTrace();
                    }
                }
            }
            for (int i = 0; i < criterias.size(); i++) {
                JSONObject inputItem = (JSONObject) criterias.get(i);
                JSONObject obj = objsMap.get(inputItem.get("id").toString());
                if (obj == null) {
                    continue;
                }
                JSONObject jsonData = (JSONObject) obj.get("infos");
                inputItem.put("value", jsonData.get(inputItem.get("code").toString()));
                if (jsonData == null || !jsonData.containsKey(inputItem.get("code").toString())) {
                    continue;
                }
                if (jsonData.get(inputItem.get("code").toString()) == null) {
                    continue;
                }
                String key = jsonData.get(inputItem.get("code").toString()).toString();
                JSONObject presentData = map.get(key);
                if (presentData != null) {
                    presentData.keySet().forEach(keyStr -> {
                        inputItem.put(keyStr, presentData.get(keyStr));
                    });
                } else {
                    String idCode = String.join("", inputItem.get("id").toString(), inputItem.get("code").toString());
                    if (exprMap.containsKey(idCode)) {
                        JSONObject exprJSON = exprMap.get(idCode);
                        if (exprJSON.get("error") != null)
                            inputItem.put("error", exprJSON.get("error"));
                        else if (exprJSON.get("data") != null) {
                            inputItem.put("data", exprJSON.get("data"));
                            inputItem.put("receivetime", exprJSON.getString("receivetime"));
                        }
                    }
                }
            }
            return DpUtils.returnJSONArray(this.checkInterrupt(criterias), "");

        } catch (ExpressionException ex) {
            return DpUtils.errorJsonMsg(Arrays.toString(ex.getStackTrace()));
        } catch (Exception e) {
            e.printStackTrace();
            return DpUtils.errorJsonMsg(Arrays.toString(e.getStackTrace()));
        }
    }

    private JSONArray checkInterrupt(JSONArray arr) {
        int minutes = 15;
        if(OssUtils.getProperty("data.interrupt.def.minutes") != null){
            try {
                minutes = Integer.valueOf(OssUtils.getProperty("data.interrupt.def.minutes").trim());
            }catch (Exception e){
                minutes = 15;
                log.warn("data.interrupt.def.minutes is configrued a wrong value!");
            }
        }

        DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
        LocalDateTime baseTime = LocalDateTime.now().minusMinutes(minutes);
        for (int i = 0; i < arr.size(); i++) {
            JSONObject item = arr.getJSONObject(i);
            if (item.get("receivetime") == null) {
                continue;
            }
            LocalDateTime receiveTime = LocalDateTime.parse(item.getString("receivetime"), format);
            if (baseTime.isAfter(receiveTime)) {
                item.put("interrupt", true);
            } else {
                item.put("interrupt", false);
            }
        }
        return arr;
    }

    /**
     * @param projectId
     * @param objsMap
     * @param exprMap
     * @param varsCriteria
     * @param keys
     * @param criterias
     * @param idToMeterFunc
     * @param allowExpr
     * @param metFunSep 表号功能号分隔符
     */
    private void processInfoCode(String projectId, Map<String, JSONObject> objsMap, HashMap<String, JSONObject> exprMap, JSONArray varsCriteria,
                                 JSONArray keys, JSONArray criterias, HashMap<String, String> idToMeterFunc, boolean allowExpr, String metFunSep) {
        for (int i = 0; i < criterias.size(); i++) {
            JSONObject inputItem = (JSONObject) criterias.get(i);
            JSONObject obj = objsMap.get(inputItem.get("id").toString());
            if (obj == null) {
                inputItem.put("error", "没有此对象");
                continue;
            }
            JSONObject jsonData = (JSONObject) obj.get("infos");
            if (jsonData == null || !jsonData.containsKey(inputItem.get("code").toString())) {
                inputItem.put("error", "没有此信息点");
                continue;
            }
            Object infoObj = jsonData.get(inputItem.get("code").toString());
            String idCode = String.join("", inputItem.get("id").toString(), inputItem.get("code").toString());
            if (infoObj instanceof JSONObject) {
                if (!allowExpr) {
                    inputItem.put("error", "不支持嵌套表达式");
                    continue;
                }
                JSONObject calcJSON = (JSONObject) infoObj;
                if (calcJSON.get(Const.EXPRESSION) == null || calcJSON.get(Const.VARIABLES) == null || !(calcJSON.get(Const.VARIABLES) instanceof JSONObject)) {
                    inputItem.put("error", "信息点配置错误");
                    continue;
                }
                if (exprMap.containsKey(idCode)) {
                    continue;
                }
                exprMap.put(idCode, calcJSON);
                JSONObject vars = (JSONObject) calcJSON.get(Const.VARIABLES);
                for (String var : vars.keySet()) {
                    String metFun = vars.getJSONObject(var).getString("meterfunc");
                    if(metFun != null && metFun.contains("-"))
                    {
                        processMeterFunc(projectId, keys, null, null, metFun, metFunSep);
                    }else
                        varsCriteria.add(vars.get(var));
                }
            } else if (infoObj instanceof String) {
                String metFun = (String) infoObj;
                try {
                    processMeterFunc(projectId, keys, idToMeterFunc, idCode, metFun, metFunSep);
                } catch (Exception ex) {
                    inputItem.put("error", "该信息点不支持查询动态数据");
                    continue;
                }
            } else {
                inputItem.put("error", "信息点配置错误");
                continue;
            }
        }
    }

    /**
     * @param projectId
     * @param keys
     * @param idToMeterFunc
     * @param idCode
     * @param metFun
     * @param metFunSep 表号功能号分隔符 "-" 或者 "+"
     */
    private void processMeterFunc(String projectId, JSONArray keys, HashMap<String, String> idToMeterFunc, String idCode, String metFun, String metFunSep) {
        String meter = metFun.substring(0, metFun.lastIndexOf(metFunSep));
        long funId = Long.parseLong(metFun.substring(metFun.lastIndexOf(metFunSep) + 1));
        if (!this.isContain(keys, meter, funId)) {
            JSONObject keyNode = new JSONObject();
            keyNode.put("building", projectId.substring(2));
            keyNode.put("meter", meter);
            keyNode.put("funcid", funId);
            keys.add(keyNode);
        }
        if (idToMeterFunc != null && !idToMeterFunc.containsKey(idCode)) {
            idToMeterFunc.put(idCode, metFun);
        }
    }


    private boolean isContain(JSONArray keys, String meter, long funcId) {
        for (int i = 0; i < keys.size(); i++) {
            JSONObject key = keys.getJSONObject(i);
            if (key.getString("meter").equalsIgnoreCase(meter) && key.getLong("funcid").equals(funcId)) {
                return true;
            }
        }
        return false;
    }

    private Map<String, JSONObject> queryObjects(String pjId, JSONArray criterias) throws Exception {
        JSONArray arr = new JSONArray();
        for (int i = 0; i < criterias.size(); i++) {
            JSONObject item = (JSONObject) criterias.get(i);
            if(!item.containsKey("id"))
                continue;
            JSONObject newObj = new JSONObject();
            newObj.put("id", item.get("id"));
            arr.add(newObj);
        }
        CommonQueryCriteria cqc = new CommonQueryCriteria();
        cqc.setCustomInfo(true);
        cqc.setLinkInfos(true);
        EqStaticInfo eqStaticInfo = new EqStaticInfo(pjId);
        JSONArray content = CommonUtil.batchQuerySelfInfo(pjId, arr, cqc, eqStaticInfo);
        Map<String, JSONObject> reMap = new HashMap<>();
        for (int i = 0; i < content.size(); i++) {
            JSONObject item = (JSONObject) content.get(i);
            reMap.put(item.get("id").toString(), item);
        }
        return reMap;
    }

    @Path("/string/batch_query")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String batchQueryStr(@QueryParam(Const.PROJECT_ID) String projectId, @QueryParam("debug") String debug, String jsonStr) {
        try {
            JSONObject json = JSONObject.parseObject(jsonStr);
            JSONArray criterias = (JSONArray) json.get("criterias");
            for (int i = 0; i < criterias.size(); i++) {
                JSONObject inputItem = (JSONObject) criterias.get(i);
                if (inputItem.get("id") == null || inputItem.get("code") == null) {
                    throw new Exception("json格式不正确, Index = " + i);
                }
            }

            Map<String, JSONObject> objsMap = queryObjects(projectId.substring(2), criterias);    // id  --> JSONObject(id + infos)
            JSONArray varsCriteria = new JSONArray();
            JSONArray keys = new JSONArray();
            HashMap<String, String> idToMeterFunc = new HashMap<>();
            processInfoCode(projectId, objsMap, null, varsCriteria, keys, criterias, idToMeterFunc, false, "+");
            JSONObject qJson = JSONObject.parseObject("{\"QueryType\":\"batch_get\",\"Datatable\":\"original_str_data_present\"}");
            qJson.put("Keys", keys);
            qJson.put("Database", OssUtils.getProperty("fjd.database.name"));
            log.info(qJson.toString());
            JSONObject re = new JSONObject();
            if(keys.size() == 0){
                re.put("Content", new JSONArray());
            }else
                re = ZillionAgentProvider.GetAgent().Query(qJson);
            Map<String, JSONObject> map = new HashMap<>();
            JSONArray contents = (JSONArray) re.get("Content");
            for (int i = 0; i < contents.size(); i++) {
                JSONObject item = (JSONObject) contents.get(i);
                String key = item.get("meter").toString() + "+" + item.get("funcid").toString();
                if (!map.keySet().contains(key)) {
                    map.put(key, item);
                }
            }
            for (int i = 0; i < criterias.size(); i++) {
                JSONObject inputItem = (JSONObject) criterias.get(i);
                JSONObject obj = objsMap.get(inputItem.get("id").toString());
                if (obj == null) {
                    continue;
                }
                JSONObject jsonData = (JSONObject) obj.get("infos");
                inputItem.put("value", jsonData.get(inputItem.get("code").toString()));
                if (jsonData == null || !jsonData.containsKey(inputItem.get("code").toString())) {
                    continue;
                }
                if (jsonData.get(inputItem.get("code").toString()) == null) {
                    continue;
                }
                String key = jsonData.get(inputItem.get("code").toString()).toString();
                JSONObject presentData = map.get(key);
                if (presentData != null) {
                    presentData.keySet().forEach(keyStr -> {
                        inputItem.put(keyStr, presentData.get(keyStr));
                    });
                }
            }
            return DpUtils.returnJSONArray(this.checkInterrupt(criterias), "");
        } catch (ExpressionException ex) {
            return DpUtils.errorJsonMsg(Arrays.toString(ex.getStackTrace()));
        } catch (Exception e) {
            e.printStackTrace();
            return DpUtils.errorJsonMsg(Arrays.toString(e.getStackTrace()));
        }
    }

    @Path("string/batch_update2")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String bUpdateStr(@QueryParam(Const.PROJECT_ID) String projectId, @QueryParam("debug") String debug, String jsonStr) throws Exception {
        JSONObject inputObj = JSONObject.parseObject(jsonStr);
        JSONArray arr = (JSONArray) inputObj.get("items");
        JSONArray sets = new JSONArray();
        JSONArray criterias = new JSONArray();
        String nowStr = CommonUtil.getTime();
        for (int i = 0; i < arr.size(); i++) {
            JSONObject item = (JSONObject) arr.get(i);
            JSONObject setObj = new JSONObject();
            JSONObject criteria = new JSONObject();
            criteria.put("building", projectId.substring(2));
            criteria.put("meter", item.get("meter"));
            criteria.put("funcid", item.get("funcid"));
            criterias.add(criteria);
            if(item.getString("receivetime") == null){
                setObj.put("receivetime", nowStr);
            }else{
                setObj.put("receivetime", item.getString("receivetime"));
            }
            setObj.put("data", item.get("data"));
            sets.add(setObj);

        }
        JSONObject json = JSONObject.parseObject("{\"QueryType\":\"batch_update\",\"Datatable\":\"original_str_data_present\"}");
        json.put("Criterias", criterias);
        json.put("Database", OssUtils.getProperty("fjd.database.name"));
        json.put("Sets", sets);
        log.info(json.toString());
        JSONObject result = ZillionAgentProvider.GetAgent().Query(json);
        JSONArray content = (JSONArray) result.get("Content");
        JSONArray inserObjs = new JSONArray();
        for (int i = 0; i < content.size(); i++) {
            if (((JSONObject) content.get(i)).getLongValue("Count") == 0) {
                JSONObject insertObj = (JSONObject) criterias.get(i);
                JSONObject valueObj = (JSONObject) sets.get(i);
                insertObj.put("data", valueObj.get("data"));
                insertObj.put("receivetime", valueObj.get("receivetime"));
                inserObjs.add(insertObj);
            }
        }
        if (inserObjs.size() > 0) {
            json = JSONObject.parseObject("{\"QueryType\":\"batch_insert\",\"Datatable\":\"original_str_data_present\"}");
            json.put("InsertObjects", inserObjs);
            json.put("Database", OssUtils.getProperty("fjd.database.name"));
            log.info(json.toString());
            json = ZillionAgentProvider.GetAgent().Query(json);
            log.info(json.toString());
        }
        return "{\"Result\":\"success\", \"ResultMsg\":\"\"}\n";
    }

    @Path("/string/batch_update")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String bUpdateStr2(@QueryParam(Const.PROJECT_ID) String projectId, @QueryParam("debug") String debug, String jsonStr) {
        try {
            JSONObject json = JSONObject.parseObject(jsonStr);
            JSONArray insertObjects = (JSONArray) json.get("insertObjects");
            for (int i = 0; i < insertObjects.size(); i++) {
                JSONObject inputItem = (JSONObject) insertObjects.get(i);
                if (inputItem.get("id") == null || inputItem.get("code") == null) {
                    return DpUtils.errorJsonMsg("json格式不正确, Index = " + i);
//                    throw new Exception("json格式不正确, Index = " + i);
                }
            }

            Map<String, JSONObject> objsMap = queryObjects(projectId.substring(2), insertObjects);    // id  --> JSONObject(id + infos)
            JSONArray varsCriteria = new JSONArray();
            JSONArray keys = new JSONArray();
            //id+infoCode -> metFun
            HashMap<String, String> idToMeterFunc = new HashMap<>();
            processInfoCode(projectId, objsMap, null, varsCriteria, keys, insertObjects, idToMeterFunc, false, "+");
            System.out.println(keys.toString());
            idToMeterFunc.forEach((k, v) -> System.out.println(String.join("**",k,v)));
            List<JSONObject> toDel = new ArrayList<>();
            for (int i = 0; i < insertObjects.size(); i++) {
                JSONObject item = (JSONObject) insertObjects.get(i);
                String metFun = idToMeterFunc.get(item.getString("id") + item.getString("code"));
                if(metFun == null){
                    toDel.add(item);
                }
                JSONObject cri = new Historydata().generateMetFun(metFun, "+");
                cri.keySet().forEach(k -> item.put(k, cri.get(k)));
                item.fluentRemove("id").remove("code");
            }
            toDel.forEach(o -> insertObjects.remove(o));
            JSONArray sets = new JSONArray();
            JSONArray criterias = new JSONArray();
            String nowStr = CommonUtil.getTime();
            for (int i = 0; i < insertObjects.size(); i++) {
                JSONObject item = (JSONObject) insertObjects.get(i);
                JSONObject setObj = new JSONObject();
                JSONObject criteria = new JSONObject();
                criteria.put("building", projectId.substring(2));
                criteria.put("meter", item.get("meter"));
                criteria.put("funcid", item.get("funcid"));
                criterias.add(criteria);
                if(item.getString("receivetime") == null){
                    setObj.put("receivetime", nowStr);
                }else{
                    setObj.put("receivetime", item.getString("receivetime"));
                }
                setObj.put("data", item.get("data"));
                sets.add(setObj);

            }
            JSONObject qJson = JSONObject.parseObject("{\"QueryType\":\"batch_update\",\"Datatable\":\"original_str_data_present\"}");
            qJson.put("Criterias", criterias);
            qJson.put("Database", OssUtils.getProperty("fjd.database.name"));
            qJson.put("Sets", sets);
            log.info(qJson.toString());
            JSONObject result = ZillionAgentProvider.GetAgent().Query(qJson);
            JSONArray content = (JSONArray) result.get("Content");
            JSONArray inserObjs = new JSONArray();
            for (int i = 0; i < content.size(); i++) {
                if (((JSONObject) content.get(i)).getLongValue("Count") == 0) {
                    JSONObject insertObj = (JSONObject) criterias.get(i);
                    JSONObject valueObj = (JSONObject) sets.get(i);
                    insertObj.put("data", valueObj.get("data"));
                    insertObj.put("receivetime", valueObj.get("receivetime"));
                    inserObjs.add(insertObj);
                }
            }
            if (inserObjs.size() > 0) {
                qJson = JSONObject.parseObject("{\"QueryType\":\"batch_insert\",\"Datatable\":\"original_str_data_present\"}");
                qJson.put("InsertObjects", inserObjs);
                qJson.put("Database", OssUtils.getProperty("fjd.database.name"));
                log.info(qJson.toString());
                qJson = ZillionAgentProvider.GetAgent().Query(qJson);
                log.info(qJson.toString());
            }
            return "{\"Result\":\"success\", \"ResultMsg\":\"\"}\n";
        } catch (ExpressionException ex) {
            log.error("", ex);
            return DpUtils.errorJsonMsg(Arrays.toString(ex.getStackTrace()));
        } catch (Exception e) {
            log.error("", e);
            return DpUtils.errorJsonMsg(Arrays.toString(e.getStackTrace()));
        }
    }
}
