package com.imdroid.sideslope.executor;

import com.imdroid.common.util.DataTypeUtil;
import com.imdroid.common.util.ThreadManager;
import com.imdroid.secapi.client.BeidouClient;
import com.imdroid.secapi.dto.GnssDevice;
import com.imdroid.common.util.ByteUtil;
import com.imdroid.sideslope.bd.Gga;
import com.imdroid.sideslope.bd.Rtcm1005;
import com.imdroid.sideslope.calc.GNSSDataCalcService;
import com.imdroid.sideslope.message.D331RtcmMessage;
import com.imdroid.sideslope.ntrip.UdpNtripServer;
import com.imdroid.sideslope.service.Device;
import com.imdroid.sideslope.service.DeviceService;
import com.imdroid.sideslope.server.DeviceChannel;
import com.imdroid.sideslope.server.OnlineChannels;
import com.imdroid.sideslope.service.DataPersistService;
import com.imdroid.sideslope.bd.RtcmGgaUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;


/**
 * @author Layton
 * @date 2023/2/2 20:49
 */
@Component
public class D331RtcmMessageExecutor implements Executor<D331RtcmMessage, Void> {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private DeviceService deviceService;
    @Autowired
    private BeidouClient beidouClient;
    @Autowired
    private DataPersistService dataPersistService;
    @Autowired
    UdpNtripServer ntripServer;
    @Autowired
    private GNSSDataCalcService gnssCalcService;

    // 添加一个成员变量用于追踪每个测站最后一次转发D300数据的时间
    private final Map<String, Long> lastD300ForwardTimeMap = new ConcurrentHashMap<>();
    private static final long D300_FORWARD_INTERVAL = 5000; // 5秒，单位毫秒

    @Override
    public Void execute(D331RtcmMessage message) {
        String id = message.getId();
        // 补齐tenantId
        Device deviceBs = deviceService.findByDeviceId(id);
        if(deviceBs == null || deviceBs.getOpMode() == GnssDevice.OP_MODE_UNUSE) return null;

        // 推送基站数据
        if(deviceBs.getOpMode() == GnssDevice.OP_MODE_USE) {
            if(deviceBs.getDeviceType()==GnssDevice.TYPE_REFERENCE_STATION) {
                byte[] forwardBytes = message.getSrcData();
                sendToRovers(deviceBs, forwardBytes);
            }
            else{
                ThreadManager.getFixedThreadPool().submit(() -> {
                    gnssCalcService.calcSingle(message, true);
                });
            }
        }

        // 如果30分钟内收到不到d3f0和d3f2，则根据UDP最后一个报文触发状态更新和统计
        if(deviceBs.getD3xxbytes()>0){
            LocalDateTime now = LocalDateTime.now();
            if(deviceBs.getLastRxTime().isBefore(now.minusMinutes(1)) &&
                (deviceBs.getLastD3f2Time() == null ||
                    deviceBs.getLastD3f2Time().isBefore(now.minusMinutes(30)))) {
                // new cycle
                logger.info("device {} rx {} d331 in a cycle while not d3f0f2",deviceBs.getDeviceId(),deviceBs.getD3xxCount());

                Device lastCycleDevice = new Device();
                lastCycleDevice.setDeviceId(deviceBs.getDeviceId());
                lastCycleDevice.setDeviceType(deviceBs.getDeviceType());
                lastCycleDevice.setTenantId(deviceBs.getTenantId());
                lastCycleDevice.setD341bytes(deviceBs.getD341bytes());
                lastCycleDevice.setD341Count(deviceBs.getD341Count());
                lastCycleDevice.setFixedNum(deviceBs.getFixedNum());
                lastCycleDevice.setFloatNum(deviceBs.getFloatNum());
                lastCycleDevice.setNoFixedAndFloatResult(deviceBs.getNoFixedAndFloatResult());
                lastCycleDevice.setLastRxTime(deviceBs.getLastRxTime());
                lastCycleDevice.setLastValidCalcDataTime(deviceBs.getLastValidCalcDataTime());
                lastCycleDevice.setSatelitesInUse(deviceBs.getSatelitesInUse());
                deviceBs.clearStat();
                ThreadManager.getFixedThreadPool().submit(() -> {
                    // 通知上线
                    try {
                        beidouClient.onDeviceActive(deviceBs.getDeviceId(), deviceBs.getTenantId());
                    } catch (Exception e) {
                        logger.error(e.toString());
                    }
                    dataPersistService.updateDeviceState(lastCycleDevice);
                });
            }
        }

        // update trx
        deviceBs.updateRx(message.getHeader(),message.getLen(),message.getPacketNum());
        // update gga
        Gga gga = message.getGga();
        if(gga != null) {
            deviceBs.updateSatelitesNum(gga.getSatellitesInUsed());
            deviceBs.setLatitude(gga.getLatitude());
            deviceBs.setLongitude(gga.getLongitude());
            deviceBs.setAltitude(gga.getAltitude());
        }

        // 添加NTRIP处理
        byte[] srcdata = message.getSrcData();
        String rtcm = ByteUtil.bytesToHexString(srcdata);
        sendToNtrip(id, rtcm);

        ThreadManager.getFixedThreadPool().submit(() -> {
            // 原始码流输出到日志文件 -- INFO 级别
            // 只有测站开了日志记录，或者消息来自基站，才将原码记录到日志文件
            if(deviceBs.getLoggingmode() == GnssDevice.LOGGING_MODE_FULL){
                logger.info("receive {} d331 message: {}", message.getId(), DataTypeUtil.getHexString(message.getSrcData()));
            }
        });

        return null;
    }

