package com.sagacloud.endpoints;

import com.alibaba.fastjson.*;
import com.sagacloud.dict.EqStaticInfo;
import com.sagacloud.dict.InfosUtil;
import com.sagacloud.endpoints.item.PhysicalObject;
import com.sagacloud.utils.*;
import com.sagacloud.utils.jsonHelper.SubsetEntity;
import com.sagacloud.utils.jsonHelper.infoPoint.InfoPoint;
import com.sagacloud.utils.objectLog.ActivityLogObject;
import com.sagacloud.utils.objectLog.ActivityLogRunnable;
import com.sagacloud.utils.objectLog.ActivityLogUtil;
import com.sagacloud.utils.objects.CommonQueryCriteria;
import com.sagacloud.utils.objects.ObjectIdName;
import com.sagacloud.utils.objects.ObjectType;
import com.sagacloud.utils.qrCode.QRCodeObj;
import com.sagacloud.utils.qrCode.QRCodeRunnable;
import com.sagacloud.utils.relationUtils.SubsetUtil;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.geom.impl.CoordinateArraySequence;

import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.container.ResourceContext;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import java.util.*;


/**
 * Created by wushengzong on 2017/4/18.
 */
@Path("space")
public class Space {
    @Context
    private ResourceContext resourceContext;

    @Path("/batch/create_update")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String createUpdate(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr){
    	final String repeat_id = "repeat_id";
    	
        JSONObject postParam = JSONObject.parseObject(jsonStr);
        JSONObject postBody = JSONObject.parseObject(jsonStr);
        // 先批量插入
        String insertBatchResult = batchCreate(projectId, postParam);
        
        JSONObject insetResultBody = JSONObject.parseObject(insertBatchResult);
        JSONArray errorAray = insetResultBody.getJSONArray("Error");
        if(null != errorAray) {
        	insetResultBody.put("error_insert", errorAray);
        	insetResultBody.remove("Error");
        }
        
        // 含有已存在的对象，进行更新操作
        if(insertBatchResult.contains(repeat_id)) {
        	// 已存在对象的id集合
        	Set<String> repeatIdSet = new HashSet<>();
        	JSONArray errorNewAray = new JSONArray();
        	if(null != errorAray) {
        		for(int i=0; i<errorAray.size(); i++) {
        			if(null != errorAray.getJSONObject(i) && null != errorAray.getJSONObject(i).getString(repeat_id)) {
        				repeatIdSet.add(errorAray.getJSONObject(i).getString(repeat_id));
        			}else {
        				errorNewAray.add(errorAray.getJSONObject(i));
        			}
        		}
         	}
        	
        	if(!errorNewAray.isEmpty()) {
        		insetResultBody.put("error_insert", errorNewAray);
        	}else {
        		insetResultBody.remove("error_insert");
        		insetResultBody.put("Result", "success");
        	}
        	
        	if(!repeatIdSet.isEmpty()) {
        		insertBatchResult = ObjectUtil.insertParamToExcuteUpdate(projectId, postBody, insetResultBody, repeatIdSet);
        	}
        }
        
        return insertBatchResult;
    }
    
    @Path("/batch/create")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String batchCreate(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr){
        JSONObject postBody = JSONObject.parseObject(jsonStr);
        return batchCreate(projectId, postBody);
    }

