package com.persagy.proxy.dictionary.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.persagy.dmp.common.context.AppContext;
import com.persagy.dmp.common.exception.BusinessException;
import com.persagy.dmp.common.helper.SpringHelper;
import com.persagy.dmp.digital.entity.ObjectRelationProjectCal;
import com.persagy.proxy.adm.constant.AdmCommonConstant;
import com.persagy.proxy.adm.constant.AdmRelationTypeEnum;
import com.persagy.proxy.adm.request.AdmQueryCriteria;
import com.persagy.proxy.adm.request.AdmResponse;
import com.persagy.proxy.adm.service.impl.AbstractAdmBaseServiceImpl;
import com.persagy.proxy.adm.utils.GeoToolsUtil;
import com.persagy.proxy.calculation.service.IAdmObjectRelationCalService;
import com.persagy.proxy.common.entity.InstanceUrlParam;
import com.persagy.proxy.dictionary.model.AdmModelRel;
import com.persagy.proxy.dictionary.service.IAdmModelRelService;
import com.persagy.proxy.object.model.AdmEquipment;
import com.persagy.proxy.object.model.AdmFloor;
import com.persagy.proxy.object.model.AdmISpace;
import com.persagy.proxy.object.service.IAdmEquipmentService;
import com.persagy.proxy.object.service.IAdmFloorService;
import com.persagy.proxy.object.service.IAdmISpaceService;
import com.persagy.proxy.object.service.IAdmSpaceService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @ClassName AdmModelRelServiceImpl
 * @Description: 模型修改设备元空间的建筑楼层信息
 * @Author linhuili
 * @Date 2021/8/31 14:31
 * @Version V1.0
 **/
@Service
@Slf4j
public class AdmModelRelServiceImpl extends AbstractAdmBaseServiceImpl<AdmModelRel> implements IAdmModelRelService {

    @Autowired
    private IAdmFloorService floorService;
    @Autowired
    private IAdmEquipmentService equipmentService;
    @Autowired
    private IAdmISpaceService iSpaceService;
    @Autowired
    private IAdmObjectRelationCalService relationCalService;

    /**
     * 模型修改设备元空间的建筑楼层信息
     * @param context
     * @param clazz
     * @param vo
     * @return
     */
    @Override
    public void doUpdateModelInfo(InstanceUrlParam context, Class<AdmModelRel> clazz, AdmModelRel vo){
        // 查询楼层实体
        List<AdmFloor> admFloors = queryFloorEntity(context, null, vo.getFloorId());
        if(CollUtil.isEmpty(admFloors)){
            throw new BusinessException("楼层不存在");
        }
        log.info("模型修改设备元空间参数{},{}"+context,vo);
        // 在用模型的楼层计算状态改为2
        //处理关系标记
        SpringHelper.getBean(IAdmModelRelService.class).processFloorCalState(context, vo.getModelId());
        AdmFloor floorEntity = admFloors.get(0);
        SpringHelper.getBean(IAdmModelRelService.class).processRelObjects(context, vo.getOldBimId(), vo.getBimId(), floorEntity);
        // 补充楼层属性
        Map<String, Object> infos = floorEntity.getInfos();
        if(CollUtil.isEmpty(infos)){
            infos = new HashMap<>(16);
            floorEntity.setInfos(infos);
        }
        infos.put("floorMap",vo.getFloorMap());
        Double floorElevation = MapUtil.getDouble(vo.getAttributeMap(), AdmCommonConstant.FLOOR_ELEVATION);
        if(floorElevation != null){
            infos.put(AdmCommonConstant.FLOOR_ELEVATION, floorElevation/1000);
        }
        floorEntity.setModelId(vo.getModelId());
        floorEntity.setBimIdPre(vo.getBimId());
        //5.修改楼层信息
        floorService.doUpdate(context, AdmFloor.class, CollUtil.newArrayList(floorEntity));
    }

    /**
     * 处理在用模型文件的楼层计算状态
     * @param context:上下文对象
     * @param modelId:模型id
     */
    @Async
    @Override
    public void processFloorCalState(InstanceUrlParam context, String modelId) {
        resetContext(context);
        try {
            List<AdmFloor> floorList = queryFloorEntity(context, modelId, null);
            if(CollUtil.isEmpty(floorList)) {
                return;
            }
            Set<String> floorQueryTypes = getFloorQueryTypes(floorList);
            List<ObjectRelationProjectCal> needCals = relationCalService.queryCalRelByRelationType(context, floorQueryTypes);
            if(CollUtil.isEmpty(needCals)) {
                return;
            }
            //更新边类型计算状态：computationalState为2
            needCals.forEach(item->{
                item.setComputationalState(2);
            });
            relationCalService.updateObjectCalRel(context, needCals);
        }finally {
            AppContext.unload();
        }
    }

