package com.pwt.netty.handler;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.thread.NamedThreadFactory;
import cn.hutool.core.thread.ThreadUtil;
import com.pwt.Globals;
import com.pwt.StationService;
import com.pwt.cloud.params.ChangeFinishParam;
import com.pwt.cloud.utils.CloudUtil;
import com.pwt.common.utils.spring.SpringUtils;
import com.pwt.netty.ChargerServer;
import com.pwt.netty.MessageSender;
import com.pwt.netty.common.StringUtils;
import com.pwt.netty.common.charger.ChargerMessage;
import com.pwt.netty.common.charger.MessageData;
import com.pwt.netty.common.charger.RequestMessage;
import com.pwt.netty.common.charger.ResponseMessage;
import com.pwt.netty.common.charger.client.*;
import com.pwt.netty.common.charger.server.CalibrateTimeCommand;
import com.pwt.netty.common.charger.server.ServerResponseData;
import com.pwt.netty.common.plc.PlcCommand;
import com.pwt.netty.message2.*;
import com.pwt.web.domain.PwtChangeBatteryRecord;
import com.pwt.web.domain.PwtChargeRecordDto;
import com.pwt.web.domain.PwtHolderBatteryStateCache;
import com.pwt.web.service.IPwtChangeBatteryRecordService;
import com.pwt.web.service.IPwtHolderBatteryStateCacheService;
import com.pwt.web.service.impl.PwtChangeBatteryRecordServiceImpl;
import io.netty.channel.*;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.NetUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.InetSocketAddress;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.pwt.Globals.*;
import static com.pwt.netty.MessageSender.MSG_OPERATION_DATA;
import static com.pwt.netty.common.constants.Constants.NET_CONNECTION_STATE_OFFLINE;
import static com.pwt.netty.common.constants.Constants.NET_CONNECTION_STATE_ONLINE;

@Slf4j
@ChannelHandler.Sharable
public class ChargerServerHandler extends SimpleChannelInboundHandler<ChargerMessage<MessageData>> {

    private IPwtChangeBatteryRecordService changeBatteryRecordService = SpringUtils.getBean(PwtChangeBatteryRecordServiceImpl.class);

    private final ChargerServer server;

    public ChargerServerHandler(ChargerServer chargerServer) {
        this.server = chargerServer;
    }

    /**
     * key: ip:port
     * value: channel
     */
    private final Map<String, Channel> channels = new ConcurrentHashMap<>();

    /**
     * key: chargerId
     * value: channel
     */
    private final Map<String, Channel> chargerChannels = new ConcurrentHashMap<>();

    /**
     * key: ip:port
     * value: chargerId
     */
    private final Map<String, String> addressToChargerMap = new ConcurrentHashMap<>();

    /**
     * key: chargerId
     * value: gunsId
     */
    private final Map<String, Set<String>> chargerGunsMap = new ConcurrentHashMap<>();

    public static final Map<String, ChargerChargeStatusData> CHARGER_CHARGE_STATUS_DATA_MAP = new ConcurrentHashMap<>();

    // 电池状态信息
    public static final Map<String, BatteryStatusData> BATTERY_STATUS_DATA_MAP = new ConcurrentHashMap<>();

    // 电池基础信息
    public static final Map<String, BatteryStatusData> BATTERY_INFO_DATA_MAP = new ConcurrentHashMap<>();


    /**
     * 充电枪和仓位
     */

    public static final Map<String, Integer> GUN_AND_HOLDER_MAP = new ConcurrentHashMap<>();

    public static final Map<Integer, String> HOLDER_AND_GUN_MAP = new ConcurrentHashMap<>();

    private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(1, Globals.ACTUAL_HOLDER_COUNT, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new NamedThreadFactory("ChargerCompleteWorker", false));

