package com.sagacloud.endpoints;


import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.CompletableFuture;

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.apache.commons.lang.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.opengamma.strata.math.impl.interpolation.PiecewiseCubicHermiteSplineInterpolator;
import com.sagacloud.exception.DepthExceededException;
import com.sagacloud.exception.ExpressionException;
import com.sagacloud.utils.Calculator;
import com.sagacloud.utils.CommonMethods;
import com.sagacloud.utils.CommonUtil;
import com.sagacloud.utils.Const;
import com.sagacloud.utils.DpUtils;
import com.sagacloud.utils.ObjectUtil;
import com.sagacloud.utils.OssUtils;
import com.sagacloud.utils.ZillionAgentProvider;
import com.sagacloud.utils.objects.CommonQueryCriteria;

/**
 * Created by wushengzong on 2017/4/21.
 */
@Path("hisdata")
public class Historydata {
    private static final Logger log = LoggerFactory.getLogger(Historydata.class);
    private static String ENTITY_NAME = "historydata";
    private final static String[] ALLOW_INFOS = {"Holiday"};
    private final static String[] PERIODS = {"1min", "5min"};
    private final static Map<String, String> table2Timefield = new HashMap<>();

    static {
        table2Timefield.put("original_month", "receivetime");
        table2Timefield.put("original_day", "receivetime");
        table2Timefield.put("original_str_data", "receivetime");
        table2Timefield.put("fjd_0_near", "data_time");
        table2Timefield.put("fjd_statinst", "data_time");
        table2Timefield.put("fjd_statinst_1d", "data_time");
        table2Timefield.put("fjd_statinst_1h", "data_time");
    }