    public String batchCreate(@QueryParam(Const.PROJECT_ID) String projectId, JSONObject postBody){
        try{
            String imageUrl = postBody.getString("innerImageUrl");
            Boolean genPersagyQr = postBody.getBoolean("genPersagyQr");
            if(genPersagyQr == null){
                genPersagyQr = false;
            }
            JSONArray criterias = postBody.getJSONArray("criterias");
            if(criterias == null || criterias.size() == 0){
                return DpUtils.returnJSONArray(new JSONArray(), "");
            }
            JSONArray errors = new JSONArray();
            String pjId = projectId.substring(2);
//            一. 准备数据
//            1. 为每个对象生成id, 或者检验指定的id符不符合规则, 检验是否有重复指定的id.
//            2. 统计  设备/系统/部件/业务空间/元空间 类型, 获取每个类型下的信息点列表. (剔除错误类型)
            Set<String> typeSet = JSONCheckUtil.checkSpGenerateId(pjId, criterias, errors);
            //检查不同业务空间的信息点
            String[] typeArr = new String[typeSet.size()];
            typeSet.toArray(typeArr);
            HashMap<String, HashMap<String, InfoPoint>> infoCodeMap = InfoCodeUtil.getWholeInfosWithCustomMap(pjId, typeArr);
//            3. 通过2获取到的信息点列表, 验证信息点的key是否正确(剔除错误)
//            4. 验证信息点节点的格式是否正确.
            JSONCheckUtil.verifySpaceInfos(projectId, criterias, infoCodeMap, errors);
//            5. 统计  building_id, floor_id(仅设备允许), (跳过关系id错误的关系).
            Map<String, String> buildingMap = new HashMap<>();  // id --> building_id
            Map<String, String> floorMap = new HashMap<>();     // id --> floor_id
            JSONCheckUtil.countEquipRelation(pjId, buildingMap, floorMap, null, criterias, errors); // 填充上面两个Map
//            6. 查看是否有二维码信息点EquipQRCode, 有的话不再插入二维码, 没有的话, 添加二维码信息点, 同时记录id(用于接口返回后添加二维码图片).
            Map<String, String> idFileKeyMap = new HashMap<>();
            JSONCheckUtil.addQRCodeInfo(projectId, criterias, "RoomQRCode", idFileKeyMap);
//            二. 插入数据
//            1. 插入对象表 (将失败的剔除)
            if(criterias.size() == 0)
                return PropertyUtil.returnOnCreation(criterias, errors, "");
            String time = CommonUtil.getTime();
            DpUtils.submitToObjectTable(pjId, criterias, time, errors);
            // 补充信息点数据
            ArrayList<ActivityLogObject> logList = new ArrayList<>();
            for(int i = 0; i < criterias.size(); ++i) {
                JSONObject single = criterias.getJSONObject(i);
                String id = single.getString("id");
                ObjectType ot = ObjectType.space;
                String detailedType = single.getString(Const.BUSINESS_TYPE);
                InfosUtil.addIdName(pjId, ot, detailedType, single.getJSONObject("infos"), id, new ObjectIdName());
                // 添加要发送到activeMQ的消息
                ActivityLogUtil.addMessageToQueue(pjId, time, logList, id, ot);
            }
            JSONArray success = new JSONArray();
            PropertyUtil.putIDToInfos(criterias, success);

//            2. 插入关系
            try {
                List<SubsetEntity> relationList = new ArrayList<>();
                Set<String> relationIdSet = new HashSet<>();
                relationIdSet.addAll(buildingMap.values());
                relationIdSet.addAll(floorMap.values());
                ObjectUtil.filterValid(pjId, relationIdSet, true, null);
                for(int i = 0; i < criterias.size(); ++i){
                    JSONObject single = criterias.getJSONObject(i);
                    String id = single.getString("id");
                    if(floorMap.get(id) != null && relationIdSet.contains(floorMap.get(id))){
                        SubsetEntity se = new SubsetEntity(floorMap.get(id), id);
                        relationList.add(se);
                    }else if(buildingMap.get(id) != null && relationIdSet.contains(buildingMap.get(id))){
                        SubsetEntity se = new SubsetEntity(buildingMap.get(id), id);
                        relationList.add(se);
                    }
                }
                SubsetUtil.modify(pjId, relationList);
            }catch (Exception ignore){}
//            3. 插入信息点
            ObjectUtil.batchUpdate(pjId, criterias, false, true, false, new CommonQueryCriteria());
//            三. 接口返回后
//            1. 发送activeMQ消息, 记录日志
            ActivityLogRunnable activityLog = new ActivityLogRunnable(logList);
            ActivityLogUtil.pool.submit(activityLog);
//            2. 生成二维码文件, 上传到文件服务器
            QRCodeRunnable qrCodeRunnable = new QRCodeRunnable(idFileKeyMap, imageUrl, projectId, genPersagyQr);
            ActivityLogUtil.pool.submit(qrCodeRunnable);
            return PropertyUtil.returnOnCreation(success, errors, "");
        }catch (Exception ex){
            ex.printStackTrace();
            return DpUtils.errorJsonMsg(ex.getMessage() == null? "添加失败!": ex.getMessage());
        }
    }
    @Path("/create")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String create(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr){
        JSONObject obj = JSONObject.parseObject(jsonStr);
        JSONObject postBody = new JSONObject();
        JSONArray criterias = new JSONArray();
        criterias.add(obj);
        postBody.put("criterias", criterias);
        String result = batchCreate(projectId, postBody);
        JSONObject criteria = JSONObject.parseObject(result);
        return DpUtils.batchToSingle(criteria);
    }

//    旧创建业务空间接口. 无添加二维码
//    @Path("/create")
//    @POST
//    @Produces(MediaType.APPLICATION_JSON)
//    public String create(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr){
//        try {
//            JSONObject insertJson = JSONObject.parseObject(jsonStr);
//            if(!insertJson.containsKey(Const.JSON_FIELD_JSON_DATA_INFOS)){
//                return DpUtils.errorJsonMsg("缺少必要字段");
//            }
//            String floor_id = null;
//            String building_id = null;
//            if(insertJson.get("floor_id") != null){
//                floor_id = insertJson.get("floor_id").toString();
//                // check if floor exist
//                JSONObject checkCriteria = new JSONObject();
//                checkCriteria.put(ObjectTable.KEY_PJ_ID, projectId.substring(2));
//                checkCriteria.put(ObjectTable.KEY_OBJ_TYPE, Const.OBJ_TYPE_FLOOR);
//                checkCriteria.put(ObjectTable.KEY_OBJ_ID, floor_id.substring(12));
//                checkCriteria.put(Const.TABLE_KEY_NAME_VALID, true);
//                if(CommonUtil.getCount(HBaseTable.objects, checkCriteria) <= 0)
//                    throw new Exception("楼层不存在.");
//                // check end.
//            }else if(insertJson.get("building_id") != null){
//                building_id = insertJson.get("building_id").toString();
//                // check if building exist
//                JSONObject checkCriteria = new JSONObject();
//                checkCriteria.put(ObjectTable.KEY_PJ_ID, projectId.substring(2));
//                checkCriteria.put(ObjectTable.KEY_OBJ_TYPE, Const.OBJ_TYPE_BUILDING);
//                checkCriteria.put(ObjectTable.KEY_OBJ_ID, building_id.substring(12));
//                checkCriteria.put(Const.TABLE_KEY_NAME_VALID, true);
//                if(CommonUtil.getCount(HBaseTable.objects, checkCriteria) <= 0)
//                    throw new Exception("建筑体不存在.");
//                // check end.
//            }
//            String assignedObjId = null;
//            if(insertJson.get("id") != null )
//            {
//                if(insertJson.get("id").toString().startsWith("Sp")) {
//                    String spId = insertJson.get("id").toString();
//                    assignedObjId = spId.substring(12);
//                    if (!DpUtils.verifyAssignedObjID(assignedObjId)) {
//                        return DpUtils.errorJsonMsg("指定的id错误.");
//                    }
//                }else
//                    return DpUtils.errorJsonMsg("指定的id错误.");
//            }
//            String subType = null;
//            if(insertJson.get("business_type") != null && insertJson.getString("business_type").length() > 0){
//                if(PhysicalObject.bussinessSpaceTypeMap.containsKey(insertJson.getString("business_type")))
//                    subType = insertJson.getString("business_type");
//                else
//                    throw new Exception("业务类型不存在!");
//            }
//            ObjectIdName name = new ObjectIdName();
//            String id = ObjectUtil.addObj(projectId.substring(2), Const.OBJ_TYPE_SPACE, subType,
//                    (JSONObject) insertJson.get(Const.JSON_FIELD_JSON_DATA_INFOS), ObjectType.space, name, assignedObjId, true);
//            if(floor_id != null){
//                //insert floor-space relation
//                SubsetUtil.addRel(projectId, floor_id, id);
//            }else if(building_id != null){
//                //insert building-space relation
//                SubsetUtil.addRel(projectId, building_id, id);
//            }
//            return DpUtils.successJsonMsgWithId("", id, name.getNameKey(), name.getNameValue());
//        }catch (Exception ex)
//        {
//            ex.printStackTrace();
//            return DpUtils.errorJsonMsg(ex.getMessage() == null? "添加失败!": ex.getMessage());
//        }
//    }