    /**
     * 处理关联的元空间和设备
     * @param context:上下文对象
     * @param oldBimId:旧的bimId
     * @param bimId:新的bimId
     * @param floor:楼层对象
     */
    @Async
    @Override
    public void processRelObjects(InstanceUrlParam context, String oldBimId, String bimId, AdmFloor floor) {
        resetContext(context);
        try {
            // 如果bimId不一致，则清空原来的楼层信息
            if(StrUtil.isNotBlank(oldBimId) && !StrUtil.equals(oldBimId, bimId)) {
                updateRelEquipments(context, oldBimId, floor, true);
                updateRelISpace(context, oldBimId, floor, true);
            }
            // 更新新关联的
            updateRelEquipments(context, bimId, floor, false);
            updateRelISpace(context, bimId, floor, false);
        }finally {
            AppContext.unload();
        }
    }
    /***
     * Description: 重新设置一下上下文
     * @param context : 上下文对象
     * return : void
     * @author : lijie
     * Update By 2021/12/21 11:57
     */
    private void resetContext(InstanceUrlParam context){
        AppContext.getContext().setGroupCode(context.getGroupCode());
        AppContext.getContext().setProjectId(context.getProjectId());
        AppContext.getContext().setAccountId(context.getUserId());
        AppContext.getContext().setAppId(context.getAppId());
    }

    /**
     * 更新设备
     * @param context
     * @param bimId
     * @param floor
     */
    private void updateRelEquipments(InstanceUrlParam context, String bimId,
                                     AdmFloor floor, boolean deleteFlag) {
        if(StrUtil.isBlank(bimId)) {
            return;
        }
        //查询模型下的设备信息
        AdmQueryCriteria criteria = new AdmQueryCriteria();
        criteria.addFilters("bimId startwith '" + bimId + ":'");
        // 如果是删除，只删本楼层的
        if(deleteFlag) {
            criteria.addFilters("floorId = '" + floor.getId() + "'");
        }
        criteria.setName(AdmEquipment.OBJ_TYPE);
        AdmResponse admResponse = equipmentService.doQuery(context, criteria, AdmEquipment.class);
        List<AdmEquipment> content = (List<AdmEquipment>) admResponse.getContent();
        if(CollUtil.isEmpty(content)) {
            return;
        }
        List<AdmEquipment> rsList = new ArrayList<>();
        for (AdmEquipment admEquipment : content) {
            if(deleteFlag) {
                admEquipment.setBuildingId(null);
                admEquipment.setFloorId(null);
                rsList.add(admEquipment);
                continue;
            }
            if(!isPointInPolygon(admEquipment, floor.getOutline())) {
                continue;
            }
            //修改设备的建筑楼层信息
            admEquipment.setBuildingId(floor.getBuildingId());
            admEquipment.setFloorId(floor.getId());
            rsList.add(admEquipment);
        }
        if(CollUtil.isEmpty(rsList)) {
            return;
        }
        equipmentService.doUpdate(context, AdmEquipment.class, rsList);
    }

    /**
     * 更新元空间
     * @param context
     * @param bimId
     * @param floor
     */
    private void updateRelISpace(InstanceUrlParam context, String bimId,
                                 AdmFloor floor, boolean deleteFlag) {
        if(StrUtil.isBlank(bimId)) {
            return;
        }
        //查询模型下的设备信息
        AdmQueryCriteria criteria = new AdmQueryCriteria();
        criteria.addFilters("bimId startwith '" + bimId + ":'");
        // 如果是删除，只删本楼层的
        if(deleteFlag) {
            criteria.addFilters("floorId = '" + floor.getId() + "'");
        }
        criteria.setName(AdmISpace.OBJ_TYPE);
        AdmResponse admResponse = iSpaceService.doQuery(context, criteria, AdmISpace.class);
        List<AdmISpace> content = (List<AdmISpace>) admResponse.getContent();
        if(CollUtil.isEmpty(content)) {
            return;
        }
        Set<String> nullList = CollUtil.newHashSet("buildingId", "floorId");
        for (AdmISpace iSpace : content) {
            if(deleteFlag) {
                iSpace.setBuildingId(null);
                iSpace.setFloorId(null);
                iSpace.setNullList(nullList);
                continue;
            }
            //修改设备的建筑楼层信息
            iSpace.setBuildingId(floor.getBuildingId());
            iSpace.setFloorId(floor.getId());
        }
        iSpaceService.doUpdate(context, AdmISpace.class, content);
    }

