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.QueryWrapper;
import com.persagy.dmp.basic.dto.CoordinateCalInfo;
import com.persagy.dmp.basic.dto.RelationCalRuleDTO;
import com.persagy.dmp.common.constant.CommonConstant;
import com.persagy.dmp.common.exception.BusinessException;
import com.persagy.dmp.define.entity.RelationDefine;
import com.persagy.dmp.digital.entity.ObjectDigital;
import com.persagy.dmp.digital.entity.ObjectRelation;
import com.persagy.dmp.rwd.basic.constant.BusinessErrorRwdCode;
import com.persagy.dmp.rwd.basic.constant.DigitalObjectType;
import com.persagy.dmp.rwd.basic.utils.DigitalCommonUtils;
import com.persagy.dmp.rwd.basic.utils.GeoToolsUtil;
import com.persagy.dmp.rwd.digital.dao.ObjectDigitalMapper;
import com.persagy.dmp.rwd.digital.domain.CalculatingDTO;
import com.persagy.dmp.rwd.digital.service.CalculateService;
import com.persagy.dmp.rwd.digital.service.IObjectDigitalService;
import com.persagy.dmp.rwd.digital.service.IObjectRelationService;
import com.persagy.dmp.rwd.digital.utils.ObjectDigitalCriteriaHelper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/***
 * Description: 根据坐标计算关系的策略类
 * @author : lijie
 * @date :2021/9/8 17:02
 * Update By lijie 2021/9/8 17:02
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class CoordinateCalculateServiceImpl implements CalculateService {

    private static final Long MAX_HANDLE_NUM = 10000L;

    private final IObjectDigitalService objectDigitalService;
    private final ObjectDigitalMapper objectDigitalMapper;
    private final IObjectRelationService objectRelationService;

    /***
     * Description: 根据坐标计算关系
     * @param calRule : 计算规则
     * @param calculatingDTO : 参数
     * @return : void
     * @author : lijie
     * @date :2021/9/8 17:14
     * Update By lijie 2021/9/8 17:14
     */
    @Override
    public void calculateRelation(RelationCalRuleDTO calRule, CalculatingDTO calculatingDTO) {
        CoordinateCalInfo graphCalInfo = calRule.getGraphCalInfo();
        RelationDefine relationDefine = calculatingDTO.getRelationDefine();
        if (StrUtil.isBlank(relationDefine.getFromObjType()) || StrUtil.isBlank(relationDefine.getToObjType())){
            throw new BusinessException(BusinessErrorRwdCode.A7312.getCode(),BusinessErrorRwdCode.A7312.getDesc());
        }
        // 试验过sql计算后,发现效率低下.先暂时使用java实现
        // 1.先分页查询from对象,在分页处理to对象
        Boolean calRelFromFileRelFlag = graphCalInfo.getCalRelFromFileRelFlag();
        if (null!=calRelFromFileRelFlag && calRelFromFileRelFlag){
            // 这种暂时不计算,还没想好怎么做
            return;
        }
        // 2.查询from侧的对象
        // 两侧都是点的则不进行计算
        if (graphCalInfo.getFromIsPointFlag() && graphCalInfo.getToIsPointFlag()){
            return;
        }
        // 点-面,面-面的计算
        calculateRelationInternal(graphCalInfo,calculatingDTO,
                calculatingDTO.getFilterRelFlag()?calculatingDTO.getFromIds():new HashSet<>(),
                relationDefine.getFromObjType(),graphCalInfo.getFromInfoCode(),
                calculatingDTO.getFilterRelFlag()?calculatingDTO.getToIds():new HashSet<>(),
                relationDefine.getToObjType(),graphCalInfo.getToInfoCode());
    }

    /***
     * Description: 内部的计算关系(点-面计算逻辑)
     * @param graphCalInfo : 坐标计算信息对象
     * @param calculatingDTO : 请求参数
     * @param fromIds : 来源id集合
     * @param fromObjType : 来源对象分类
     * @param fromInfoCode : 来与对象信息点
     * @param toIds : 去向id集合
     * @param toObjType : 去向对象分类
     * @param toInfoCode : 去向对象信息点
     * @return : void
     * @author : lijie
     * @date :2021/9/11 14:43
     * Update By lijie 2021/9/11 14:43
     */
    private void calculateRelationInternal(CoordinateCalInfo graphCalInfo, CalculatingDTO calculatingDTO,
                                           Set<String> fromIds, String fromObjType, String fromInfoCode,
                                           Set<String> toIds, String toObjType, String toInfoCode) {
        if (StrUtil.isBlank(fromInfoCode) || StrUtil.isBlank(toInfoCode)){
            return;
        }
        // 1.查询来源对象
        List<ObjectDigital> fromObjectDigitals;
        if (graphCalInfo.getFromIsPointFlag()){
            fromObjectDigitals = queryPointData(fromIds,fromObjType,fromInfoCode,calculatingDTO);
        }else {
            fromObjectDigitals = queryPlaneData(fromIds,fromObjType,fromInfoCode,calculatingDTO);
        }
        if (CollUtil.isEmpty(fromObjectDigitals)){
            return;
        }
        // 2.查询去向对象
        List<ObjectDigital> toObjectDigitals;
        if (graphCalInfo.getToIsPointFlag()){
            toObjectDigitals = queryPointData(toIds,toObjType,toInfoCode,calculatingDTO);
        }else {
            toObjectDigitals = queryPlaneData(toIds,toObjType,toInfoCode,calculatingDTO);
        }
        if (CollUtil.isEmpty(toObjectDigitals)){
            return;
        }
        // 呵呵,O(n^2)
        List<ObjectRelation> resultList = new ArrayList<>();
        for (ObjectDigital fromObjectDigital : fromObjectDigitals) {
            for (ObjectDigital toObjectDigital : toObjectDigitals) {
                boolean pointInPoly;
                if (graphCalInfo.getFromIsPointFlag()){
                    pointInPoly = GeoToolsUtil
                            .isPointInPoly(fromObjectDigital.getBimLocation(), toObjectDigital.getOutLines());
                }else if (graphCalInfo.getToIsPointFlag()){
                    pointInPoly = GeoToolsUtil
                            .isPointInPoly(toObjectDigital.getBimLocation(), fromObjectDigital.getOutLines());
                }else {
                    pointInPoly = GeoToolsUtil
                            .isPolyInPoly(fromObjectDigital.getOutLines(), toObjectDigital.getOutLines());
                }
                if (!pointInPoly){
                    continue;
                }
                ObjectRelation objectRelation = new ObjectRelation();
                objectRelation.setGroupCode(fromObjectDigital.getGroupCode());
                objectRelation.setProjectId(fromObjectDigital.getProjectId());
                objectRelation.setGraphCode(calculatingDTO.getGraphCode());
                objectRelation.setRelCode(calculatingDTO.getRelCode());
                objectRelation.setObjFrom(fromObjectDigital.getId());
                objectRelation.setObjTo(toObjectDigital.getId());
                if (DigitalObjectType.SPACE.getIndex().equals(fromObjectDigital.getObjType())){
                    objectRelation.setRelValue(fromObjectDigital.getClassCode());
                }
                if (DigitalObjectType.SPACE.getIndex().equals(toObjectDigital.getObjType())){
                    objectRelation.setRelValue(toObjectDigital.getClassCode());
                }
                resultList.add(objectRelation);
            }
        }
        if (CollUtil.isEmpty(resultList)){
            return;
        }
        // 删除关系
        deleteRelDataByCondition(calculatingDTO,fromIds,toIds,
                resultList.stream().map(ObjectRelation::getObjFrom).collect(Collectors.toSet()),
                resultList.stream().map(ObjectRelation::getObjTo).collect(Collectors.toSet()),
                resultList.stream().filter(objectRelation -> StrUtil.isNotBlank(objectRelation.getRelValue()))
                        .map(ObjectRelation::getRelValue).collect(Collectors.toSet()));
        // 新增关系
        objectRelationService.save(resultList);
    }
    /***
     * Description: 根据请求参数和id集合删除关系
     * @param calculatingDTO : 请求参数
     * @param fromIds : 来源id集合
     * @param toIds : 去向id集合
     * @return : void
     * @author : lijie
     * @date :2021/9/11 16:13
     * Update By lijie 2021/9/11 16:13
     */
    private void deleteRelDataByCondition(CalculatingDTO calculatingDTO, Set<String> fromIds,
                                          Set<String> toIds,Set<String> relFromIds,Set<String> relToIds,
                                          Set<String> relValues) {
        QueryWrapper<ObjectRelation> queryWrapper = ObjectDigitalCriteriaHelper.createCommonQueryWrapper(CollUtil.newHashSet(),calculatingDTO);
        queryWrapper.eq(StrUtil.toUnderlineCase(ObjectRelation.GRAPH_CODE_HUM),calculatingDTO.getGraphCode());
        queryWrapper.eq(StrUtil.toUnderlineCase(ObjectRelation.REL_CODE_HUM),calculatingDTO.getRelCode());
        if (CollUtil.isNotEmpty(relValues)){
            queryWrapper.in(StrUtil.toUnderlineCase(ObjectRelation.REL_VALUE_HUM),relValues);
        }
        queryWrapper.and(CollUtil.isNotEmpty(fromIds)
                || CollUtil.isNotEmpty(toIds)
                || CollUtil.isNotEmpty(relFromIds)
                || CollUtil.isNotEmpty(relToIds),wrapper->{
            wrapper.or(CollUtil.isNotEmpty(fromIds),subWapper->subWapper.in(ObjectRelation.PROP_ID,fromIds));
            wrapper.or(CollUtil.isNotEmpty(toIds),subWapper->subWapper.in(ObjectRelation.PROP_ID,toIds));
            wrapper.or(CollUtil.isNotEmpty(relFromIds),subWapper->subWapper.in(ObjectRelation.OBJ_FROM,relFromIds));
            wrapper.or(CollUtil.isNotEmpty(relToIds),subWapper->subWapper.in(ObjectRelation.OBJ_TO,relToIds));
        });
        objectRelationService.delete(queryWrapper);
    }

    /***
     * Description: 查询去向数据
     * @param toIds : 去向id集合
     * @param objType : 去向对象类型
     * @param toInfoCode : 去向对象信息点
     * @param calculatingDTO : 请求参数
     * @return : java.util.List<com.persagy.dmp.digital.entity.ObjectDigital>
     * @author : lijie
     * @date :2021/9/11 15:36
     * Update By lijie 2021/9/11 15:36
     */
    private List<ObjectDigital> queryPlaneData(Set<String> toIds, String objType, String toInfoCode, CalculatingDTO calculatingDTO) {
        QueryWrapper<ObjectDigital> queryWrapper = ObjectDigitalCriteriaHelper.createCommonQueryWrapper(toIds,calculatingDTO);
        if (StrUtil.isNotBlank(objType)){
            queryWrapper.eq(ObjectDigital.OBJ_TYPE,objType);
        }
        queryWrapper.select(ObjectDigital.PROP_ID,
                StrUtil.toUnderlineCase(CommonConstant.QUERY_GROUPCODE),
                StrUtil.toUnderlineCase(CommonConstant.QUERY_PROJECTID),
                StrUtil.format("JSON_UNQUOTE(JSON_EXTRACT(infos,'$.{}')) AS outLines", toInfoCode),
                ObjectDigital.CLASS_CODE,ObjectDigital.OBJ_TYPE);
        return objectDigitalMapper.queryToData(toIds,objType,toInfoCode,calculatingDTO);
    }

    /***
     * Description: 查询来源数据
     * @param fromIds : 来源id集合
     * @param objType : 对象分类
     * @param fromInfoCode : 来源信息点
     * @param calculatingDTO : 请求参数
     * @return : void
     * @author : lijie
     * @date :2021/9/11 15:32
     * Update By lijie 2021/9/11 15:32
     */
    private List<ObjectDigital> queryPointData(Set<String> fromIds, String objType, String fromInfoCode, CalculatingDTO calculatingDTO) {
        QueryWrapper<ObjectDigital> queryWrapper = ObjectDigitalCriteriaHelper.createCommonQueryWrapper(fromIds, calculatingDTO);
        if (StrUtil.isNotBlank(objType)){
            queryWrapper.eq(ObjectDigital.OBJ_TYPE,objType);
        }
        queryWrapper.select(ObjectDigital.PROP_ID,
                StrUtil.toUnderlineCase(CommonConstant.QUERY_GROUPCODE),
                StrUtil.toUnderlineCase(CommonConstant.QUERY_PROJECTID),
                StrUtil.format("JSON_OBJECT('x',JSON_EXTRACT(JSON_UNQUOTE(CONCAT('[',SUBSTRING(" +
                                "JSON_EXTRACT(infos, '$.{}') FROM 2 FOR LENGTH(" +
                                "JSON_EXTRACT(infos, '$.{}'))-2),']')),'$[0]'),'y'," +
                                "JSON_EXTRACT(JSON_UNQUOTE(CONCAT('[',SUBSTRING(JSON_EXTRACT(infos, '$.{}') " +
                                "FROM 2 FOR LENGTH(JSON_EXTRACT(infos, '$.{}'))-2),']')), '$[1]')) AS bimLocation",
                        fromInfoCode,fromInfoCode,fromInfoCode,fromInfoCode),
                ObjectDigital.CLASS_CODE,ObjectDigital.OBJ_TYPE);
        return objectDigitalMapper.queryFromData(fromIds,objType,fromInfoCode,calculatingDTO);
    }
}