    static {
//        GUN_AND_HOLDER_MAP.put("GHLN07149CDJ21", 2);
//        GUN_AND_HOLDER_MAP.put("GHLN07149CDJ41", 4);
//        GUN_AND_HOLDER_MAP.put("GHLN07149CDJ11", 3);
//        GUN_AND_HOLDER_MAP.put("GHLN07149CDJ13", 1);
//
//        HOLDER_AND_GUN_MAP.put(2, "GHLN07149CDJ21");
//        HOLDER_AND_GUN_MAP.put(4, "GHLN07149CDJ41");
//        HOLDER_AND_GUN_MAP.put(3, "GHLN07149CDJ11");
//        HOLDER_AND_GUN_MAP.put(1, "GHLN07149CDJ13");
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        channels.put(NetUtil.toSocketAddressString((InetSocketAddress) channel.remoteAddress()), channel);
        if (log.isInfoEnabled()) {
            log.info("The connection of " + channel.remoteAddress() + " -> " + channel.localAddress() + " is established.");
        }
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        // 如果channel断开
        // 获取ip:port
        String address = NetUtil.toSocketAddressString((InetSocketAddress) channel.remoteAddress());
        channels.remove(address);
        // 通过ip:port获取chargerId
        String chargerId = addressToChargerMap.remove(address);
        // 从chargerChannels中移除
        if (StringUtils.isNotEmpty(chargerId)) {
            chargerChannels.remove(chargerId);
            refreshChargerUiData(chargerId, null);

            // 移除charger对应的所有枪号
            Set<String> guns = chargerGunsMap.get(chargerId);
            if (guns != null && guns.size() > 0) {
                for (String gunId :
                        guns) {
                    // 断线时移除充电机状态信息
                    CHARGER_CHARGE_STATUS_DATA_MAP.remove(gunId);
                    refreshChargerGun(gunId, null);
                }
                guns.clear();
            }
        }
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        Channel channel = ctx.channel();
        if (evt instanceof IdleStateEvent) {
            log.info("IdleStateEvent triggered, close channel {}", channel);
            channel.close();
        }
        super.userEventTriggered(ctx, evt);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error(cause.getMessage(), cause);
        // 发生错误，关闭channel
        ctx.channel().close();
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ChargerMessage msg) throws Exception {

        String gunId = msg.getChargerGunId();
        // 判断这个枪是否是主枪，或者应急充电枪
        if (!GUN_INDEX_MAP.containsKey(gunId)) {
            if (msg.getType() == 32) {
                // 不回复0x20消息，后续消息会阻塞
            } else {
                return;
            }
        }
        Integer holderIndex = GUN_AND_HOLDER_MAP.get(gunId);
        if (holderIndex == null) {
            // 充电机数据记录
            if (msg.getType() == 32 || msg.getType() == 16) {
                // 不回复0x20消息，后续消息会阻塞
                if ((emergencyChargerId + "3").equals(gunId) || (emergencyChargerId + "4").equals(gunId)) {
                    holderIndex = -1;
                } else {
                    holderIndex = 999;
                }
            } else {
                holderIndex = 999;
            }
        }
        String chargerAddress = msg.getChargerAddress();
        String chargerId = msg.getChargerAddress();
        Channel channel = ctx.channel();
//        if (log.isInfoEnabled()) {
//            log.info("Received message: {}", msg);
//        }

        switch (msg.getType()) {
            // region client request
            case 0x10:
                // 如果没有保存channel信息,则保存
                if (!chargerChannels.containsKey(chargerAddress)) {
                    chargerChannels.put(chargerAddress, channel);
                    addressToChargerMap.put(NetUtil.toSocketAddressString((InetSocketAddress) channel.remoteAddress()),
                            chargerAddress);
                    // 第一次，发送校时请求
                    channel.writeAndFlush(buildCalibrateTimeRequest(chargerAddress, msg.getGunNo()))
                            .addListener(new ChannelFutureListener() {
                                @Override
                                public void operationComplete(ChannelFuture channelFuture) throws Exception {
                                    if (channelFuture.cause() != null) {
                                        log.error("校时请求发送失败", channelFuture.cause());
                                    }
                                }
                            });
                }

                Set<String> gunSet = chargerGunsMap.get(chargerAddress);
                gunId = msg.getChargerGunId();
                if (gunSet == null) {
                    gunSet = ConcurrentHashMap.newKeySet();
                    gunSet.add(gunId);
                    chargerGunsMap.put(chargerAddress, gunSet);
                } else {
                    gunSet.add(gunId);
                }

                sendResponse(channel, buildServerResponse((RequestMessage) msg));

                ChargerChargeStatusData chargerChargeStatusData1 = (ChargerChargeStatusData) msg.getMessageData();
                if (holderIndex != null && holderIndex == -1) {// 应急充电处理
                    Globals.emergencyHolderBatteryCacheMap.put(holderIndex, new PwtHolderBatteryStateCache(holderIndex,
                            chargerChargeStatusData1.getBatteryNumber(), 282, chargerChargeStatusData1.getSOC()));
                    CHARGER_CHARGE_STATUS_DATA_MAP.put(gunId, chargerChargeStatusData1);
                    // 刷新充电机ui
                    refreshChargerGun(gunId, chargerChargeStatusData1);
                    break;
                }
                if (holderIndex != null) {
                    PwtHolderBatteryStateCache cache = Globals.holderBatteryStateCacheMap.get(holderIndex);
                    log.error("【ChargerService】仓位：{},缓存信息：{},枪号：{},充电机信息：{}", holderIndex, cache, gunId, chargerChargeStatusData1);
                    if (chargerChargeStatusData1.getChargingStatus() == 0 || (chargerChargeStatusData1.getChargingStatus() == 2 && (holderIndex == 1 || holderIndex == 3))) {
                        // 待机且没有电池缓存信息
                        if (cache != null && cache.getBatterySn() != null) {
                            chargerChargeStatusData1.setBatteryNumber(cache.getBatterySn());
                            if (chargerChargeStatusData1.getChargingStatus() == 0) {
                                chargerChargeStatusData1.setSOC(cache.getSoc());
                            }
                        }
                    }
                    // 电池编码
                    if (holderIndex == 1 || holderIndex == 3) {
                        if (cache != null && cache.getBatterySn() != null) {
                            if (chargerChargeStatusData1.getChargingStatus() != 1) {
                                chargerChargeStatusData1.setBatteryNumber(cache.getBatterySn());
                            }
                        }
                    }
                    //故障时缓存保存到数据库
                    if (chargerChargeStatusData1.getChargingStatus() == 3) {
                        log.info("cache：{}", cache);
                        IPwtHolderBatteryStateCacheService cacheService = SpringUtils.getBean(IPwtHolderBatteryStateCacheService.class);
                        cacheService.updateBatteryStateCacheRecord(cache);
                    }
                    // 电量90%多时落库一次
                    if (chargerChargeStatusData1.getSOC() > 90 && chargerChargeStatusData1.getSOC() < 100) {
                        // 写一次数据库
                        // 刷新数据库缓存记录(解决应用重启后电池编码显示不正确的问题)
                        ThreadUtil.execute(() -> {
                            IPwtHolderBatteryStateCacheService cacheService = SpringUtils.getBean(IPwtHolderBatteryStateCacheService.class);
                            cacheService.updateBatteryStateCacheRecord(cache);
                        });
                    }
                }
                CHARGER_CHARGE_STATUS_DATA_MAP.put(gunId, chargerChargeStatusData1);
                // 刷新充电机ui
                refreshChargerGun(gunId, chargerChargeStatusData1);
//                List<ChargerGun> chargerGuns = uiData.getChargerGuns();
                // 更新换电记录(换电控制器缺失数据补齐, 需要注意的是一拖二的充电机1，3枪返回的枪号是同一个，即在运行中的那个)
                if (Globals.tempBatteryPlace != null && Globals.tempBatteryPlace.equals(gunId) && chargerChargeStatusData1.getChargingStatus() == 1 && chargerChargeStatusData1.getSOC() > 0) {
//                    PwtChangeBatteryRecord record = changeBatteryRecordService.getLastestRecord();
                    if (record != null && record.getVin() != null) {
                        log.info("调用云平台开始充电接口");
                        ThreadUtil.execute(() -> CloudUtil.powerStart(chargerChargeStatusData1.getBatteryNumber(), Globals.tempBatteryPlace));

                        log.error("插入换电记录:{}", record);
                        record.setChangeBatterySn(chargerChargeStatusData1.getBatteryNumber());
                        double putPower = (long) 282 * chargerChargeStatusData1.getSOC() / 100D;
                        record.setChangeBatteryPower(BigDecimal.valueOf(putPower));
                        changeBatteryRecordService.insertPwtChangeBatteryRecord(record);
                        record.setVin(null);
                        int putNum = ChargerServerHandler.GUN_AND_HOLDER_MAP.get(Globals.tempBatteryPlace);
                        // 刷新缓存
                        Globals.holderBatteryStateCacheMap.put(putNum,
                                new PwtHolderBatteryStateCache(putNum,
                                        chargerChargeStatusData1.getBatteryNumber(), 282, chargerChargeStatusData1.getSOC()));
                        Globals.tempBatteryPlace = null;
                        // 发送云平台换电结束
                        ThreadUtil.execute(() -> {
                            ChangeFinishParam param = new ChangeFinishParam();
                            param.setVehicleCode(record.getVehicleNo());
                            param.setDownBatteryCode(chargerChargeStatusData1.getBatteryNumber());
                            param.setDownBatteryLevel(chargerChargeStatusData1.getSOC() / 100D * 350);
                            param.setUpBatteryCode(record.getExchangeBatterySn());
                            param.setUpBatteryLevel(record.getExchangeBatteryPower().doubleValue());
                            CloudUtil.changeFinish(param);
                        });
                    }
                }
                break;
            case 0x11:
                BatteryStatusData batteryStatusData = (BatteryStatusData) msg.getMessageData();
                BATTERY_STATUS_DATA_MAP.put(gunId, batteryStatusData);
                break;
            case 0x12:
                // todo 故障
                break;
            case 0x14:
                BatteryOtherData messageData = (BatteryOtherData) msg.getMessageData();
                resolveBatteryStatusData(gunId, messageData);
                break;
            case 0x15:
                // 充电枪
                gunId = msg.getChargerGunId();
                if (GUN_AND_HOLDER_MAP.containsKey(gunId)) {
                    ChargerStatusData chargerStatusData = (ChargerStatusData) msg.getMessageData();
                    // 刷新充电机ui
                    refreshChargerUiData(chargerId, chargerStatusData);
                }
                break;
            case 0x20:
                //充电完成或停止充电触发
                ChargeCompleteData chargeCompleteData = (ChargeCompleteData) msg.getMessageData();
                PwtHolderBatteryStateCache cache = null;
                if (holderIndex == -1) {
                    cache = Globals.emergencyHolderBatteryCacheMap.get(holderIndex);
                } else {
                    cache = Globals.holderBatteryStateCacheMap.get(holderIndex);
                }
                if (cache == null) {
                    log.info("充电完成，但找不到电池缓存信息");
                } else {
                    log.info("充电完成，准备写入记录");
                    EXECUTOR.execute(new ChargeCompleteTask(gunId, chargeCompleteData, cache));
                    //云平台发送充电完成
                    final PwtHolderBatteryStateCache finalCache = cache;
                    // 应急充电
                    if (emergencyChargerId.equals(msg.getChargerAddress()) && holderIndex == -1) {
                        chargeFinishParam.setLevel(chargeCompleteData.getCurrentChargeData());
                        ThreadUtil.execute(() -> CloudUtil.chargeFinish(chargeFinishParam));
                    } else {
                        ThreadUtil.execute(() -> CloudUtil.powerFinish(finalCache, chargeCompleteData.getCurrentChargeData()));
                    }
                }
                final ChargerServer.DelayStartInfo delayStartInfo = ChargerServer.delayStartInfo;
                if (delayStartInfo != null && msg.getChargerGunId().equals(delayStartInfo.getTriggerGunId())) {
                    server.newDelayTask(delayStartInfo);
                }

                sendResponse(channel, buildServerResponse((RequestMessage) msg));
                break;
            case 0x30:
                sendResponse(channel, buildServerResponse((RequestMessage) msg));
                break;
            // endregion

            // region client response
            case 0x51:
                ChargerPromiseHolder.completePromise(new ChargerPromiseHolder.ChargerRequestKey(msg.getChargerGunId(), msg.getType() - 1, msg.getStreamId()),
                        (ResponseMessage) msg);
                break;
            // 其他响应不做处理
//            case 0x71:
//                break;
//            case 0x81:
//                break;
//            case 0x91:
//                break;
            // endregion
            default:
        }
    }