    @Path("/delete")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String delete(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr){
        try {
            JSONObject criteria = JSONObject.parseObject(jsonStr);
            criteria = (JSONObject) criteria.get("criteria");
            CommonUtil.deleteObject(criteria.get("id").toString(), criteria.get("id").toString().substring(2, 12),
                    criteria.get("id").toString().substring(0, 2), criteria.get("id").toString().substring(12), ObjectType.space,true);
            return DpUtils.successJsonMsg("");
        }catch (Exception ex)
        {
            ex.printStackTrace();
            return DpUtils.errorJsonMsg(ex.getMessage());
        }
    }
    @Path("/batch/delete")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String batchDelete(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr){
        try {
            JSONObject criteria = JSONObject.parseObject(jsonStr);
            criteria = (JSONObject) criteria.get("criteria");
            JSONArray arr = criteria.getJSONArray("ids");
            if(arr == null || arr.size() == 0)
                throw new Exception("id不能为空");
            Set<String> idSet = new HashSet<>();
            for(int i = 0; i < arr.size(); ++i){
                if(arr.get(i) == null || !arr.get(i).toString().startsWith("Sp"))
                    continue;
                if(!idSet.contains(arr.getString(i))){
                    idSet.add(arr.getString(i));
                }
            }
            CommonUtil.batchDeleteObject(projectId.substring(2), idSet, true, false);
            return DpUtils.successJsonMsg("");
        }catch (Exception ex)
        {
            ex.printStackTrace();
            return DpUtils.errorJsonMsg(ex.getMessage());
        }
    }
    @Path("/affected/query")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String queryAffectedBusinessSpace(@QueryParam(Const.PROJECT_ID) String projectId){
        try {
            String pjId = projectId.substring(2);
            Set<String> idSet = SpaceUtils.getAffectedSpaceIdList(pjId);
            JSONArray criteriasNode = new JSONArray();
            for(String id : idSet){
                JSONObject obj = new JSONObject();
                obj.put("id", id);
                criteriasNode.add(obj);
            }
            CommonQueryCriteria cqc = new CommonQueryCriteria();
            EqStaticInfo eqStaticInfo = new EqStaticInfo(projectId);
            JSONArray result = CommonUtil.batchQuerySelfInfo(projectId.substring(2), criteriasNode, cqc, eqStaticInfo);
            return DpUtils.returnJSONArray(result, "");
        }catch (Exception ex)
        {
            ex.printStackTrace();
            return DpUtils.errorJsonMsg(ex.getMessage());
        }
    }
    @Path("/affected/delete_notice")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String deleteAffectedBusinessSpaceNotice(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr){
        try {
            JSONObject criteria = JSONObject.parseObject(jsonStr);
            JSONArray idArray = criteria.getJSONArray("ids");
            Set<String> idSet = new HashSet<>();
            for(int i = 0; i < idArray.size(); ++i){
                String id = idArray.getString(i);
                if(!idSet.contains(id) && id.startsWith(Const.OBJ_TYPE_SPACE))
                    idSet.add(id);
            }
            SpaceUtils.deleteAffectedSpaceIds(projectId.substring(2), idSet);
            return DpUtils.successJsonMsg("");
        }catch (Exception ex)
        {
            ex.printStackTrace();
            return DpUtils.errorJsonMsg(ex.getMessage());
        }
    }

