package com.persagy.dmp.rwd.digital.service.calstrategy;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.persagy.dmp.basic.dto.RelationCalRuleDTO;
import com.persagy.dmp.basic.dto.RequestData;
import com.persagy.dmp.common.constant.CommonConstant;
import com.persagy.dmp.define.entity.DigitalEquipClassRel;
import com.persagy.dmp.define.entity.RelationDefine;
import com.persagy.dmp.digital.entity.*;
import com.persagy.dmp.rwd.basic.constant.EquipmentTypeEnum;
import com.persagy.dmp.rwd.define.service.DigitalEquipClassRelService;
import com.persagy.dmp.rwd.define.service.IRelationDefineService;
import com.persagy.dmp.rwd.digital.dao.ObjectDigitalMapper;
import com.persagy.dmp.rwd.digital.dao.ObjectRelationMapper;
import com.persagy.dmp.rwd.digital.domain.CalculatingDTO;
import com.persagy.dmp.rwd.digital.domain.FloorCalDTO;
import com.persagy.dmp.rwd.digital.service.*;
import com.persagy.dmp.rwd.digital.utils.ObjectDigitalCriteriaHelper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.locationtech.jts.math.Vector2D;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/***
 * Description: 管网系统设备分块关系计算
 * @author : lijie
 * @date :2021/10/9 16:26
 * Update By lijie 2021/10/9 16:26
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class SysBlockCalculateServiceImpl implements CalculateService {

    private final IObjectRelationService objectRelationService;
    private final ObjectRelationMapper objectRelationMapper;
    private final ObjectDigitalMapper objectDigitalMapper;
    private final RelationProjectCalService relationProjectCalService;
    private final IRelationDefineService relationDefineService;
    private final ConnectedBlockSourceService connectedBlockSourceService;
    private final ConnectedBlockService connectedBlockService;
    private final DigitalEquipClassRelService digitalEquipClassRelService;


    /***
     * Description: 空间连通关系计算
     * @param calRule : 计算规则
     * @param calculatingDTO : 请求参数
     * @return : void
     * @author : lijie
     * @date :2021/10/9 16:26
     * Update By lijie 2021/10/9 16:26
     */
    @Override
    public void calculateRelation(RelationCalRuleDTO calRule, CalculatingDTO calculatingDTO) {
        // 1.根据项目id和图类型查询对应的关系数据
        ObjectRelationProjectCal objectRelationProjectCal = getRelationProjectCalByProjectIdAndRelCode(calculatingDTO);
        if (null==objectRelationProjectCal
                || CollUtil.isEmpty(objectRelationProjectCal.getMepSystemType())){
            return;
        }
        RelationDefine relationDefine = getRelationCalByGraphCodeAndRelCode(calculatingDTO);
        if (null==relationDefine){
            return;
        }
        Set<String> mepSystemTypes = objectRelationProjectCal.getMepSystemType();
        String defineName = relationDefine.getName();
        // 2.分块
        relCalculateBlock(calRule,calculatingDTO,mepSystemTypes,defineName);
        // 3.设置源
        setSourceType(calRule,calculatingDTO,mepSystemTypes,defineName);
        // 4.计算流向



    }

    /***
     * Description: 设置数据源
     * @param calRule : 计算规则
     * @param calculatingDTO : 请求参数
     * @param mepSystemTypes : 构件用途类型
     * @param defineName : 边类型名称
     * @return : void
     * @author : lijie
     * @date :2021/10/13 18:49
     * Update By lijie 2021/10/13 18:49
     */
    private void setSourceType(RelationCalRuleDTO calRule, CalculatingDTO calculatingDTO,
                               Set<String> mepSystemTypes, String defineName) {
        // 1.根据项目id和边类型编码名称查询项目边关系数据
        List<DigitalEquipClassRel> objectRelationProjectCals =
                queryObjectRelationProjectCalsByProjectIdAndRelName(calculatingDTO.getGroupCode(),
                        calculatingDTO.getProjectId(),defineName);
        if (CollUtil.isEmpty(objectRelationProjectCals)){
            // 2.没有指定源设备类型, 直接删除项目下所有源设备类型
            deletePrevSource(calculatingDTO.getProjectId(), null, mepSystemTypes);
            return;
        }
        // 3.根据设备类型查询设备数据
        // 3.1 根据对象类型code查询对象数据
        Set<String> equipClassCodes = objectRelationProjectCals
                .stream()
                .map(DigitalEquipClassRel::getSourceEquipCode)
                .collect(Collectors.toSet());
        List<ObjectDigital> equipments = queryEquipmentsByClassCodes(equipClassCodes,calculatingDTO.getProjectId());
        if (CollUtil.isEmpty(equipments)){
            return;
        }
        Set<String> equipmentIds = equipments.stream().map(ObjectDigital::getId).collect(Collectors.toSet());
        // 3.2 根据项目id和管道用途类型查询分块数据
        List<ConnectedBlock> connectedBlocks =
                queryConnectedBlockByProjectId(calculatingDTO.getGroupCode(), calculatingDTO.getProjectId(),mepSystemTypes);
        if (CollUtil.isEmpty(connectedBlocks)){
            return;
        }
        List<ConnectedBlockSource> result = new ArrayList<>();
        for (ConnectedBlock connectedBlock : connectedBlocks) {
            if (!equipmentIds.contains(connectedBlock.getFirstId())
                    && !equipmentIds.contains(connectedBlock.getSecondId())){
                continue;
            }
            ConnectedBlockSource connectedBlockSource = new ConnectedBlockSource();
            connectedBlockSource.setProjectId(connectedBlock.getProjectId());
            connectedBlockSource.setBlockId(connectedBlock.getBlockId());
            connectedBlockSource.setDomain(connectedBlock.getDomain());
            connectedBlockSource.setMepSystemType(connectedBlock.getMepSystemType());
            connectedBlockSource.setBuildingId(connectedBlock.getBuildingId());
            if (equipmentIds.contains(connectedBlock.getFirstId())){
                connectedBlockSource.setSourceId(connectedBlock.getFirstId());
                connectedBlockSource.setSourceType(connectedBlock.getFirstType());
                connectedBlockSource.setSourceFlag(true);
            }else {
                connectedBlockSource.setSourceId(connectedBlock.getSecondId());
                connectedBlockSource.setSourceType(connectedBlock.getSecondType());
                connectedBlockSource.setSourceFlag(true);
            }
            result.add(connectedBlockSource);
        }
        if (CollUtil.isNotEmpty(result)){
            deletePrevSource(calculatingDTO.getProjectId(),null,mepSystemTypes);
            connectedBlockSourceService.saveBatch(result);
        }
    }
    /***
     * Description: 根据项目id查询分块信息
     * @param groupCode : 集团编码
     * @param projectId : 项目id
     * @param mepSystemTypes : 管道用途分类
     * @return : java.util.List<com.persagy.dmp.digital.entity.ConnectedBlock>
     * @author : lijie
     * @date :2021/10/13 22:21
     * Update By lijie 2021/10/13 22:21
     */
    private List<ConnectedBlock> queryConnectedBlockByProjectId(String groupCode, String projectId,
                                                                Set<String> mepSystemTypes) {
        LambdaQueryWrapper<ConnectedBlock> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ConnectedBlock::getGroupCode,groupCode);
        queryWrapper.eq(ConnectedBlock::getProjectId,projectId);
        queryWrapper.in(ConnectedBlock::getMepSystemType,mepSystemTypes);
        queryWrapper.eq(ConnectedBlock::getValid,true);
        return connectedBlockService.list(queryWrapper);
    }

    /***
     * Description: 根据对象类型查询设备列表
     * @param classCodes : 对象类型列表
     * @param projectId: 项目id
     * @return : java.util.List<com.persagy.dmp.digital.entity.ObjectDigital>
     * @author : lijie
     * @date :2021/10/13 22:06
     * Update By lijie 2021/10/13 22:06
     */
    private List<ObjectDigital> queryEquipmentsByClassCodes(Set<String> classCodes, String projectId) {
        QueryWrapper<ObjectDigital> queryWrapper = new QueryWrapper<>();
        queryWrapper.in(ObjectDigital.CLASS_CODE,classCodes);
        queryWrapper.eq(StrUtil.toUnderlineCase(CommonConstant.QUERY_PROJECTID),projectId);
        queryWrapper.eq(ObjectDigital.PROP_VALID,true);
        queryWrapper.isNotNull("JSON_UNQUOTE(JSON_EXTRACT(infos, '$.bimId'))");
        return objectDigitalMapper.selectList(queryWrapper);
    }

    /***
     * Description:  删除项目下所有源设备类型
     * @param projectId : 项目id
     * @param buildingId : 建筑id
     * @param mepSystemTypes : 管道用途类型名称
     * @return : void
     * @author : lijie
     * @date :2021/10/13 20:06
     * Update By lijie 2021/10/13 20:06
     */
    private void deletePrevSource(String projectId, String buildingId, Set<String> mepSystemTypes) {
        LambdaQueryWrapper<ConnectedBlockSource> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ConnectedBlockSource::getProjectId,projectId);
        queryWrapper.in(ConnectedBlockSource::getMepSystemType,mepSystemTypes);
        if (StrUtil.isNotBlank(buildingId)){
            queryWrapper.eq(ConnectedBlockSource::getBuildingId,buildingId);
        }
        connectedBlockSourceService.remove(queryWrapper);
    }

    /***
     * Description: 根据项目id和边类型编码名称查询数据
     *
     * @param groupCode
     * @param projectId : 项目id
     * @param defineName : 边类型编码名称
     * @return : java.util.List<com.persagy.dmp.digital.entity.ObjectRelationProjectCal>
     * @author : lijie
     * @date :2021/10/13 20:00
     * Update By lijie 2021/10/13 20:00
     */
    private List<DigitalEquipClassRel> queryObjectRelationProjectCalsByProjectIdAndRelName(String groupCode, String projectId,
                                                                                           String defineName) {
        LambdaQueryWrapper<DigitalEquipClassRel> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DigitalEquipClassRel::getGroupCode,groupCode);
        queryWrapper.eq(DigitalEquipClassRel::getProjectId,projectId);
        queryWrapper.eq(DigitalEquipClassRel::getRelCodeName,defineName);
        queryWrapper.eq(DigitalEquipClassRel::getValid,true);
        return digitalEquipClassRelService.list(queryWrapper);
    }

    /***
     * Description: 分块
     * @param calRule : 计算规则
     * @param calculatingDTO : 请求参数
     * @param useTypes : 管道用途
     * @param defineName :
     * @return : void
     * @author : lijie
     * @date :2021/10/12 20:12
     * Update By lijie 2021/10/12 20:12
     */
    private void relCalculateBlock(RelationCalRuleDTO calRule, CalculatingDTO calculatingDTO,
                                   Set<String> useTypes, String defineName) {
        for (String useType : useTypes) {
            String classCode = getClassCodeByUseType(useType);
            // 1.根据项目id查询楼层
            List<FloorCalDTO> floors = queryFloorByProjectId(calculatingDTO);
            if (CollUtil.isEmpty(floors)){
                continue;
            }
            for (FloorCalDTO floor : floors) {
                // 2.查询符合条件的管道构件
                List<ObjectDigital> connectors = objectDigitalMapper.queryConnectors(calculatingDTO.getGroupCode(),
                        calculatingDTO.getProjectId(),floor.getModelId(),useType,classCode);
                if (CollUtil.isEmpty(connectors)){
                    continue;
                }
                // 2.1 对connectors进行排序
                sortConnectorsAndSetUseState(connectors);
                // 3.根据modelId查询其他构件
                List<ObjectDigital> elements = objectDigitalMapper.queryModelElements(calculatingDTO.getGroupCode(),
                        calculatingDTO.getProjectId(),floor.getModelId());
                // 4.获得一个楼层的分块



            }

        }

    }
    /***
     * Description: 根据设备,部件,连接件,其他,水管,风管进行排序
     * @param connectors : 设备连接件构件集合
     * @return : void  
     * @author : lijie
     * @date :2021/10/13 16:40
     * Update By lijie 2021/10/13 16:40
     */
    private void sortConnectorsAndSetUseState(List<ObjectDigital> connectors) {
        Map<String,Integer> sortMap = new HashMap<>();
        sortMap.put(EquipmentTypeEnum.OTMCEM.name(),0);
        sortMap.put(EquipmentTypeEnum.OTMCCN.name(),1);
        sortMap.put(EquipmentTypeEnum.OTMCJO.name(),2);
        sortMap.put(EquipmentTypeEnum.OTMCOT.name(),2);
        sortMap.put(EquipmentTypeEnum.OTMCPL.name(),3);
        sortMap.put(EquipmentTypeEnum.OTMCDU.name(),4);
        connectors.forEach(connector->{
            if (null==connector.getInfos()
                    || !connector.getInfos().has("belongType")){
                return;
            }
            String belongType = connector.getInfos().get("belongType").asText();
            if (!sortMap.containsKey(belongType)) {
                return;
            }
            connector.setSequenceId(sortMap.get(belongType));
            connector.setUsedFlag(false);
        });
        // 根据sequenceId进行排序
        connectors.sort(Comparator.comparing(ObjectDigital::getSequenceId));
    }

    /***
     * Description: 根据项目id查询楼层
     * @param calculatingDTO : 请求参数
     * @return : java.util.List<com.persagy.dmp.digital.entity.ObjectDigital>
     * @author : lijie
     * @date :2021/10/12 20:32
     * Update By lijie 2021/10/12 20:32
     */
    private List<FloorCalDTO> queryFloorByProjectId(CalculatingDTO calculatingDTO) {
        return objectDigitalMapper.queryFloorsByProjectId(calculatingDTO);
    }

    /***
     * Description: 根据管道类型获得对应的对象类型code
     * @param useType : 管道用途类型名称
     * @return : java.lang.String
     * @author : lijie
     * @date :2021/10/12 15:40
     * Update By lijie 2021/10/12 15:40
     */
    private String getClassCodeByUseType(String useType){
        if (useType.equals("补风管") || useType.equals("空调送风风管") || useType.equals("空调排风风管")
                || useType.equals("通风送风风管") || useType.equals("正压送风风管") || useType.equals("新风风管")
                || useType.equals("排烟风管") || useType.equals("通风排风风管")
                || useType.equals("送风兼消防补风风管") || useType.equals("排风兼排烟管") || useType.equals("排油烟管")
                || useType.equals("回风风管")){
            return EquipmentTypeEnum.OTAAPL.name();
        }
        return EquipmentTypeEnum.OTLAPL.name();
    }

    /***
     * Description: 根据图类型与关系类型查询关系类型数据
     * @param calculatingDTO : 请求参数
     * @return : com.persagy.dmp.define.entity.RelationDefine
     * @author : lijie
     * @date :2021/10/12 15:10
     * Update By lijie 2021/10/12 15:10
     */
    private RelationDefine getRelationCalByGraphCodeAndRelCode(CalculatingDTO calculatingDTO) {
        LambdaQueryWrapper<RelationDefine> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RelationDefine::getGraphCode,calculatingDTO.getGraphCode());
        queryWrapper.eq(RelationDefine::getCode,calculatingDTO.getRelCode());
        queryWrapper.eq(RelationDefine::getValid,true);
        return relationDefineService.getOne(queryWrapper);
    }

    /***
     * Description: 根据项目id和图类型查询关系数据
     * @param calculatingDTO : 请求参数
     * @return : com.persagy.dmp.digital.entity.ObjectRelationProjectCal
     * @author : lijie
     * @date :2021/10/12 14:56
     * Update By lijie 2021/10/12 14:56
     */
    private ObjectRelationProjectCal getRelationProjectCalByProjectIdAndRelCode(CalculatingDTO calculatingDTO) {
        LambdaQueryWrapper<ObjectRelationProjectCal> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ObjectRelationProjectCal::getGraphCode,calculatingDTO.getGraphCode());
        queryWrapper.eq(ObjectRelationProjectCal::getProjectId,calculatingDTO.getProjectId());
        queryWrapper.eq(ObjectRelationProjectCal::getValid,true);
        queryWrapper.eq(ObjectRelationProjectCal::getRelCode,calculatingDTO.getRelCode());
        queryWrapper.eq(ObjectRelationProjectCal::getGroupCode,calculatingDTO.getGroupCode());
        return relationProjectCalService.getOne(queryWrapper);
    }

    /***
     * Description: 根据请求参数和id集合删除关系
     * @param calculatingDTO : 请求参数
     * @return : void
     * @author : lijie
     * @date :2021/9/11 16:13
     * Update By lijie 2021/9/11 16:13
     */
    private void deleteRelDataByCondition(CalculatingDTO calculatingDTO) {
        QueryWrapper<ObjectRelation> queryWrapper = ObjectDigitalCriteriaHelper.createCommonQueryWrapper(null,calculatingDTO);
        queryWrapper.eq(StrUtil.toUnderlineCase(ObjectRelation.GRAPH_CODE_HUM),calculatingDTO.getGraphCode());
        queryWrapper.eq(StrUtil.toUnderlineCase(ObjectRelation.REL_CODE_HUM),calculatingDTO.getRelCode());
        objectRelationService.remove(queryWrapper);
    }
}