    private class ChargeCompleteTask implements Runnable {

        private final String gunId;
        private final ChargeCompleteData data;
        private final PwtHolderBatteryStateCache cache;

        public ChargeCompleteTask(String gunId, ChargeCompleteData data, PwtHolderBatteryStateCache cache) {
            this.gunId = gunId;
            this.data = data;
            this.cache = cache;
        }

        @Override
        public void run() {
            try {
                log.info("开始写入充电");
                PwtChargeRecordDto dto = new PwtChargeRecordDto();
                dto.setBatterySn(cache.getBatterySn());
                dto.setChargerNo(gunId);
                // 需要查询流水号和枪号，如果存在，则是应急充电
                if ((emergencyChargerId + "3").equals(gunId) || (emergencyChargerId + "4").equals(gunId)) {
                    dto.setChargeMode(2);
                } else {
                    dto.setChargeMode(1);
                }
                dto.setChargeQuantity(BigDecimal.valueOf(data.getCurrentChargeData()));
                dto.setChargeBeginTime(LocalDateTime.ofInstant(Instant.ofEpochSecond(data.getChargeStartTime()), ZoneId.systemDefault()));
                dto.setChargeEndTime(LocalDateTime.ofInstant(Instant.ofEpochSecond(data.getChargeEndTime()), ZoneId.systemDefault()));
                dto.setChargeDuration((int) Duration.between(dto.getChargeBeginTime(), dto.getChargeEndTime()).toMinutes());
                dto.setStartSoc(data.getStartSOC());
                dto.setEndSoc(data.getEndSOC());
                dto.setStopReason(data.getStopReason());
                server.getRecordService().insertPwtChargeRecord(dto);
                log.info("写入充电记录成功, record:{}", dto);
                // 刷新UI
                OperationData operationData = uiData.getOperationData();
                Globals.OP_EXCHANGE_POWER.set(operationData.getTotalExchangePower() + ((dto.getEndSoc() - dto.getStartSoc()) * 282 / 100));
                operationData.setTotalExchangePower(Globals.OP_EXCHANGE_POWER.get());
                uiData.setOperationData(operationData);
                MessageSender.send(MSG_OPERATION_DATA, operationData);
            } catch (Exception e) {
                log.info("写入充电记录失败, gun:{}, data:{}, cache:{}", gunId, data, cache, e);
            }
        }
    }