    /**
     * 判断点是否在面中
     * @return
     */
    private boolean isPointInPolygon(AdmEquipment equipment, List<ObjectNode> outline){
        // 如果为空，表示无限边界
        if(CollUtil.isEmpty(outline)) {
            return true;
        }
        Boolean flag = true;
        //设备BIM坐标
        String bimLocation = equipment.getBimLocation();
        //判断点是否在面中：即设备位置是否在该楼层轮廓线中
        if(StrUtil.isBlank(bimLocation)){
            return false;
        }
        String[] split = bimLocation.split(",");
        ObjectNode node = JsonNodeFactory.instance.objectNode();
        node.put("x",Double.valueOf(split[0]));
        node.put("y",Double.valueOf(split[1]));
        node.put("z",Double.valueOf(split[2]));
        return GeoToolsUtil.isPointInPoly(node,outline);
    }

    /**
     * 查询模型文件是否有楼层在使用
     */
    private List<AdmFloor> queryFloorEntity(InstanceUrlParam context, String modelId, String floorId){
        if(StrUtil.isBlank(modelId) && StrUtil.isBlank(floorId)){
            return new ArrayList<>();
        }
        AdmQueryCriteria criteria = new AdmQueryCriteria();
        if(StrUtil.isNotBlank(modelId)) {
            criteria.addFilters(" modelId = '" + modelId + "' ");
        }
        if(StrUtil.isNotBlank(floorId)) {
            criteria.addFilters(" id = '"+floorId+"' ");
        }
        criteria.setName(AdmFloor.OBJ_TYPE);
        AdmResponse admResponse = floorService.doQuery(context, criteria, AdmFloor.class);
        return (List<AdmFloor>)admResponse.getContent();
    }

    /**
     * 获取楼层查询类型
     * @param floorList
     */
    private Set<String> getFloorQueryTypes(List<AdmFloor> floorList){
        Set<String> types = new HashSet<>();
        for (AdmFloor admFloor : floorList) {
            if(CollUtil.isNotEmpty(admFloor.getOutline())){
                types.add(AdmRelationTypeEnum.EQ2FL.getRelCode());
                types.add(AdmRelationTypeEnum.FL2SP.getRelCode());
                types.add(AdmRelationTypeEnum.BD2SP.getRelCode());
            }
        }
        types.add(AdmRelationTypeEnum.EQ2SP_IN.getRelCode());
        types.add(AdmRelationTypeEnum.EQ2EQ_ACAIRNETWORK_2.getRelCode());
        types.add(AdmRelationTypeEnum.EQ2EQ_CONDWATERNETWORK_1.getRelCode());
        types.add(AdmRelationTypeEnum.EQ2EQ_VENTNETWORK_2.getRelCode());
        types.add(AdmRelationTypeEnum.EQ2EQ_ACAIRNETWORK_3.getRelCode());
        types.add(AdmRelationTypeEnum.EQ2EQ_HEATWATERLOOP_4.getRelCode());
        types.add(AdmRelationTypeEnum.EQ2EQ_HEATWATERLOOP_1.getRelCode());
        types.add(AdmRelationTypeEnum.EQ2EQ_DHWNETWORK_1.getRelCode());
        types.add(AdmRelationTypeEnum.EQ2EQ_RECYCLEWATERNETWORK_1.getRelCode());
        types.add(AdmRelationTypeEnum.EQ2EQ_FIREVENTNETWORK_1.getRelCode());
        types.add(AdmRelationTypeEnum.EQ2EQ_FIREWATERNETWORK_1.getRelCode());
        types.add(AdmRelationTypeEnum.EQ2EQ_VRFNETWORK_1.getRelCode());
        types.add(AdmRelationTypeEnum.EQ2EQ_CHILLWATERLOOP_2.getRelCode());
        types.add(AdmRelationTypeEnum.EQ2EQFRESHAIRNETWORK_1.getRelCode());
        types.add(AdmRelationTypeEnum.EQ2EQ_CHILLWATERLOOP_1.getRelCode());
        types.add(AdmRelationTypeEnum.EQ2EQ_DRINKINGWATERNETWORK_1.getRelCode());
        types.add(AdmRelationTypeEnum.EQ2EQ_COOLINGWATERLOOP_2.getRelCode());
        types.add(AdmRelationTypeEnum.EQ2EQ_VENTNETWORK_1.getRelCode());
        types.add(AdmRelationTypeEnum.EQ2EQ_HEATWATERLOOP_2.getRelCode());
        types.add(AdmRelationTypeEnum.EQ2EQ_COOLINGWATERLOOP_1.getRelCode());
        types.add(AdmRelationTypeEnum.EQ2EQ_ACAIRNETWORK_1.getRelCode());
        types.add(AdmRelationTypeEnum.EQ2EQ_SUPPLYWATERNETWORK_1.getRelCode());
        types.add(AdmRelationTypeEnum.EQ2EQ_DRAININGWATERNETWORK_1.getRelCode());
        return types;
    }
}