    private void sendToRovers(Device deviceBs, byte[] forwardBytes){
        List<Device> deviceList = deviceService.findByParentId(deviceBs.getDeviceId());
        //logger.debug("base station {} has {} rovers: ", message.getId(),deviceList.size());
        DeviceChannel deviceChannel = null;

        for (Device device : deviceList) {
            // 不是使用状态不推送
            if (device.getOpMode() != GnssDevice.OP_MODE_USE) continue;
            // 间隔推送
            if ((device.getModel()==GnssDevice.MODEL_G510) &&
                    (device.getFixedNum()>100) && (device.getGnssSampleRate()>1)
                    && (deviceBs.getD3xxCount()%device.getGnssSampleRate()) != 0) {
                //if(!UBXUtil.has1005(forwardBytes)) continue; //1005必推
                continue;
            }
            // 不在线的不推送
            String deviceId = device.getDeviceId();
            // 获取设备通道并发送数据
            if(device.getDataChannelType() == Device.CHANNEL_TYPE_UDP) {
                deviceChannel = OnlineChannels.INSTANCE.getDataChannel(deviceId);
            } else {
                deviceChannel = OnlineChannels.INSTANCE.getConfigChannel(deviceId);
            }
            if(deviceChannel == null || !deviceChannel.isOnline()){
                continue;
            }

            //推送
            if(deviceId.startsWith("2307")){
                // 处理2307型号的测站
                forwardBytes[2] = (byte) (forwardBytes[2] & 0x07);//兼容不带序号的测站
            }
            // 对所有测站类型为0的设备执行转发
            if (logger.isDebugEnabled()) {
                logger.debug("forward d331 rtcm from {} to device {}", deviceBs.getDeviceId(), deviceId);
            }
            ByteBuf buf = Unpooled.buffer();
            buf.writeBytes(forwardBytes);

            //推送策略：F9P基站（505）兼容博通（510）推送
            if ((deviceBs.getModel()==null || deviceBs.getModel() == GnssDevice.MODEL_G505) &&
                    (device.getModel()!=null && device.getModel()==GnssDevice.MODEL_G510)){
                buf = insert1005(deviceBs, deviceId, buf);
            }

            deviceChannel.writeAndFlush(buf);
        }
    }