    private static void resolveBatteryStatusData(String gunId, BatteryOtherData batteryOtherData) {
        // 查找仓位号，并判断是否为主枪
        Integer holderIndex = GUN_AND_HOLDER_MAP.get(gunId);
        if (holderIndex == null) {
            // 没有仓位，也就没有电池
            return;
        }


        // plc是否在线
        if (Globals.plcStateInfo != null) {
            boolean[] batteryExists = Globals.plcStateInfo.getBatteryExists();
            // plc在线，且没有电池，不做处理
            if (!batteryExists[holderIndex - 1]) {
                return;
            }
        }

        // 充电机状态
        ChargerChargeStatusData chargerChargeStatusData2 = CHARGER_CHARGE_STATUS_DATA_MAP.get(gunId);
        BatteryStatusData batteryStatusData = BATTERY_STATUS_DATA_MAP.get(gunId);

        if (chargerChargeStatusData2 == null || batteryStatusData == null) {
            return;
        }
        String holderId = holderIndex + "";
        BatteryInfo batteryInfo = null;
        BatteryStateInfo batteryStateInfo;
        if (chargerChargeStatusData2.getChargingStatus() == 0) {
            batteryStateInfo = new BatteryStateInfo(holderId);
            if ("1".equals(holderId) || "3".equals(holderId)) {
                Optional<BatteryInfo> newest = uiData.getBatteryInfos().stream().filter(e -> holderId.equals(e.getHolderId())).max(Comparator.comparingLong(BaseData::getTimestamp));
                if (newest.isPresent()) {
                    batteryStateInfo.setBatterySn(newest.get().getBatterySn());
                } else {
                    batteryStateInfo.setBatterySn(chargerChargeStatusData2.getBatteryNumber());
                }
            } else {
                batteryStateInfo.setBatterySn(chargerChargeStatusData2.getBatteryNumber());
            }
            // 判断之前是否有数据，有数据则不需要用缓存数据渲染
            if (!Globals.HOLDER_BATTERY_INFO.containsKey(holderId)) {
                if (Globals.holderBatteryStateCacheMap.containsKey(holderIndex)) {
                    batteryInfo = new BatteryInfo(holderId);
                    batteryInfo.setBatterySn(batteryStateInfo.getBatterySn());
                    PwtHolderBatteryStateCache cache = Globals.holderBatteryStateCacheMap.get(holderIndex);
                    if (cache != null) {
                        // 使用缓存数据渲染
                        batteryInfo.setRatedCapacity((double) cache.getRatedCapacity());
                    }
                }
            }

            refreshBatteryData(holderId, batteryInfo, batteryStateInfo);
        } else {
            // 从充电机状态读取 电池sn
            String batterySn = chargerChargeStatusData2.getBatteryNumber();
//            if (chargerChargeStatusData2.getChargingStatus() != 1) {
//                if ("1".equals(holderId) || "3".equals(holderId)) {
//                    Optional<BatteryInfo> newest = uiData.getBatteryInfos().stream().filter(e -> holderId.equals(e.getHolderId())).max(Comparator.comparingLong(BaseData::getTimestamp));
//                    if (newest.isPresent()) {
//                        batterySn = newest.get().getBatterySn();
//                    }
//                }
//            }
            batteryInfo = new BatteryInfo(holderId);
            batteryInfo.setBatterySn(batterySn);
            batteryInfo.setBatteryType(batteryStatusData.getBatteryTypeStr());
            batteryInfo.setBatteryFactory(batteryStatusData.getBatteryFactory());

            int batteryRatedCapacity = batteryStatusData.getBatteryRatedCapacity();
            int batteryRatedVoltage = batteryStatusData.getBatteryRatedVoltage();

            // 计算额定容量
            int powerCap = 282;
            batteryInfo.setRatedCapacity((double) powerCap);
            batteryInfo.setRatedVoltage((double) batteryStatusData.getBatteryRatedVoltage());
            // 系统总能量
            //           totalCapacity;
            //            插枪充电次数 chargeCount1;
            // 换电充电次数
            batteryInfo.setChargeCount2(chargerChargeStatusData2.getChargingTimes());
            // 电池总电压  batteryTotalVoltage;
            // 电池剩余容量 residualEnergy
            //   最高充电电压  maxChargeVoltage


            batteryStateInfo = new BatteryStateInfo(holderId);
            batteryStateInfo.setBatterySn(batterySn);
            batteryStateInfo.setState(1);
            batteryStateInfo.setSOC((double) chargerChargeStatusData2.getSOC());
            List<ChargerGun> chargerGuns = uiData.getChargerGuns();
            ChargerGun chargerGun = chargerGuns.stream().filter(e -> e.getHolderId() != null && e.getHolderId().equals(holderId))
                    .collect(Collectors.toList()).get(0);
            batteryStateInfo.setBatteryVoltage(chargerGun.getVoltage());
            //极值信息
            batteryStateInfo.setCellMaxVoltage(batteryStatusData.getCellMaxVoltage() * 0.01);
            batteryStateInfo.setCellMaxVoltageNum(batteryStatusData.getCellMaxVoltageNo());
            batteryStateInfo.setCellMinVoltage(batteryStatusData.getCellMinVoltage() * 0.01);
            batteryStateInfo.setCellMinVoltageNum(batteryStatusData.getCellMinVoltageNo());
            batteryStateInfo.setCellMaxTemperature((double) batteryStatusData.getMaxBatteryTemperature() - 40);
            batteryStateInfo.setCellMaxTemperatureNum(batteryStatusData.getMaxTemperatureCheckPointNo());
            batteryStateInfo.setCellMinTemperature((double) batteryStatusData.getMinBatteryTemperature() - 40);
            batteryStateInfo.setCellMinTemperatureNum(batteryStatusData.getMinTemperatureCheckPointNo());

            refreshBatteryCache(holderIndex, chargerChargeStatusData2, batterySn, powerCap);
            refreshBatteryData(holderId, batteryInfo, batteryStateInfo);
        }

    }