    @Path("/query_intersected")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String queryIntersected(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr){
        JSONObject bJson = JSONObject.parseObject(jsonStr);
        String id = bJson.getString("id");
        JSONObject body = JSONObject.parseObject(String.format("{\"criterias\":[{\"id\":\"%s\"}]}", id));
        JSONArray content = JSONObject.parseObject(resourceContext.getResource(PhysicalObject.class).batchQueryById(projectId, body)).getJSONArray("Content");
        if(content.size() == 0){
            return DpUtils.errorJsonMsg("Object not existed.");
        }
        if(!content.getJSONObject(0).containsKey("location")){
            return DpUtils.errorJsonMsg("Cannot address the floor.");
        }
        JSONArray polyArrs = content.getJSONObject(0).getJSONObject("infos").getJSONArray("Outline");
        if(polyArrs == null || polyArrs.size() == 0){
            return DpUtils.errorJsonMsg("This space doesn't have valid Outline.");
        }

        String flId = content.getJSONObject(0).getJSONObject("location").getString("floor");
        String reStr = resourceContext.getResource(PhysicalObject.class).subsetQuery(projectId, new JSONObject()
                .fluentPut("criteria", new JSONObject().fluentPut("id", flId)
                        .fluentPut("type", bJson.get("type") == null ? new JSONArray().fluentAdd("Sp") : bJson.get("type"))));
        JSONArray sps = JSONObject.parseObject(reStr).getJSONArray("Content");
        if(sps.size() == 0){
            return DpUtils.successJsonResp("", sps).toString();
        }
        JSONArray reArr = new JSONArray();

        GeometryFactory gf = new GeometryFactory();
        List<Polygon> thisPolies = this.buildPolygons(gf, polyArrs);

        for (int i = 0; i < sps.size(); i++) {
            JSONObject sp = sps.getJSONObject(i);
            if(sp.getString("id").equalsIgnoreCase(id)){
                continue;
            }
            if(!sp.getJSONObject("infos").containsKey("Outline") || sp.getJSONObject("infos").getJSONArray("Outline").size() == 0){
                continue;
            }
            List<Polygon> tmpPolies = this.buildPolygons(gf, sp.getJSONObject("infos").getJSONArray("Outline"));
            for(Polygon srcPoly : thisPolies){
                boolean bleak = false;
                for(Polygon tmpPoly : tmpPolies){
                    if(srcPoly.intersects(tmpPoly)){
                        reArr.add(sp);
                        bleak = true;
                        break;
                    }
                }
                if(bleak){
                    break;
                }
            }
        }
        return DpUtils.successJsonResp("", reArr).fluentPut("Count", reArr.size()).toString();
    }

