package com.imdroid.sideslope.calc;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.imdroid.secapi.dto.*;
import com.imdroid.sideslope.sal.Device;
import com.imdroid.common.util.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

@Service
public class GNSSCalcFilterService {
    private final Logger logger = LoggerFactory.getLogger(GNSSCalcFilterService.class);

    @Autowired
    GnssCalcDataMapper repository;
    @Autowired
    GnssMsgMapper msgMapper;

    static final int FILTER_MIN_RECORD_NUM = 10;

    static class VaryFilterCycle{
        public LocalDateTime startTime;
        public int filterCycleHour;
    }
    final private ConcurrentHashMap<String, VaryFilterCycle> autoCycleDevices = new ConcurrentHashMap<>();

    final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    public void calc(Device gnssDevice, GnssGroupCalc groupCalc, GnssCalcData newRecord,
                     double[] referPos)  {
        String deviceId = newRecord.getDeviceid();

        //补充解算记录的设备信息
        newRecord.setTenantid(gnssDevice.getTenantId());
        newRecord.setEnabled(true);

        // 计算平滑周期
        int filterCycle = groupCalc.getFilter_hour();
        VaryFilterCycle varyCycle = autoCycleDevices.get(deviceId);
        if(varyCycle!=null){
            filterCycle = varyCycle.filterCycleHour;
        }

        // 平滑处理
        calcFilterLocation(newRecord, referPos, filterCycle, groupCalc.getFilter_min_hour(),
                groupCalc.getXy_threshold(), groupCalc.getZ_threshold(), groupCalc.getAdv_filter());

        repository.insert(newRecord);

        // 更新平滑周期
        if(groupCalc.getAuto_filter()) {
            refreshFilterCycle(deviceId, groupCalc, newRecord);
        }

    }

    public boolean isGoodGnssData(GnssCalcData newRecord, double[] referPos, List<GnssCalcData> gnssHistoryRecords,
                                  float xyThreshold, float zThreshold, boolean isAdvFilter) {
        boolean isGood = true;

        // 检查是不是坏点
        GnssCalcData record0 = null;
        if(gnssHistoryRecords.size()>0) record0 = gnssHistoryRecords.get(0);
        if(record0 != null && Math.abs(record0.getB562e() - newRecord.getB562e())<0.001) {
            return false;
        }
        if(referPos==null && record0!=null) {
            referPos = new double[]{record0.getB562e(), record0.getB562n(), record0.getB562d()};
        }
        if(referPos==null) return false;

        if (Math.abs(newRecord.getB562e() - referPos[0]) > xyThreshold ||
                Math.abs(newRecord.getB562n() - referPos[1]) > xyThreshold ||
                Math.abs(newRecord.getB562d() - referPos[2]) > zThreshold) {
            logger.debug("{} bad point,delta: {},{},{} ",newRecord.getDeviceid(),
                    newRecord.getB562e()- referPos[0],newRecord.getB562n()- referPos[1],newRecord.getB562d()- referPos[2]);
            isGood = false; //记录为坏点，下次不参与滤波
        }
        else if(isAdvFilter){
            GnssCalcData record1 = null;
            if(gnssHistoryRecords.size()>1) record1 = gnssHistoryRecords.get(1);
            if(record1 == null ||
                    Math.abs(newRecord.getB562e() - record1.getB562e()) > xyThreshold*2 ||
                    Math.abs(newRecord.getB562n() - record1.getB562n()) > xyThreshold*2 ||
                    Math.abs(newRecord.getB562d() - record1.getB562d()) > zThreshold*2) {
                logger.debug("{} adv filter bad point",newRecord.getDeviceid());
                isGood = false; //记录为坏点，下次不参与滤波
            }
        }
        return isGood;
    }

    /**
     * 计算东北天的最近融合数据的加权平均, 刘畅20230725 copy 自 avgEND; id>20, 滤波参数6h 或 25h
     */

    public boolean calcFilterLocation(GnssCalcData newRecord, double[] referPos, int filterCycleHour, int minCycleHour,
                                      float xyThreshold, float zThreshold, boolean isAdvFilter){
        String deviceId = newRecord.getDeviceid();
        // 选取[newRecordTime-filterCycleHour, newRcordTime]之间的记录做平滑
        // 如果这个时间段的记录数少于FILTER_MIN_RECORD_NUM，本次不做平滑
        LocalDateTime newRecordTime = newRecord.getCreatetime();
        LocalDateTime filterAfterTime = newRecordTime.minusHours(filterCycleHour);
        LocalDateTime minCycleTime = newRecordTime.minusHours(minCycleHour);
        QueryWrapper<GnssCalcData> query = new QueryWrapper<>();
        query.eq("deviceid", deviceId);
        query.ge("createtime", filterAfterTime.format(dateFormatter));
        query.le("createtime", newRecordTime.format(dateFormatter));
        //query.eq("enabled", true);// 坏点参与比较，不参与滤波
        query.orderByDesc("createtime");

        List<GnssCalcData> gnssHistoryRecords = repository.selectList(query);

        if(gnssHistoryRecords.size() == 0){
            //第一个点无参考，当作坏点处理
            newRecord.setEnabled(false);
        }
        else {
            newRecord.setEnabled(
                    isGoodGnssData(newRecord, referPos, gnssHistoryRecords,xyThreshold,zThreshold,isAdvFilter));

            if(newRecord.getEnabled()) {
                // 求本组和最近recordNum组原始值的平均值
                double sumE = newRecord.getB562e();
                double sumN = newRecord.getB562n();
                double sumD = newRecord.getB562d();
                double msumE = sumE;
                double msumN = sumN;
                double msumD = sumD;
                int count = 1;
                int minCount = 1;

                for (GnssCalcData record : gnssHistoryRecords) {
                    if (record.getEnabled()) {//只选取好点参与滤波
                        sumE += record.getB562e();
                        sumN += record.getB562n();
                        sumD += record.getB562d();
                        count++;
                        // 计算短周期
                        if(record.getCreatetime().isAfter(minCycleTime)){
                            msumE = sumE;
                            msumN = sumN;
                            msumD = sumD;
                            minCount = count;
                        }
                    }
                }
                logger.debug("{} filter records num: {}",deviceId,count);
                if (count >= FILTER_MIN_RECORD_NUM) {
                    newRecord.setRpose(NumberUtils.scaleTwo(sumE / count));
                    newRecord.setRposn(NumberUtils.scaleTwo(sumN / count));
                    newRecord.setRposd(NumberUtils.scaleTwo(sumD / count));
                    newRecord.setAuxe(NumberUtils.scaleTwo(msumE / minCount));
                    newRecord.setAuxn(NumberUtils.scaleTwo(msumN / minCount));
                    newRecord.setAuxd(NumberUtils.scaleTwo(msumD / minCount));
                    return true;
                }
            }
        }

        return false;
    }