    private static synchronized void refreshBatteryData(String holderId, BatteryInfo batteryInfo, BatteryStateInfo batteryStateInfo) {
        Map<String, Object> data = new HashMap<>();
        if (batteryInfo != null) {
            //填充电池温度和极值信息
            setBatteryStateInfoParam(holderId, batteryStateInfo);
            Globals.uiData.getBatteryInfos().add(batteryInfo);
            Globals.HOLDER_BATTERY_INFO.put(holderId, batteryInfo);
            data.put(MessageSender.MSG_BATTER_INFOS, Collections.singletonList(batteryInfo));
        }

        Globals.uiData.getBatteryStates().add(batteryStateInfo);
        Globals.HOLDER_BATTERY_STATE_INFO.put(holderId, batteryStateInfo);
        data.put(MessageSender.MSG_BATTER_STATES, Collections.singletonList(batteryStateInfo));
        MessageSender.send(data);
    }

    private static void setBatteryStateInfoParam(String holderId, BatteryStateInfo batteryStateInfo) {
        // 1、3位是一拖二
        if (holderId == null) {
            return;
        }
        int holderIdInt = Integer.parseInt(holderId);
        int holderTemp = holderIdInt == 3 ? 1 : (holderIdInt > 3 ? holderIdInt - 1 : holderIdInt);
        Charger charger = uiData.getChargers().stream().filter(e -> holderTemp == e.getIndex())
                .collect(Collectors.toList()).get(0);
        if (Objects.nonNull(charger) && CollUtil.isNotEmpty(charger.getChargeModule())) {
            //电池温度
            double average = charger.getChargeModule().stream().mapToDouble(ChargerModule::getTemperature)
                    .filter(e -> e > -40 && e < 300).summaryStatistics().getAverage();
            batteryStateInfo.setBatteryTemperature(average);
//            List<BatteryStateInfo> collect = uiData.getBatteryStates().stream()
//                    .filter(e -> e.getHolderId().equals(holderId))
//                    .sorted((x, y) -> {
//                        if (x.getTimestamp() == y.getTimestamp()) {
//                            return 0;
//                        } else {
//                            return x.getTimestamp() > y.getTimestamp() ? -1 : 1;
//                        }
//                    }).collect(Collectors.toList());
//            BatteryStateInfo oldData = null;
//            if (CollUtil.isNotEmpty(collect)) {
//                oldData = collect.get(0);
//            }
//            //电压极值
//            if (oldData == null || (Objects.isNull(oldData.getCellMaxVoltage()) || oldData.getCellMaxVoltage() < batteryStateInfo.getBatteryVoltage())) {
//                batteryStateInfo.setCellMaxVoltage(batteryStateInfo.getCellMaxVoltage());
//                batteryStateInfo.setCellMaxVoltageNum(batteryStateInfo.getCellMaxVoltageNum());
//            } else {
//                batteryStateInfo.setCellMaxVoltage(oldData.getCellMaxVoltage());
//                batteryStateInfo.setCellMaxVoltageNum(oldData.getCellMaxVoltageNum());
//            }
//            if (oldData == null || (Objects.isNull(oldData.getCellMinVoltage()) || oldData.getCellMinVoltage() > batteryStateInfo.getBatteryVoltage())) {
//                batteryStateInfo.setCellMinVoltage(batteryStateInfo.getCellMinVoltage());
//                batteryStateInfo.setCellMinVoltageNum(batteryStateInfo.getCellMinVoltageNum());
//            } else {
//                batteryStateInfo.setCellMinVoltage(oldData.getCellMinVoltage());
//                batteryStateInfo.setCellMinVoltageNum(oldData.getCellMinVoltageNum());
//            }
//            //温度极值
//            Optional<ChargerModule> max1 = charger.getChargeModule().stream().filter(e -> e.getTemperature() > -40 && e.getTemperature() < 300)
//                    .max(Comparator.comparing(ChargerModule::getTemperature));
//            if (max1.isPresent()) {
//                ChargerModule chargerModule = max1.get();
//                if (Objects.isNull(batteryStateInfo.getCellMaxTemperature()) || Objects.isNull(oldData) || oldData.getCellMaxTemperature() < chargerModule.getTemperature()) {
//                    batteryStateInfo.setCellMaxTemperature(chargerModule.getTemperature());
//                } else {
//                    batteryStateInfo.setCellMaxTemperature(oldData.getCellMaxTemperature());
//                }
//            } else {
//                if (Objects.nonNull(oldData)) {
//                    batteryStateInfo.setCellMaxTemperature(oldData.getCellMaxTemperature());
//                }
//            }
//            Optional<ChargerModule> min1 = charger.getChargeModule().stream().filter(e -> e.getTemperature() > -40 && e.getTemperature() < 300)
//                    .min(Comparator.comparing(ChargerModule::getTemperature));
//            if (min1.isPresent()) {
//                ChargerModule chargerModule = min1.get();
//                if (Objects.isNull(batteryStateInfo.getCellMinTemperature()) || Objects.isNull(oldData) || oldData.getCellMinTemperature() > chargerModule.getTemperature()) {
//                    batteryStateInfo.setCellMinTemperature(chargerModule.getTemperature());
//                } else {
//                    batteryStateInfo.setCellMinTemperature(oldData.getCellMinTemperature());
//                }
//            } else {
//                if (Objects.nonNull(oldData)) {
//                    batteryStateInfo.setCellMinTemperature(oldData.getCellMinTemperature());
//                }
//            }
        }
    }