    @Path("/batch_insert")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String bInsertOriginal(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {
        return this.bInsert(projectId, jsonStr,"original_month", true, "-", false);

    }

    @Path("/batch_insert_period_data")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String bInsertPeriodData(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {
        return this.bInsertPeriod_data(projectId, jsonStr, "fjd_0_near", false);
    }

    @Path("/batch_insert_statinst")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String bInsertStatinst(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {
        return this.bInsertPeriod_data(projectId, jsonStr, "fjd_statinst", false);
    }

    @Path("/queryFjdStatisnst")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String queryFjdStatisnst(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {
    	String projectLocalId = ObjectUtil.getProjLocalID(projectId.substring(2, 12));
    	if(null == projectLocalId) {
    		return DpUtils.errorJsonMsg("找不到项目本地编码！");
    	}else {
    		projectLocalId = Const.OBJ_TYPE_PROJECT + projectLocalId;
    	}
        JSONObject json = JSONObject.parseObject(jsonStr);
        if (json.get("criteria") == null || json.getJSONObject("criteria").get("time_period") == null) {
            return DpUtils.errorJsonMsg("参数格式不对，criteria下需要time_period字段，值为 1h 或 1d");
        }
        String period = json.getJSONObject("criteria").getString("time_period");
        json.getJSONObject("criteria").remove("time_period");
        return queryData(projectId, json.toString(), "fjd_statinst_" + period, "-", projectLocalId);
    }
    
    @Path("/query_statinst")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String queryStatinst(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {
        JSONObject json = JSONObject.parseObject(jsonStr);
        if (json.get("criteria") == null || json.getJSONObject("criteria").get("time_period") == null) {
            return DpUtils.errorJsonMsg("参数格式不对，criteria下需要time_period字段，值为 1h 或 1d");
        }
        String period = json.getJSONObject("criteria").getString("time_period");
        json.getJSONObject("criteria").remove("time_period");
        return queryData(projectId, json.toString(), "fjd_statinst_" + period, "-", null);
    }

    /**
     * @param projectId
     * @param jsonStr
     * @param table
     * @param metFunSep 表号功能号分隔符 "-" 或者 "+"
     * @param pjLocalId Pj+项目本地编码，不为null时，查询能源数据时使用本地编码
     * @return
     */
    private String queryData(String projectId, String jsonStr, String table, String metFunSep, String pjLocalId) {
        JSONObject queryData = new JSONObject();
        pjLocalId = pjLocalId == null ? projectId : pjLocalId;
        try {
            JSONObject json = JSONObject.parseObject(jsonStr);
            json = (JSONObject) json.get("criteria");
            if (json.get("id") == null || json.get("code") == null || json.get(table2Timefield.get(table)) == null)
                throw new Exception("json格式不正确.");

            String id = json.get("id").toString();
            Object metFunObj = CommonUtil.getInfoById(projectId.substring(2, 12), id, (String) json.get("code"));
            if (metFunObj instanceof JSONObject){
            	// 绑点的为点位公式
            	
            	/*
	               {
						"expr": "(a+b+c)/3",
						"vars": {
							"a": {
								"meterfunc": "10112-11101"
							},
							"b": {
								"meterfunc": "10116-11101"
							},
							"c": {
								"meterfunc": "10117-11101"
							}
						}
					}
            	 */
            	// 检查点位公式是否规范
            	JSONObject calcJSON = (JSONObject) metFunObj;
                if (calcJSON.get(Const.EXPRESSION) == null || calcJSON.get(Const.VARIABLES) == null || !(calcJSON.get(Const.VARIABLES) instanceof JSONObject))
                    return DpUtils.errorJsonMsg("信息点值错误："+calcJSON.toJSONString());
                
                // 读expr, 返回变量.
                Calculator calc = new Calculator();
                String[] vars = calc.readExpression(calcJSON.get(Const.EXPRESSION).toString());
                
                // 匹配变量是否存在.
                for (int i = 0; i < vars.length; ++i)
                    if (!((JSONObject) calcJSON.get(Const.VARIABLES)).containsKey(vars[i]))
                        throw new Exception("信息点值错误，表达式变量中不存在" + vars[i]);
                
                LinkedHashMap<String, LinkedHashMap<String, Double>> varValue = new LinkedHashMap<>();
                // 获取各个变量的值. [{"时间":值}]
                LinkedHashMap<String, Double> lastMap = null;
                
                for (int i = 0; i < vars.length; ++i) {
                	// {"meterfunc":"10112-11101"}
                    JSONObject var = (JSONObject) ((JSONObject) calcJSON.get(Const.VARIABLES)).get(vars[i]);
                    if (var == null || var.get("meterfunc") == null || !var.getString("meterfunc").contains("-"))
                        throw new Exception("变量" + vars[i] + "的值配置错误");
                  
                    JSONArray arr = null;
                    
                    String metFunStr = var.getString("meterfunc"); 
                    
                    JSONObject criteria = this.generateMetFun(metFunStr, metFunSep);
                    JSONObject finalJson = json;
                    json.keySet().forEach(key -> {
                        if (!key.equalsIgnoreCase("id") && !key.equalsIgnoreCase("code")) {
                            criteria.put(key, finalJson.get(key));
                        }
                    });
                    queryData.put("criteria", criteria);
                    JSONObject resultContent = CommonMethods.queryData(pjLocalId, queryData.toJSONString(), table);
                    if(null == resultContent || !resultContent.containsKey("Content")) {
                    	return DpUtils.returnJSONArray(new JSONArray(), "");
                    }else {
                    	arr = resultContent.getJSONArray("Content");
                    }
                    
                	if (arr == null || arr.size() == 0)
                		return DpUtils.returnJSONArray(new JSONArray(), "");
                    LinkedHashMap<String, Double> timeValueMap = new LinkedHashMap<>();
                    for (int j = 0; j < arr.size(); ++j) {
                        JSONObject obj = (JSONObject) arr.get(j);
                        if (lastMap == null) {
                            timeValueMap.put(obj.get("receivetime").toString(), (Double) obj.get("data"));
                        } else {
                            if (lastMap.containsKey(obj.get("receivetime").toString())) {
                                timeValueMap.put(obj.get("receivetime").toString(), (Double) obj.get("data"));
                            }
                        }
                    }
                    lastMap = timeValueMap;
                    if (timeValueMap.size() == 0)
                        return DpUtils.returnJSONArray(new JSONArray(), "");
                    varValue.put(vars[i], timeValueMap);
                }
                // 依次匹配相同时间点, 计算, 添加到return里并返回.
                JSONArray result = new JSONArray();
                for (String time : lastMap.keySet()) {
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("receivetime", time);
                    result.add(jsonObject);
                    ArrayList<Double> values = new ArrayList<>();
                    for (int i = 0; i < vars.length; ++i) {
                        Map<String, Double> singleVar = varValue.get(vars[i]);
                        values.add(singleVar.get(time));
                    }
                    Double re = calc.compute(vars, values);
                    jsonObject.put("data", re);
                }
                return DpUtils.returnJSONArray(result, "");
            }else {
            	// 绑点为单个标号-功能号
            	String metFunStr = null; 
                if(null != metFunObj) {
                	metFunStr = metFunObj.toString();
                }
                if (metFunStr == null) {
                    return DpUtils.errorJsonMsg("Assigned code does not exist");
                }
                JSONObject criteria = this.generateMetFun(metFunStr, metFunSep);
                JSONObject finalJson = json;
                json.keySet().forEach(key -> {
                    if (!key.equalsIgnoreCase("id") && !key.equalsIgnoreCase("code")) {
                        criteria.put(key, finalJson.get(key));
                    }
                });
                queryData.put("criteria", criteria);
                return CommonMethods.queryData(pjLocalId, queryData.toJSONString(), table).toString();
            }
        } catch (Exception ex) {
            log.error("Error: ", ex);
            return DpUtils.errorJsonMsg(Arrays.toString(ex.getStackTrace()));
        }
    }

    private String bInsertPeriod_data(String projectId, String jsonStr, String table, boolean pjLocalId) {
        JSONObject json = JSONObject.parseObject(jsonStr);
        JSONArray jsonArray = (JSONArray) json.get("insertObjects");
        Map<String, JSONArray> period2Arr = new HashMap<>();
        JSONObject insertObject = null;
        Map<String, String> cache = new HashMap<>();
        String errorMsg = "";
        
        String building = projectId.substring(2, projectId.length());
        if(pjLocalId) {
        	building = ObjectUtil.getProjLocalID(building);
        }
        
        for (int i = 0; i < jsonArray.size(); i++) {
            insertObject = (JSONObject) jsonArray.get(i);
            try {
                this.rewrite(projectId, insertObject, cache, true, "-");
                insertObject.put("building", building);
            } catch (Exception e) {
                errorMsg = String.join("##", insertObject.toString() + " with error : " + e.getMessage());
                continue;
            }
            String timePeriod = insertObject.getString("time_period");
            if (period2Arr.get(timePeriod) == null) {
                period2Arr.put(timePeriod, new JSONArray());
            }
            period2Arr.get(timePeriod).add(insertObject);
        }
        if (period2Arr.values().stream().mapToInt(arr -> arr.size()).sum() == 0) {
            return DpUtils.errorJsonMsg(errorMsg);
        }
        JSONObject re = null;
        boolean success = false;
        for (String strPeriod : period2Arr.keySet()) {
            if (period2Arr.get(strPeriod).size() == 0) {
                continue;
            }
            JSONObject exeJson = JSONObject.parseObject(String.format("{\"QueryType\":\"batch_insert\",\"Datatable\":\"%s\"}", table + "_" + strPeriod));
            exeJson.put("InsertObjects", period2Arr.get(strPeriod));
            exeJson.put("Database", OssUtils.getProperty("fjd.database.name"));

            try {
                log.info(exeJson.toString());
                re = ZillionAgentProvider.GetAgent().Query(exeJson);
                success = true;
            } catch (Exception e) {
                log.error("", e);
                errorMsg = errorMsg + "####" + Arrays.toString(e.getStackTrace());
            }
        }
        if (success) {
            re.put("ResultMsg", errorMsg);
            return re.toString();
        } else {
            return DpUtils.errorJsonMsg(errorMsg);
        }

    }

    /**
     * @param projectId
     * @param jsonStr
     * @param table
     * @param ifAddInfo 表号功能号没配的话是否自动加上
     * @param metFunSep 表号功能号分隔符 "-" 或者 "+"
     * @param pjLocalId true,数据表building字段是项目本地编码；false,数据表building字段是项目数据字典id（不包含前缀Pj）
     * @return
     */
    private String bInsert(String projectId, String jsonStr, String table, boolean ifAddInfo, String metFunSep, boolean pjLocalId) {
        JSONObject json = JSONObject.parseObject(jsonStr);
        JSONArray jsonArray = (JSONArray) json.get("insertObjects");
        JSONArray insertObjects = new JSONArray();
        JSONObject insertObject = null;
        Map<String, String> cache = new HashMap<>();
        String errorMsg = "";
        
        String building = projectId.substring(2, projectId.length());
        if(pjLocalId) {
        	building = ObjectUtil.getProjLocalID(building);
        }
        
        for (int i = 0; i < jsonArray.size(); i++) {
            insertObject = (JSONObject) jsonArray.get(i);
            try {
                this.rewrite(projectId, insertObject, cache, ifAddInfo, metFunSep);
                insertObject.put("building", building);
            } catch (Exception e) {
                errorMsg = String.join("##", errorMsg, insertObject.toString() + " with error : " + e.getMessage());
                continue;
            }
            insertObjects.add(insertObject);
        }
        if (insertObjects.size() == 0) {
            return DpUtils.errorJsonMsg(errorMsg);
        }
        JSONObject exeJson = JSONObject.parseObject(String.format("{\"QueryType\":\"batch_insert\",\"Datatable\":\"%s\"}", table));
        exeJson.put("InsertObjects", insertObjects);
        exeJson.put("Database", OssUtils.getProperty("fjd.database.name"));
        JSONObject re = null;
        try {
            log.info(exeJson.toString());
            re = ZillionAgentProvider.GetAgent().Query(exeJson);
            re.put("ResultMsg", errorMsg);
            return re.toString();
        } catch (Exception e) {
            log.error("", e);
            return DpUtils.errorJsonMsg(Arrays.toString(e.getStackTrace()));
        }
    }

    private void rewrite(String projectId, JSONObject json, Map<String, String> cache, boolean ifAddInfo, String metFunSep) throws Exception {

        if (json.get("id") == null || json.get("code") == null) {
            throw new Exception("json格式不正确.");
        }
        String id = json.get("id").toString();
        String info = (String) json.get("code");
        String metFunStr = cache.get(id + info);
        if (metFunStr == null) {
            metFunStr = (String) CommonUtil.getInfoById(projectId.substring(2, 12), id, info);
        }

        if (metFunStr == null) {
            if (!ifAddInfo) {
                throw new Exception("Assigned code does not exist");
            }
            JSONArray criterias = this.buildInserObj(id, info);
            ArrayList<String> error = ObjectUtil.batchUpdate(projectId, criterias, true, false, true, new CommonQueryCriteria());
            if (error.size() > 0) {
                throw new Exception("Failed to insert assigned code");
            }
            metFunStr = String.join(metFunSep, id + info, "0");
        }
        cache.put(id + info, metFunStr);
        JSONObject criteria = this.generateMetFun(metFunStr, metFunSep);
        json.remove("id");
        json.remove("code");
        criteria.keySet().forEach(key -> {
            json.put(key, criteria.get(key));
        });
    }

    private JSONArray buildInserObj(String id, String info) {
        JSONArray criterias = new JSONArray();
        JSONObject criteria = new JSONObject();
        criteria.put("id", id);
        JSONObject infos = new JSONObject();
        JSONArray values = new JSONArray();
        JSONObject value = new JSONObject();
        value.put("value", String.join("-", id + info, "0"));
        values.add(value);
        infos.put(info, values);
        criteria.put("infos", infos);
        criterias.add(criteria);
        return criterias;
    }


    @Path("/batch_delete")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String delete(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {
        String[] requiredFields = {"receivetime"};
        return bDelete(projectId, jsonStr, "original_month", requiredFields, "-", false);
    }

    @Path("/batch_delete_statinst")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String bDeleteStatinst(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {
        String[] requiredFields = {"time_period", "data_time"};
        return bDeletePeriodData(projectId, jsonStr, "fjd_statinst", requiredFields, false);
    }

    @Path("/batch_delete_period_data")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String bDeletePeriodData(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {
        String[] requiredFields = {"time_period", "data_time"};
        return bDeletePeriodData(projectId, jsonStr, "fjd_0_near", requiredFields, false);
    }

    private String bDeletePeriodData(String projectId, String jsonStr, String table, String[] requiredFields, boolean pjLocalId) {
        JSONObject json = JSONObject.parseObject(jsonStr);
        JSONArray jsonArray = (JSONArray) json.get("criterias");
//        JSONArray criterias = new JSONArray();
        Map<String, JSONArray> period2Arr = new HashMap<>();
        JSONObject criteria = null;
        Map<String, String> cache = new HashMap<>();
        String errorMsg = "";
        
        String building = projectId.substring(2, projectId.length());
        if(pjLocalId) {
        	building = ObjectUtil.getProjLocalID(building);
        }
        
        for (int i = 0; i < jsonArray.size(); i++) {
            criteria = (JSONObject) jsonArray.get(i);
            try {
                /*if(!Arrays.asList(ALLOW_INFOS).contains((String)criteria.get("code"))){
                    throw new Exception("Not support this info-point. ");
                }*/
                for (int j = 0; j < requiredFields.length; j++) {
                    if (criteria.get(requiredFields[j]) == null) {
                        throw new Exception("Must contain: " + Arrays.toString(requiredFields));
                    }
                }
                this.rewrite(projectId, criteria, cache, false, "-");
                criteria.put("building", building);
            } catch (Exception e) {
                errorMsg = String.join("##", criteria.toString() + " with error : " + e.getMessage());
                continue;
            }
            String timePeriod = criteria.getString("time_period");
            criteria.remove("time_period");
            if (period2Arr.get(timePeriod) == null) {
                period2Arr.put(timePeriod, new JSONArray());
            }
            period2Arr.get(timePeriod).add(criteria);
        }
        if (period2Arr.values().stream().mapToInt(arr -> arr.size()).sum() == 0) {
            return DpUtils.errorJsonMsg(errorMsg);
        }
        JSONObject re = null;
        boolean success = false;
        for (String strPeriod : period2Arr.keySet()) {
            if (period2Arr.get(strPeriod).size() == 0) {
                continue;
            }
            JSONObject exeJson = JSONObject.parseObject(String.format("{\"QueryType\":\"batch_delete\",\"Datatable\":\"%s\"}", table + "_" + strPeriod));
            exeJson.put("Criterias", period2Arr.get(strPeriod));
            exeJson.put("Database", OssUtils.getProperty("fjd.database.name"));

            try {
                log.info(exeJson.toString());
                re = ZillionAgentProvider.GetAgent().Query(exeJson);
                success = true;
            } catch (Exception e) {
                log.error("", e);
                errorMsg = errorMsg + "####" + Arrays.toString(e.getStackTrace());
            }
        }
        if (success) {
            re.put("ResultMsg", errorMsg);
            return re.toString();
        } else {
            return DpUtils.errorJsonMsg(errorMsg);
        }

    }

    private String bDelete(String projectId, String jsonStr, String table, String[] requiredFields, String metFunSep, boolean pjLocalId) {
        JSONObject json = JSONObject.parseObject(jsonStr);
        JSONArray jsonArray = (JSONArray) json.get("criterias");
        JSONArray criterias = new JSONArray();
        JSONObject criteria = null;
        Map<String, String> cache = new HashMap<>();
        String errorMsg = "";
        
        String building = projectId.substring(2, projectId.length());
        if(pjLocalId) {
        	building = ObjectUtil.getProjLocalID(building);
        }
        
        for (int i = 0; i < jsonArray.size(); i++) {
            criteria = (JSONObject) jsonArray.get(i);
            try {
                /*if(!Arrays.asList(ALLOW_INFOS).contains((String)criteria.get("code"))){
                    throw new Exception("Not support this info-point. ");
                }*/
                for (int j = 0; j < requiredFields.length; j++) {
                    if (criteria.get(requiredFields[j]) == null) {
                        throw new Exception("Must contain receivetime.");
                    }
                }
                this.rewrite(projectId, criteria, cache, false, metFunSep);
                criteria.put("building", building);
            } catch (Exception e) {
                errorMsg = String.join("##", errorMsg, criteria.toString() + " with error : " + e.getMessage());
                continue;
            }
            criterias.add(criteria);
        }
        if (criterias.size() == 0) {
            return DpUtils.errorJsonMsg(errorMsg);
        }
        JSONObject exeJson = JSONObject.parseObject(String.format("{\"QueryType\":\"batch_delete\",\"Datatable\":\"%s\"}", table));
        exeJson.put("Criterias", criterias);
        exeJson.put("Database", OssUtils.getProperty("fjd.database.name"));
        JSONObject re = null;
        try {
            log.info(exeJson.toString());
            re = ZillionAgentProvider.GetAgent().Query(exeJson);
            re.put("ResultMsg", errorMsg);
            return re.toString();
        } catch (Exception e) {
            log.error("", e);
            return DpUtils.errorJsonMsg(Arrays.toString(e.getStackTrace()));
        }
    }


    @Path("/queryOriginalData")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String queryOriginalData(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {
    	String projectLocalId = ObjectUtil.getProjLocalID(projectId.substring(2, 12));
    	if(null == projectLocalId) {
    		return DpUtils.errorJsonMsg("找不到项目本地编码！");
    	}else {
    		projectLocalId = Const.OBJ_TYPE_PROJECT + projectLocalId;
    	}
    	
        if(OssUtils.getProperty("data.high.frequency") != null && OssUtils.getProperty("data.high.frequency").equalsIgnoreCase("true")){
            JSONObject re = JSONObject.parseObject(queryData(projectId, jsonStr, "original_day", "-", projectLocalId));
            if(re.getJSONArray("Content").size() > 0){
                return re.toString();
            }
        }
        return queryData(projectId, jsonStr, "original_month", "-", projectLocalId);
    }

    @Path("/query_by_obj")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String select(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {
        if(OssUtils.getProperty("data.high.frequency") != null && OssUtils.getProperty("data.high.frequency").equalsIgnoreCase("true")){
            JSONObject re = JSONObject.parseObject(queryData(projectId, jsonStr, "original_day", "-", null));
            if(re.getJSONArray("Content").size() > 0){
                return re.toString();
            }
        }
        return queryData(projectId, jsonStr, "original_month", "-", null);
    }

    public JSONObject generateMetFun(String valueStr, String metFunSep) throws Exception {
        String regex = "";
        if(metFunSep.equalsIgnoreCase("+")){
            regex = "[+]";
        }else {
            regex = metFunSep;
        }
        String[] strs = valueStr.split(regex);
        if (strs.length < 2) {
            throw new Exception(String.format("Assigned code does not has a 'string%slong' style value.", metFunSep));
        }
        if (!strs[strs.length - 1].matches("[0-9]+")) {
            throw new Exception(String.format("Assigned code does not has a 'string%slong' style value.", metFunSep));
        }
        JSONObject criteria = new JSONObject();
        criteria.put("meter", valueStr.replace(metFunSep + strs[strs.length - 1], ""));
        criteria.put("funcid", Long.valueOf(strs[strs.length - 1]));
        return criteria;
    }
    
    @Path("/batchQueryPeriodData")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String batchQueryPeriodDataUseProjectLocalId(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {
        JSONArray cris = JSONObject.parseObject(jsonStr).getJSONArray("criterias");
        log.info("并行请求 query_period_data - start");
        CompletableFuture<String>[] futures = new CompletableFuture[cris.size()];
        for (int i = 0; i < cris.size(); i++) {
            int index = i;
            futures[i] = CompletableFuture.supplyAsync(() -> this.queryPeriodDataUseProjectLocalId(projectId, new JSONObject().fluentPut("criteria", cris.get(index)).toString()));
        }
        CompletableFuture.allOf(futures).join();
        
        log.info("并行请求 query_period_data - end");
        JSONArray arr = new JSONArray();
        for (int i = 0; i < cris.size(); i++) {
            try {
                arr.add(JSONObject.parseObject(futures[i].get()).fluentPut("id", cris.getJSONObject(i).get("id")).fluentPut("code", cris.getJSONObject(i).get("code")));
            } catch (Exception e) {
                log.error("",e);
            }
        }
        return new JSONObject().fluentPut("Content", arr).fluentPut("Count", arr.size()).toString();
    }

    @Path("/queryPeriodData")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String queryPeriodDataUseProjectLocalId(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {
        try {
            JSONObject json = JSONObject.parseObject(jsonStr);
            if (json == null)
                return DpUtils.errorJsonMsg("json格式不正确.");
            json = (JSONObject) json.get("criteria");
            if (json == null || json.get("id") == null || json.get("code") == null || json.get("period") == null || json.get("receivetime") == null)
                return DpUtils.errorJsonMsg("json格式不正确.");

            String projectLocalId = ObjectUtil.getProjLocalID(projectId.substring(2, 12));;
            if(null == projectLocalId) {
            	 return DpUtils.errorJsonMsg("找不到项目本地编码");
           	}else {
           		projectLocalId = Const.OBJ_TYPE_PROJECT + projectLocalId;
           	}
            
            String id = json.get("id").toString();
            JSONObject criteria = new JSONObject();
            criteria.put("criteria", new JSONObject());
            ((JSONObject) criteria.get("criteria")).put("id", id);
            Object metFunStr = CommonUtil.getInfoById(projectId.substring(2, 12), id, (String) json.get("code"));
            if (metFunStr == null) {
                return DpUtils.errorJsonMsg("Assigned code does not exist");
            }
            
            if (metFunStr instanceof JSONObject) {
                JSONObject calcJSON = (JSONObject) metFunStr;
                if (calcJSON.get(Const.EXPRESSION) == null || calcJSON.get(Const.VARIABLES) == null || !(calcJSON.get(Const.VARIABLES) instanceof JSONObject))
                    return DpUtils.errorJsonMsg("信息点值错误!");
             
                // 点位表达式，如(a+b+c)/2
                String expression = calcJSON.get(Const.EXPRESSION).toString();

                // 读expression, 返回变量
                Calculator calc = new Calculator();
                // 解读到表达式中的变量, 如[a,b,c] 
                String[] vars = calc.readExpression(calcJSON.get(Const.EXPRESSION).toString());
                // 表达式是否为求均值
                boolean computeAvg = calc.ifAvgExp(expression);
                
                // 匹配变量是否存在.
                for (int i = 0; i < vars.length; ++i)
                    if (!((JSONObject) calcJSON.get(Const.VARIABLES)).containsKey(vars[i]))
                        throw new Exception("变量中不存在" + vars[i]);
                
                LinkedHashMap<String, LinkedHashMap<String, Double>> varValue = new LinkedHashMap<>();
                // 获取各个变量的值.
                LinkedHashMap<String, Double> lastMap = null;
                
                // 没有值得变量 ["a"]
                Set<String> varNoValue = new HashSet<>();
                
                
                for (int i = 0; i < vars.length; ++i) {
                	// 表达式因数对应的点位信息，如：{"meterfunc":"1071-166"}
                    JSONObject var = (JSONObject) ((JSONObject) calcJSON.get(Const.VARIABLES)).get(vars[i]);
                    if (var == null || ((var.get("id") == null || var.get("code") == null) && var.getString("meterfunc") == null))
                        throw new Exception("变量" + vars[i] + "的值配置错误");
                   
                    // 获取点位数据
                    // [{"data_flag":0,"data_time":"20200512154500","meter":"1071","data_value":26.5,"funcid":166,"building":"1101080253","time_period":"15min"}]
                    JSONArray arr = getMeterFuncPeriodData(projectId, var, json, projectLocalId);
                    if (arr == null || arr.size() == 0) {
                    	// 若为求均值，并且表达式中所有变量对应点位都没值，返回无值
                    	if(computeAvg && varNoValue.size() == (vars.length-1)) {
                    		return DpUtils.returnJSONArray(new JSONArray(), "");
                    	}
                    	varNoValue.add(vars[i]);
                    	continue;
                    }
                    
                    // 解析点位 时间-值信息 {"20200512154500":26.5}
                    LinkedHashMap<String, Double> timeValueMap = new LinkedHashMap<>();
                    for (int j = 0; j < arr.size(); ++j) {
                        JSONObject obj = (JSONObject) arr.get(j);
                        if (lastMap == null) {
                            timeValueMap.put(obj.get("data_time").toString(), (Double) obj.get("data_value"));
                        } else {
                            if (lastMap.containsKey(obj.get("data_time").toString())) {
                                timeValueMap.put(obj.get("data_time").toString(), (Double) obj.get("data_value"));
                            }
                        }
                    }
                    
                    lastMap = timeValueMap;
                    if (timeValueMap.size() == 0)
                        return DpUtils.returnJSONArray(new JSONArray(), "");
                    
                    varValue.put(vars[i], timeValueMap);
                }
                
                // 依次匹配相同时间点, 计算, 添加到return里并返回.
                JSONArray result = new JSONArray();
                
                if(computeAvg) {
                	// 对于求均值的表达式进行特殊处理：如果表达式中某一个因数没值则忽略该因数，求有值得因数的均值
                	
	                for (String time : lastMap.keySet()) {
	                    JSONObject jsonObject = new JSONObject();
	                    jsonObject.put("time_period", json.get("period"));
	                    jsonObject.put("data_time", time);
	                    result.add(jsonObject);
	                    
	                    ArrayList<Double> values = new ArrayList<>();
	                    // 有值的因数
	                    List<String> varHaveValue = new ArrayList<>();
	                    
	                    for (int i = 0; i < vars.length; ++i) {
	                        Map<String, Double> singleVar = varValue.get(vars[i]);
	                        if(null != singleVar && singleVar.get(time) != null) {
	                        	varHaveValue.add(vars[i]);
	                        	values.add(singleVar.get(time));
	                        }
	                    }
	                    
	                    Double re = null;
	                    if(varHaveValue.size() == vars.length) {
	                    	calc.readExpression(expression);
	                    	re = calc.compute(vars, values);
	                    }else {
	                    	// 重构表达式
	                    	String newExpression = "(";
	                    	String[] varsAray = new String[varHaveValue.size()];
	                    	for(int i=0; i<varHaveValue.size(); i++) {
	                    		varsAray[i] = varHaveValue.get(i);
	                    		newExpression += varHaveValue.get(i) + "+";
	                    	}
	                    	newExpression = newExpression.substring(0, newExpression.length()-1) + ")/" + varHaveValue.size();
	                    	calc.readExpression(newExpression);
	                    	re = calc.compute(varsAray, values);
	                    }
	                    
	                    jsonObject.put("data_value", re);
	                }
                }else {
                	for (String time : lastMap.keySet()) {
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put("time_period", json.get("period"));
                        jsonObject.put("data_time", time);
                        result.add(jsonObject);
                        ArrayList<Double> values = new ArrayList<>();
                        for (int i = 0; i < vars.length; ++i) {
                            Map<String, Double> singleVar = varValue.get(vars[i]);
                            values.add(singleVar.get(time));
                        }
                        Double re = calc.compute(vars, values);
                        jsonObject.put("data_value", re);
                    }
                }
                return DpUtils.returnJSONArray(result, "");
            } else if (metFunStr instanceof String)
                return this.innerQueryPeriodData(projectId, metFunStr.toString(), json, projectLocalId).toString();
            else
                return DpUtils.errorJsonMsg("信息点值有错误!");
        } catch (ExpressionException eex) {
            log.error(eex.getMessage(), eex);
            return DpUtils.errorJsonMsg(eex.getMessage());
        } catch (DepthExceededException deex) {
            log.error(deex.getMessage(), deex);
            return DpUtils.errorJsonMsg(deex.getMessage());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return DpUtils.errorJsonMsg(e.getMessage() + " : " + Arrays.toString(e.getStackTrace()));
        }
    }

    @Path("/query_period_data")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String queryPeriodData(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {
        try {
            JSONObject json = JSONObject.parseObject(jsonStr);
            if (json == null)
                return DpUtils.errorJsonMsg("json格式不正确.");
            json = (JSONObject) json.get("criteria");
            if (json == null || json.get("id") == null || json.get("code") == null || json.get("period") == null || json.get("receivetime") == null)
                return DpUtils.errorJsonMsg("json格式不正确.");

            String id = json.get("id").toString();
            JSONObject criteria = new JSONObject();
            criteria.put("criteria", new JSONObject());
            ((JSONObject) criteria.get("criteria")).put("id", id);
            Object metFunStr = CommonUtil.getInfoById(projectId.substring(2, 12), id, (String) json.get("code"));
            if (metFunStr == null) {
                return DpUtils.errorJsonMsg("Assigned code does not exist");
            }
            if (metFunStr instanceof JSONObject) {
                JSONObject calcJSON = (JSONObject) metFunStr;
                if (calcJSON.get(Const.EXPRESSION) == null || calcJSON.get(Const.VARIABLES) == null || !(calcJSON.get(Const.VARIABLES) instanceof JSONObject))
                    return DpUtils.errorJsonMsg("信息点值错误!");
             
                // 点位表达式，如(a+b+c)/2
                String expression = calcJSON.get(Const.EXPRESSION).toString();

                // 读expression, 返回变量
                Calculator calc = new Calculator();
                // 解读到表达式中的变量, 如[a,b,c] 
                String[] vars = calc.readExpression(calcJSON.get(Const.EXPRESSION).toString());
                // 表达式是否为求均值
                boolean computeAvg = calc.ifAvgExp(expression);
                
                // 匹配变量是否存在.
                for (int i = 0; i < vars.length; ++i)
                    if (!((JSONObject) calcJSON.get(Const.VARIABLES)).containsKey(vars[i]))
                        throw new Exception("变量中不存在" + vars[i]);
                
                LinkedHashMap<String, LinkedHashMap<String, Double>> varValue = new LinkedHashMap<>();
                // 获取各个变量的值.
                LinkedHashMap<String, Double> lastMap = null;
                
                // 没有值得变量 ["a"]
                Set<String> varNoValue = new HashSet<>();
                
                for (int i = 0; i < vars.length; ++i) {
                	// 表达式因数对应的点位信息，如：{"meterfunc":"1071-166"}
                    JSONObject var = (JSONObject) ((JSONObject) calcJSON.get(Const.VARIABLES)).get(vars[i]);
                    if (var == null || ((var.get("id") == null || var.get("code") == null) && var.getString("meterfunc") == null))
                        throw new Exception("变量" + vars[i] + "的值配置错误");
                   
                    // 获取点位数据
                    // [{"data_flag":0,"data_time":"20200512154500","meter":"1071","data_value":26.5,"funcid":166,"building":"1101080253","time_period":"15min"}]
                    JSONArray arr = getMeterFuncPeriodData(projectId, var, json, null);
                    if (arr == null || arr.size() == 0) {
                    	// 若为求均值，并且表达式中所有变量对应点位都没值，返回无值
                    	if(computeAvg && varNoValue.size() == (vars.length-1)) {
                    		return DpUtils.returnJSONArray(new JSONArray(), "");
                    	}
                    	varNoValue.add(vars[i]);
                    	continue;
                    }
                    
                    // 解析点位 时间-值信息 {"20200512154500":26.5}
                    LinkedHashMap<String, Double> timeValueMap = new LinkedHashMap<>();
                    for (int j = 0; j < arr.size(); ++j) {
                        JSONObject obj = (JSONObject) arr.get(j);
                        if (lastMap == null) {
                            timeValueMap.put(obj.get("data_time").toString(), (Double) obj.get("data_value"));
                        } else {
                            if (lastMap.containsKey(obj.get("data_time").toString())) {
                                timeValueMap.put(obj.get("data_time").toString(), (Double) obj.get("data_value"));
                            }
                        }
                    }
                    
                    lastMap = timeValueMap;
                    if (timeValueMap.size() == 0)
                        return DpUtils.returnJSONArray(new JSONArray(), "");
                    
                    varValue.put(vars[i], timeValueMap);
                }
                
                // 依次匹配相同时间点, 计算, 添加到return里并返回.
                JSONArray result = new JSONArray();
                
                if(computeAvg) {
                	// 对于求均值的表达式进行特殊处理：如果表达式中某一个因数没值则忽略该因数，求有值得因数的均值
                	
	                for (String time : lastMap.keySet()) {
	                    JSONObject jsonObject = new JSONObject();
	                    jsonObject.put("time_period", json.get("period"));
	                    jsonObject.put("data_time", time);
	                    result.add(jsonObject);
	                    
	                    ArrayList<Double> values = new ArrayList<>();
	                    // 有值的因数
	                    List<String> varHaveValue = new ArrayList<>();
	                    
	                    for (int i = 0; i < vars.length; ++i) {
	                        Map<String, Double> singleVar = varValue.get(vars[i]);
	                        if(null != singleVar && singleVar.get(time) != null) {
	                        	varHaveValue.add(vars[i]);
	                        	values.add(singleVar.get(time));
	                        }
	                    }
	                    
	                    Double re = null;
	                    if(varHaveValue.size() == vars.length) {
	                    	calc.readExpression(expression);
	                    	re = calc.compute(vars, values);
	                    }else {
	                    	// 重构表达式
	                    	String newExpression = "(";
	                    	String[] varsAray = new String[varHaveValue.size()];
	                    	for(int i=0; i<varHaveValue.size(); i++) {
	                    		varsAray[i] = varHaveValue.get(i);
	                    		newExpression += varHaveValue.get(i) + "+";
	                    	}
	                    	newExpression = newExpression.substring(0, newExpression.length()-1) + ")/" + varHaveValue.size();
	                    	calc.readExpression(newExpression);
	                    	re = calc.compute(varsAray, values);
	                    }
	                    
	                    jsonObject.put("data_value", re);
	                }
                }else {
                	for (String time : lastMap.keySet()) {
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put("time_period", json.get("period"));
                        jsonObject.put("data_time", time);
                        result.add(jsonObject);
                        ArrayList<Double> values = new ArrayList<>();
                        for (int i = 0; i < vars.length; ++i) {
                            Map<String, Double> singleVar = varValue.get(vars[i]);
                            values.add(singleVar.get(time));
                        }
                        Double re = calc.compute(vars, values);
                        jsonObject.put("data_value", re);
                    }
                }
                return DpUtils.returnJSONArray(result, "");
            } else if (metFunStr instanceof String)
                return this.innerQueryPeriodData(projectId, metFunStr.toString(), json, null).toString();
            else
                return DpUtils.errorJsonMsg("信息点值有错误!");
        } catch (ExpressionException eex) {
            log.error(eex.getMessage(), eex);
            return DpUtils.errorJsonMsg(eex.getMessage());
        } catch (DepthExceededException deex) {
            log.error(deex.getMessage(), deex);
            return DpUtils.errorJsonMsg(deex.getMessage());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return DpUtils.errorJsonMsg(e.getMessage() + " : " + Arrays.toString(e.getStackTrace()));
        }
    }

    @Path("/batch_query_period_data")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String bQueryPeriodData(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {
        JSONArray cris = JSONObject.parseObject(jsonStr).getJSONArray("criterias");
        log.info("并行请求 query_period_data - start");
        CompletableFuture<String>[] futures = new CompletableFuture[cris.size()];
        for (int i = 0; i < cris.size(); i++) {
            int index = i;
            futures[i] = CompletableFuture.supplyAsync(() -> this.queryPeriodData(projectId, new JSONObject().fluentPut("criteria", cris.get(index)).toString()));
        }
        CompletableFuture.allOf(futures).join();
        
        log.info("并行请求 query_period_data - end");
        JSONArray arr = new JSONArray();
        for (int i = 0; i < cris.size(); i++) {
            try {
                arr.add(JSONObject.parseObject(futures[i].get()).fluentPut("id", cris.getJSONObject(i).get("id")).fluentPut("code", cris.getJSONObject(i).get("code")));
            } catch (Exception e) {
                log.error("",e);
            }
        }
        return new JSONObject().fluentPut("Content", arr).fluentPut("Count", arr.size()).toString();
    }


    private JSONArray getMeterFuncPeriodData(String pjId, JSONObject var, JSONObject json, String pjLocalId) throws Exception {
        Object metFunStr = null;
        String id = var.getString("id");
        String code = var.getString("code");
        if (var.getString("meterfunc") != null && var.getString("meterfunc").length() > 0) {
            metFunStr = var.getString("meterfunc");
        } else {
            metFunStr = CommonUtil.getInfoById(pjId.substring(2, 12), id, code);
        }
        if (metFunStr == null) {
            throw new Exception("信息点不存在:" + id + ":" + code);
        }
        if (metFunStr instanceof JSONObject)
            throw new DepthExceededException();
        if (!(metFunStr instanceof String))
            throw new Exception("数据错误:" + id + ":" + code);
        
        String projectCode = pjId;
        if(pjLocalId != null) {
        	projectCode = pjLocalId;
        }
        
        return (JSONArray) this.innerQueryPeriodData(projectCode, metFunStr.toString(), json, pjLocalId).get("Content");
    }

    @Path("/batch_query_period_data_by_metfun")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String bQueryPeriodDataByMetfun(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) throws Exception {
        JSONObject jsonBody = JSONObject.parseObject(jsonStr);
        JSONArray arr = jsonBody.getJSONArray("criterias");
        if (arr == null) {
            return DpUtils.errorJsonMsg("json格式不正确.");
        }
        JSONObject reJson = new JSONObject();
        for (int i = 0; i < arr.size(); i++) {
            JSONObject cri = arr.getJSONObject(i);
            String metFun = cri.getString("met_fun");
            cri.remove("met_fun");
            reJson.put(metFun, this.innerQueryPeriodData(projectId, metFun, cri, null));
        }
        return reJson.toString();
    }

    private JSONObject innerQueryPeriodData(String projectId, String metFunStr, JSONObject json, String pjLocalId) throws Exception {
        JSONObject queryData = new JSONObject();

        JSONObject newCriteria = this.generateMetFun(metFunStr, "-");
        String period = json.get("period").toString().trim();
        Boolean interpolation = json.getBoolean("interpolation");
        if(interpolation == null){
            interpolation = true;
        }
        int stepLength = this.rewritePeriod(projectId, period, newCriteria, interpolation);
        newCriteria.put("data_time", json.get("receivetime"));

        String tableSuffix = newCriteria.getString("time_period");
        newCriteria.remove("time_period");

        queryData.put("criteria", newCriteria);

        TreeMap<String, JSONObject> sortedMap = new TreeMap();
        
        if(null != pjLocalId) {
        	projectId = pjLocalId;
        }
        
        JSONObject jsonObject2 = CommonMethods.queryData(projectId, queryData.toJSONString(), "fjd_2_near_" + tableSuffix);
        JSONObject jsonObject1 = CommonMethods.queryData(projectId, queryData.toJSONString(), "fjd_1_near_" + tableSuffix);
        JSONObject jsonObject0 = CommonMethods.queryData(projectId, queryData.toJSONString(), "fjd_0_near_" + tableSuffix);
        JSONObject[] jsonObjects = {jsonObject0, jsonObject1, jsonObject2};
        Arrays.stream(jsonObjects).forEach(reJson -> {
            JSONArray arr = (JSONArray) reJson.get("Content");
            if (arr == null) {
                return;
            }
            for (int i = 0; i < arr.size(); i++) {
                JSONObject item = (JSONObject) arr.get(i);
                item.put("time_period", tableSuffix);
                sortedMap.put(item.get("data_time").toString(), item);
            }
        });

        JSONObject result = new JSONObject();
        JSONArray arr = new JSONArray();
        sortedMap.descendingKeySet().stream().forEach(key -> arr.add(sortedMap.get(key)));
        result.put("Result", "success");
        result.put("Count", (long) arr.size());
        result.put("Content", arr);
        result.put("ResultMsg", "");
        if (stepLength < 1) {
            return result;
        } else {
            return this.interpolate(stepLength, result);
        }

    }

    private JSONObject interpolate(int stepLength, JSONObject rawRe) {
        JSONArray content = (JSONArray) rawRe.get("Content");
        if (content == null || content.size() < 2) {
            return rawRe;
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
        Map<LocalDateTime, JSONObject> map = new TreeMap<>();
        JSONObject tmpItem = null;
        for (int i = 0; i < content.size(); i++) {
            tmpItem = (JSONObject) content.get(i);
            map.put(LocalDateTime.parse(tmpItem.get("data_time").toString(), formatter), tmpItem);
        }
        int rawStepLenth = Integer.parseInt(tmpItem.get("time_period").toString().replace("min", ""));
        List<LocalDateTime> timeArr = new ArrayList<>(map.keySet());
        JSONArray array = new JSONArray();
        PiecewiseCubicHermiteSplineInterpolator pchip = new PiecewiseCubicHermiteSplineInterpolator();
        List<Double> X = new ArrayList<>();
        List<Double> Y = new ArrayList<>();
        int start = -1;
        for (int i = 0; i < timeArr.size(); i++) {
            if (i == 0) {
                start = 0;
                X.add((double) timeArr.get(i).toInstant(ZoneOffset.ofTotalSeconds(0)).toEpochMilli());
                Y.add(map.get(timeArr.get(i)).getDouble("data_value"));
                continue;
            }
            if (timeArr.get(i).minusMinutes(rawStepLenth * 2).compareTo(timeArr.get(i - 1)) > 0 || i == timeArr.size() - 1) {
                if (timeArr.get(i).minusMinutes(rawStepLenth * 2).compareTo(timeArr.get(i - 1)) < 0 && i == timeArr.size() - 1) {
                    X.add((double) timeArr.get(i).toInstant(ZoneOffset.ofTotalSeconds(0)).toEpochMilli());
                    Y.add(map.get(timeArr.get(i)).getDouble("data_value"));
                }
                LocalDateTime tmpDateTime = timeArr.get(start);
                if (X.size() == 1) {
                    array.add(map.get(tmpDateTime));
                }
                while (true) {
                    if (tmpDateTime.compareTo(timeArr.get(i)) > 0) {
                        break;
                    }
                    if (map.get(tmpDateTime) != null && !array.contains(map.get(tmpDateTime))) {
                        array.add(map.get(tmpDateTime));
                    } else {
                        double value = pchip.interpolate(ArrayUtils.toPrimitive(X.toArray(new Double[X.size()])), ArrayUtils.toPrimitive(Y.toArray(new Double[Y.size()])), (double) tmpDateTime.toInstant(ZoneOffset.ofTotalSeconds(0)).toEpochMilli());
                        JSONObject jsonObject = JSONObject.parseObject(tmpItem.toString());
                        jsonObject.put("data_time", tmpDateTime.format(formatter));
                        jsonObject.put("data_value", value);
                        jsonObject.put("time_period", String.format("%dmin", stepLength));
                        array.add(jsonObject);
                    }
                    tmpDateTime = tmpDateTime.plusMinutes(stepLength);
                }
                X.clear();
                Y.clear();
                start = i;
                if (timeArr.get(i).minusMinutes(rawStepLenth * 2).compareTo(timeArr.get(i - 1)) > -1) {
                    X.add((double) timeArr.get(i).toInstant(ZoneOffset.ofTotalSeconds(0)).toEpochMilli());
                    Y.add(map.get(timeArr.get(i)).getDouble("data_value"));
                }
            } else {
                X.add((double) timeArr.get(i).toInstant(ZoneOffset.ofTotalSeconds(0)).toEpochMilli());
                Y.add(map.get(timeArr.get(i)).getDouble("data_value"));
            }

        }
        rawRe.put("Content", array);
        rawRe.put("Count", (long) array.size());
        return rawRe;
    }

    private int rewritePeriod(String projectId, String period, JSONObject criteria, boolean interpolation) {
        if (!Arrays.asList(PERIODS).contains(period) || !interpolation) {
            criteria.put("time_period", period);
            return -1;
        }
        JSONObject queryData = new JSONObject();
        queryData.put("criteria", criteria);
        log.info(queryData.toJSONString());
        JSONObject result = CommonMethods.queryData(projectId, queryData.toJSONString(), "dy_pointlist");
        JSONArray contents = (JSONArray) result.get("Content");
        if (contents == null || contents.size() == 0) {
            criteria.put("time_period", period);
            return -1;
        }
        String minPeriod = ((JSONObject) contents.get(0)).get("collect_cycle").toString();
        try {
            int min = Integer.parseInt(minPeriod.trim().replace("min", ""));
            int intPeriod = Integer.parseInt(period.trim().replace("min", ""));
            if (min > intPeriod) {
                criteria.put("time_period", minPeriod);
                return intPeriod;
            }
        } catch (NumberFormatException e) {
            log.error("collect_cycle : " + minPeriod, e);
        }
        criteria.put("time_period", period);
        return -1;


    }


    @Path("/query")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String query(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {
//        accountId = "0";               //需要修改
        if(OssUtils.getProperty("data.high.frequency") != null && OssUtils.getProperty("data.high.frequency").equalsIgnoreCase("true")){
            JSONObject re = CommonMethods.queryData(projectId, jsonStr, "original_day");
            if(re.getJSONArray("Content").size() > 0){
                return re.toString();
            }
        }
        return CommonMethods.queryData(projectId, jsonStr, "original_month").toString();
    }


    @Path("/query_meta_info")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String queryMetaInfo(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {
//        accountId = "0";               //需要修改
        JSONObject queryData = new JSONObject();
        try {
            JSONObject json = JSONObject.parseObject(jsonStr);
            json = (JSONObject) json.get("criteria");
            if (json.get("id") == null || json.get("code") == null)
                throw new Exception("json格式不正确.");

            String id = json.get("id").toString();
            JSONObject criteria = new JSONObject();//(JSONObject) JSONValue.parse("{\"criteria\":{}}");JSONObject insideCriteria = (JSONObject)criteria.get("criteria");
            criteria.put("criteria", new JSONObject());
            ((JSONObject) criteria.get("criteria")).put("id", id);
//			JSONObject jsonData = CommonMethods.getJsondataById(projectId, id);
//			if(jsonData == null || !jsonData.containsKey(json.get("code")))
//				return DpUtils.errorJsonMsg("Assigned code does not exist");
//
//			JSONObject newCriteria = ((JSONObject)jsonData.get(json.get("code")));

            String metFunStr = (String) CommonUtil.getInfoById(projectId.substring(2, 12), id, (String) json.get("code"));
            if (metFunStr == null) {
                return DpUtils.errorJsonMsg("Assigned code does not exist");
            }
            JSONObject newCriteria = this.generateMetFun(metFunStr, "-");
            //newCriteria.put("building", projectId.substring(2));

            queryData.put("criteria", newCriteria);
            return CommonMethods.queryData(projectId, queryData.toJSONString(), "dy_pointlist").toString();
        } catch (Exception ex) {
            ex.printStackTrace();
            return DpUtils.errorJsonMsg(Arrays.toString(ex.getStackTrace()));
        }
    }

    @Path("/string/batch_insert")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String bInsertStrOriginal(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {
        return this.bInsert(projectId, jsonStr, "original_str_data", false, "+", false);

    }

    @Path("/string/batch_delete")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String deleteStrOriginal(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {
        String[] requiredFields = {"receivetime"};
        return bDelete(projectId, jsonStr, "original_str_data", requiredFields, "+", false);
    }

    @Path("/string/query_by_obj")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String selectStrData(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr) {
        return queryData(projectId, jsonStr, "original_str_data", "+", null);
    }
}