    private ByteBuf insert1005(Device deviceBs, String deviceId, ByteBuf buf){
        long currentTime = System.currentTimeMillis();
        Long lastForwardTime = lastD300ForwardTimeMap.getOrDefault(deviceId, 0L);

        ByteBuf buf1005 = buf;

        // 检查是否满足10秒转发间隔，只有满足条件时才添加D300字符串
        if(currentTime - lastForwardTime >= D300_FORWARD_INTERVAL) {
            //logger.info("Adding D300 string for device: {}", deviceId);

            // 获取当前buf中的数据
            byte[] originalData = buf.array();
            String originalHex = ByteUtil.bytesToHexString(originalData);

            // 找到D300和D301的位置
            int d300Index = originalHex.indexOf("d300");
            int d301Index = originalHex.indexOf("d301");

            // 确定插入位置：如果两个都存在，取位置靠前的；如果只存在一个，就用那个位置
            int insertIndex = -1;
            if (d300Index != -1 && d301Index != -1) {
                // 两个都存在，取位置靠前的
                insertIndex = (d300Index < d301Index) ? d300Index : d301Index;
                //logger.info("Found both D300 and D301, D300 at {}, D301 at {}, will insert before position {}",
                ///       d300Index, d301Index, insertIndex);
            } else if (d300Index != -1) {
                insertIndex = d300Index;
                //logger.info("Found D300 at position {}", d300Index);
            } else if (d301Index != -1) {
                insertIndex = d301Index;
                //logger.info("Found D301 at position {}", d301Index);
            }

            if (insertIndex != -1) {
                // 创建新的buf
                buf1005 = Unpooled.buffer();
                // 写入D300/D301之前的数据
                buf1005.writeBytes(originalData, 0, insertIndex / 2);

                // 使用f9p坐标生成1005消息，并插入
                double[] ecef =new double[3];
                ecef[0] = deviceBs.getEcefx();
                ecef[1] = deviceBs.getEcefy();
                ecef[2] = deviceBs.getEcefz();
                String rtcm1005 = Rtcm1005.generateRtcm1005Hex(ecef, 0);
                if (rtcm1005 != null) {
                    // 写入RTCM 1005消息
                    byte[] rtcm1005Bytes = ByteUtil.hexStringTobyte(rtcm1005);
                    buf1005.writeBytes(rtcm1005Bytes);
                    // logger.info("Generated RTCM 1005 message for base station {}: {}", deviceBs.getDeviceId(), rtcm1005);
                } else {
                    //logger.warn("Failed to generate RTCM 1005 message for base station: {}", deviceBs.getDeviceId());
                }

                // 写入剩余的数据
                buf1005.writeBytes(originalData, insertIndex / 2, originalData.length - insertIndex / 2);

                // 添加日志，记录插入位置和完整数据
//                                    logger.info("Inserted RTCM 1005 message before position {}, complete data: {}",
//                                            insertIndex,
//                                            ByteUtil.bytesToHexString(buf.array()));
            }

            // 更新最后转发时间
            lastD300ForwardTimeMap.put(deviceId, currentTime);

            // 添加日志，记录测站转发的完整数据
            //logger.info("Forward data to device: {}, time: {}, complete data: {}",
            //       deviceId,
            //        LocalDateTime.now(),
            //        ByteUtil.bytesToHexString(buf.array()));
        }
        return buf1005;
    }

    private void sendToNtrip(String mountpoint, String hexData) {
        try {

            // 将原始字节转换为16进制字符串用于RTCM提取
            //String hexData = ByteUtil.bytesToHexString(rawData);
            //System.out.println(hexData);

            // 提取RTCM数据并发送到NtripServer,使用设备ID作为挂载点
            Optional.ofNullable(RtcmGgaUtil.getRtcms(hexData))
                    .ifPresent(rtcm -> {
                        //System.out.println("挂载点: " + mountpoint);
                        //System.out.println("RTCM数据: " + rtcm);
                        ntripServer.send(mountpoint, rtcm);
                    });
        } catch (Exception e) {
            logger.error("处理NTRIP数据失败, 挂载点: {}, 错误: {}", mountpoint, e.getMessage());
        }
    }

    @Override
    public Class<?> getMessageType() {
        return D331RtcmMessage.class;
    }
}