    public static synchronized void removeBatteryData(int holderIndex) {
        Globals.holderBatteryStateCacheMap.remove(holderIndex);
        String holderId = holderIndex + "";
        BatteryInfo batteryInfo = Globals.HOLDER_BATTERY_INFO.get(holderId);
        BatteryInfo removeInfo;
        Map<String, Object> data = new HashMap<>();

//        移除,从Globals.uiData;
        if (batteryInfo != null) {
            removeInfo = new BatteryInfo(holderId);
            removeInfo.setIsDelete(true);
            Globals.uiData.getBatteryInfos().remove(batteryInfo);
            Globals.HOLDER_BATTERY_INFO.remove(holderId);

            data.put(MessageSender.MSG_BATTER_INFOS, Collections.singletonList(removeInfo));
        }

        BatteryStateInfo batteryStateInfo = Globals.HOLDER_BATTERY_STATE_INFO.get(holderId);
        BatteryStateInfo removeState;
        if (batteryStateInfo != null) {
            removeState = new BatteryStateInfo(holderId);
            removeState.setIsDelete(true);
            Globals.uiData.getBatteryStates().remove(batteryStateInfo);
            Globals.HOLDER_BATTERY_STATE_INFO.remove(holderId);
            data.put(MessageSender.MSG_BATTER_STATES, Collections.singletonList(removeState));
        }

        if (data.size() != 0) {
            MessageSender.send(data);
            log.info("移除电仓数据:{}", data);
        }
    }