    /**根据空间Outline json构建多边形
     * @param gf
     * @param polyArrs
     * @return
     */
    private List<Polygon> buildPolygons(GeometryFactory gf, JSONArray polyArrs){
        List<Polygon> polies = new ArrayList<>();
        for (int i = 0; i < polyArrs.size(); i++) {
            List<Coordinate> points = new ArrayList<>();
            List<List<Coordinate>> lsts = new ArrayList<>();
            JSONArray polyArr = polyArrs.getJSONArray(i);
            JSONArray pntArr = polyArr.getJSONArray(0);
            for (int j = 0; j < pntArr.size(); j++) {
                JSONObject item = pntArr.getJSONObject(j);
                points.add(new Coordinate(item.getDoubleValue("X"), item.getDoubleValue("Y")));
            }
            for (int j = 1; j < polyArr.size(); j++) {
                JSONArray tmpArr = polyArr.getJSONArray(j);
                List<Coordinate> tmpPnts = new ArrayList<>();
                for (int k = 0; k < tmpArr.size(); k++) {
                    JSONObject item = tmpArr.getJSONObject(k);
                    tmpPnts.add(new Coordinate(item.getDoubleValue("X"), item.getDoubleValue("Y")));
                }
                lsts.add(tmpPnts);
            }
            LinearRing shell = new LinearRing(new CoordinateArraySequence(points.toArray(new Coordinate[points.size()])), gf);
            LinearRing[] holes = new LinearRing[lsts.size()];
            for (int k = 0; k < lsts.size(); k++) {
                List<Coordinate> lst = lsts.get(k);
                holes[k] = new LinearRing(new CoordinateArraySequence(lst.toArray(new Coordinate[lst.size()])), gf);
            }
            Polygon polygon = new Polygon(shell, holes, gf);
            polies.add(polygon);
        }
        return polies;
    }

}