    void refreshFilterCycle(String deviceId, GnssGroupCalc groupCalc, GnssCalcData curCalcData){
        if(curCalcData.getAuxe()==null) return;

        LocalDateTime now = LocalDateTime.now();
        // 比较当前均值和FilterMinHour前的均值之差是否超过门限
        LocalDateTime cmpTime = now.minusHours(groupCalc.getFilter_min_hour());
        QueryWrapper<GnssCalcData> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("deviceid", deviceId);
        queryWrapper.le("createtime",cmpTime.format(dateFormatter));
        queryWrapper.eq("enabled",true);
        queryWrapper.isNotNull("rpose");
        queryWrapper.last("limit 1");
        queryWrapper.orderByDesc("createtime");
        GnssCalcData gnssCalcData = repository.selectOne(queryWrapper);

        if (gnssCalcData != null) {
            if(gnssCalcData.getAuxe()==null) return;
            double gapE = Math.abs(gnssCalcData.getAuxe() - curCalcData.getAuxe());
            double gapN = Math.abs(gnssCalcData.getAuxn() - curCalcData.getAuxn());
            if (gapE >= groupCalc.getAuto_threshold() || gapN >= groupCalc.getAuto_threshold()) {
                VaryFilterCycle varyCycle = new VaryFilterCycle();
                varyCycle.startTime = now;
                varyCycle.filterCycleHour = groupCalc.getFilter_min_hour();
                autoCycleDevices.put(deviceId, varyCycle);
                saveMsg(deviceId, curCalcData.getTenantid(),
                        "filter cycle changed to"+varyCycle.filterCycleHour,now);
            }
            else {
                VaryFilterCycle varyCycle = autoCycleDevices.get(deviceId);
                if(varyCycle != null){
                    if(varyCycle.filterCycleHour >= groupCalc.getFilter_hour()){
                        //变周期结束
                        autoCycleDevices.remove(deviceId);
                    }
                    else {
                        if (now.isAfter(varyCycle.startTime.plusHours(1))) {
                            varyCycle.filterCycleHour++;
                            varyCycle.startTime = now;
                            saveMsg(deviceId, curCalcData.getTenantid(),
                                    "filter cycle changed to"+varyCycle.filterCycleHour,now);
                        }
                    }
                }
            }
        }
    }

    public LocalDateTime updateRpos(Device gnssDevice, GnssGroupCalc groupCalc,LocalDateTime afterTime){
        String deviceId=gnssDevice.getDeviceId();
        // 平滑处理
        LocalDateTime beforTime = afterTime.plusHours(groupCalc.getFilter_hour());
        QueryWrapper<GnssCalcData> query = new QueryWrapper<>();
        query.eq("deviceid", deviceId);
        query.le("createtime", beforTime.format(dateFormatter));
        query.ge("createtime", afterTime.format(dateFormatter));
        //query.eq("enabled",true); //好点坏点重新判断
        query.orderByAsc("createtime");

        LocalDateTime lastTime = afterTime;

        List<GnssCalcData> calcDataListToUpdate = repository.selectList(query);
        double[] referPos = null;
        for(GnssCalcData calcData:calcDataListToUpdate){
            calcFilterLocation(calcData, referPos, groupCalc.getFilter_hour(), groupCalc.getFilter_min_hour(),
                    groupCalc.getXy_threshold(), groupCalc.getZ_threshold(), groupCalc.getAdv_filter());
            repository.updateById(calcData);
            lastTime = calcData.getCreatetime();
            referPos = new double[]{calcData.getB562e(), calcData.getB562n(), calcData.getB562d()};
        }
        return lastTime;
    }

    void saveMsg(String deviceId, Integer tenantId, String content, LocalDateTime time){
        GnssMsg gnssMsg = new GnssMsg();
        gnssMsg.setTenantid(tenantId);
        gnssMsg.setCreatetime(time);
        gnssMsg.setDeviceid(deviceId);
        gnssMsg.setMsgtype(0);
        gnssMsg.setMsglen(content.length());
        gnssMsg.setTx(false);
        if(content.length()<=128) {
            gnssMsg.setContent(content);
        }
        else{
            gnssMsg.setContent(content.substring(0,120)+"...");
        }
        msgMapper.insert(gnssMsg);
    }
}