    private static void refreshBatteryCache(Integer holderIndex, ChargerChargeStatusData chargerChargeStatusData2, String batterySn, int powerCap) {
        PwtHolderBatteryStateCache cache = new PwtHolderBatteryStateCache();
        cache.setUpdateTime(LocalDateTime.now());
        cache.setBatterySn(batterySn);
        cache.setSoc(chargerChargeStatusData2.getSOC());
        cache.setRatedCapacity(powerCap);
        cache.setHolderId(holderIndex);
        log.info("更新cache:{}", cache);
        Globals.holderBatteryStateCacheMap.put(holderIndex, cache);
    }

    private static void refreshChargerGun(String gunId, ChargerChargeStatusData chargerChargeStatusData) {

        Integer index = GUN_INDEX_MAP.get(gunId);
        if (index == null) {
            return;
        }

        ChargerGun oldGun = Globals.uiData.getChargerGuns().get(index - 1);
        if (oldGun == null) {
            return;
        }

        ChargerGun newGun = new ChargerGun();
        newGun.setChargerId(oldGun.getChargerId());
        newGun.setId(oldGun.getId());
        newGun.setIndex(oldGun.getIndex());
        newGun.setName(oldGun.getName());
        newGun.setHolderId(oldGun.getHolderId());

        if (chargerChargeStatusData != null) {
            newGun.setChargeTime(chargerChargeStatusData.getChargedMinutes());
            newGun.setSurplusTime(chargerChargeStatusData.getRemainingChargingTime());
            newGun.setCurrent(BigDecimal.valueOf(chargerChargeStatusData.getOutputCurrent()).setScale(2, RoundingMode.HALF_DOWN).doubleValue());
            newGun.setDemandCurrent(BigDecimal.valueOf(chargerChargeStatusData.getDemandCurrent()).setScale(2, RoundingMode.HALF_DOWN).doubleValue());
            newGun.setVoltage(BigDecimal.valueOf(chargerChargeStatusData.getOutputVoltage()).setScale(2, RoundingMode.HALF_DOWN).doubleValue());
            newGun.setDemandVoltage(BigDecimal.valueOf(chargerChargeStatusData.getDemandVoltage()).setScale(2, RoundingMode.HALF_DOWN).doubleValue());
            newGun.setSOC(chargerChargeStatusData.getSOC());

            int chargingStatus = chargerChargeStatusData.getChargingStatus();
            ChargerStateInfo chargerStateInfo = new ChargerStateInfo();
            if (chargingStatus == 0 || chargingStatus == 2) {
                if (chargerChargeStatusData.getSOC() == 100) {
                    // 充满
                    chargerStateInfo.setState(3);
                } else {
                    // 待机
                    chargerStateInfo.setState(1);
                }
            } else if (chargingStatus == 1) {
                if (chargerChargeStatusData.getSOC() == 100) {
                    // 充满
                    chargerStateInfo.setState(3);
                } else {
                    // 充电
                    chargerStateInfo.setState(2);
                }
            } else {
                // 故障
                chargerStateInfo.setState(4);
            }
            newGun.setState(chargerStateInfo);
        }


        Globals.uiData.getChargerGuns().set(index - 1, newGun);
        int state = newGun.getState().getState();
        Set<Map.Entry<Integer, String>> entries = HOLDER_AND_GUN_MAP.entrySet();
        for (Map.Entry<Integer, String> entry : entries) {
            Integer key = entry.getKey();
            String value = entry.getValue();
            if (key == 1 && value.equals(newGun.getId())) {
                plcClient.sendCommand(PlcCommand.W_BatteryCharging, state == 2);
            }
            if (key == 2 && value.equals(newGun.getId())) {
                plcClient.sendCommand(PlcCommand.W_BatteryCharging2, state == 2);
            }
            if (key == 3 && value.equals(newGun.getId())) {
                plcClient.sendCommand(PlcCommand.W_BatteryCharging3, state == 2);
            }
        }
        MessageSender.send(MessageSender.MSG_GUNS, Collections.singletonList(newGun));

    }

    private static void refreshChargerUiData(String chargeId, ChargerStatusData chargerStatusData) {

        Integer index = CHARGE_INDEX_MAP.get(chargeId);
        if (index == null) {
            return;
        }

        Charger old = Globals.uiData.getChargers().get(index - 1);
        Charger charger = new Charger();
        charger.setId(old.getId());
        charger.setIndex(old.getIndex());
        charger.setName(old.getName());

        List<ChargerModule> chargerModules = new ArrayList<>(Globals.ACTUAL_CHARGER_MODULE_COUNT);
        if (chargerStatusData != null) {
            charger.setCommunicationState(NET_CONNECTION_STATE_ONLINE);
            int status = 0;
            int modIndex = 0;
            // module 1
            ChargerModule chargerModule = new ChargerModule();
            chargerModule.setIndex(++modIndex);
            chargerModule.setStatus(status);
            chargerModule.setOutVoltage(chargerStatusData.getStatus1() / 10D);
            chargerModule.setOutCurrent(chargerStatusData.getStatus2() / 10D);
            chargerModule.setTemperature(chargerStatusData.getStatus3() - 40);
            chargerModules.add(chargerModule);

            // module 2
            chargerModule = new ChargerModule();
            chargerModule.setIndex(++modIndex);
            chargerModule.setStatus(status);
            chargerModule.setOutVoltage(chargerStatusData.getStatus4() / 10D);
            chargerModule.setOutCurrent(chargerStatusData.getStatus5() / 10D);
            chargerModule.setTemperature(chargerStatusData.getStatus6() - 40);
            chargerModules.add(chargerModule);

            // module 3
            chargerModule = new ChargerModule();
            chargerModule.setIndex(++modIndex);
            chargerModule.setStatus(status);
            chargerModule.setOutVoltage(chargerStatusData.getStatus7() / 10D);
            chargerModule.setOutCurrent(chargerStatusData.getStatus8() / 10D);
            chargerModule.setTemperature(chargerStatusData.getStatus9() - 40);
            chargerModules.add(chargerModule);

            // module 4
            chargerModule = new ChargerModule();
            chargerModule.setIndex(++modIndex);
            chargerModule.setStatus(status);
            chargerModule.setOutVoltage(chargerStatusData.getStatus10() / 10D);
            chargerModule.setOutCurrent(chargerStatusData.getStatus11() / 10D);
            chargerModule.setTemperature(chargerStatusData.getStatus12() - 40);
            chargerModules.add(chargerModule);

            // module 5
            chargerModule = new ChargerModule();
            chargerModule.setIndex(++modIndex);
            chargerModule.setStatus(status);
            chargerModule.setOutVoltage(chargerStatusData.getStatus13() / 10D);
            chargerModule.setOutCurrent(chargerStatusData.getStatus14() / 10D);
            chargerModule.setTemperature(chargerStatusData.getStatus15() - 40);
            chargerModules.add(chargerModule);

            // module 6
            chargerModule = new ChargerModule();
            chargerModule.setIndex(++modIndex);
            chargerModule.setStatus(status);
            chargerModule.setOutVoltage(chargerStatusData.getStatus16() / 10D);
            chargerModule.setOutCurrent(chargerStatusData.getStatus17() / 10D);
            chargerModule.setTemperature(chargerStatusData.getStatus18() - 40);
            chargerModules.add(chargerModule);


            // module 7
            chargerModule = new ChargerModule();
            chargerModule.setIndex(++modIndex);
            chargerModule.setStatus(status);
            chargerModule.setOutVoltage(chargerStatusData.getStatus19() / 10D);
            chargerModule.setOutCurrent(chargerStatusData.getStatus20() / 10D);
            chargerModule.setTemperature(chargerStatusData.getStatus21() - 40);
            chargerModules.add(chargerModule);


            // module 8
            chargerModule = new ChargerModule();
            chargerModule.setIndex(++modIndex);
            chargerModule.setStatus(status);
            chargerModule.setOutVoltage(chargerStatusData.getStatus22() / 10D);
            chargerModule.setOutCurrent(chargerStatusData.getStatus23() / 10D);
            chargerModule.setTemperature(chargerStatusData.getStatus24() - 40);
            chargerModules.add(chargerModule);
        } else {
            charger.setCommunicationState(NET_CONNECTION_STATE_OFFLINE);

            for (int i = 1; i <= Globals.ACTUAL_CHARGER_MODULE_COUNT; i++) {
                ChargerModule m = new ChargerModule();
                m.setIndex(i);
                chargerModules.add(m);
            }
        }

        charger.setChargeModule(chargerModules);

        Globals.uiData.getChargers().set(index - 1, charger);
        MessageSender.send(MessageSender.MSG_CHARGERS, Collections.singletonList(charger));
    }

    public Map<String, Channel> getChannels() {
        return channels;
    }

    public Channel getChannel(String gunId) {
        if (gunId == null) {
            return null;
        }
        // 判断枪号是否存在
        String chargerId = gunId.substring(0, gunId.length() - 1);
        Set<String> gunSet = chargerGunsMap.get(chargerId);
        if (gunSet == null || !gunSet.contains(gunId)) {
            return null;
        }
        return chargerChannels.get(chargerId);
    }

    private RequestMessage buildCalibrateTimeRequest(String chargerId, char gunNo) {
        RequestMessage requestMessage = new RequestMessage();
        requestMessage.setChargerAddress(chargerId);
        requestMessage.setGunNo(gunNo);
        requestMessage.setType((byte) 0x90);
        CalibrateTimeCommand command = new CalibrateTimeCommand();
        requestMessage.setMessageData(command);
        requestMessage.setLength(command.getProtocolLength());
        return requestMessage;
    }

    private ResponseMessage buildServerResponse(RequestMessage request) {
        if (request == null) {
            return null;
        }
        int respType;
        switch (request.getType()) {
            case 0x10:
            case 0x20:
            case 0x30:
                respType = request.getType() + 1;
                break;
            default:
                return null;
        }
        ResponseMessage resp = new ResponseMessage();

        ServerResponseData chargeCompleteRespData = new ServerResponseData();
        resp.setChargerAddress(request.getChargerAddress());
        resp.setGunNo(request.getGunNo());
        resp.setType(respType);
        resp.setLength(chargeCompleteRespData.getProtocolLength());
        resp.setStreamId(request.getStreamId());
        resp.setMessageData(chargeCompleteRespData);

        return resp;
    }

    private void sendResponse(Channel ch, ResponseMessage msg) {
        ch.writeAndFlush(msg)
                .addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        if (future.cause() != null) {
                            log.error("send resp failed. type: {}, streamId:{}", msg.getType(), msg.getStreamId(), future.cause());
                        }
                    }
                });
    }
}
