package com.mvc.iot;

import com.mvc.entity.TPackLog;
import com.mvc.iot.model.IotDataModel;
import com.mvc.iot.model.IotTempModel;
import com.mvc.netty.FzBaowenModel;
import com.mvc.netty.PackageParse;
import com.mvc.netty.dealconstant.HandleMsgCode;
import com.mvc.netty.dealconstant.PackageOperateCode;
import com.mvc.utils.CRC16Util;
import com.mvc.utils.DateUtil;
import com.mvc.utils.NumberUtil;
import com.mvc.utils.StoreBlockingQueue;
import org.apache.commons.lang3.SerializationUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class IoTDataHandler implements Runnable {
    private static final Logger logger = LoggerFactory.getLogger(IoTDataHandler.class);

    @Resource
    private PackageParse packageParse;

    private static final int TIMEOUT_MINUTE = 60;//数据超时分钟数(从initTime至当前时间)

    private static Map<String, IotDataModel> iotDataCache = new ConcurrentHashMap<>();//Key:IMEI_SAMPLETIME(时间戳)

    @Override
    public void run() {
        while (true) {
            try {
                IotTempModel tempModel = IotStoreBlockingQueue.iotDataQueue.take();
                handleIotData(tempModel);
                handleTimeOutData();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 处理IOT数据
     *
     * @param tempModel
     */
    private void handleIotData(IotTempModel tempModel) {
        String resStr = tempModel.getMessage();
        logger.info("[IOT]开始处理数据{}:{}", tempModel.getType(), resStr);
        /* 以下解析基于每包都是完整的前提,不考虑有分包情况 */

        //到报文起始符14字节 //是否有头信息
        if (resStr.length() < 34 || !resStr.substring(0, 4).equals(PackageOperateCode.SOH.getKey().toLowerCase())) {
            logger.info("[IOT]数据长度或格式错误:{}", resStr);
            return;
        }

        //判断类型，长度
        String direandlength = resStr.substring(22, 26);
        String idenStr = NumberUtil.hexStringToBinaryString(direandlength);
        int bodyLength = NumberUtil.binaryToDecimal(idenStr.substring(4));
        if (resStr.length() < bodyLength * 2 + 34) {
            logger.info("[IOT]长度与实际不符:{}", resStr);
            return;
        }

        boolean crcflag = CRC16Util.checkBanwenCrc(resStr.substring(0, bodyLength * 2 + 34));
        if (!crcflag) {
            logger.info("[IOT]CRC校验错误:{}", resStr);
            return;
        }

        HandleMsgCode handleMsgCode = HandleMsgCode.get(resStr.substring(20, 22));
        if (handleMsgCode == null) {
            logger.info("[IOT]未找到功能码:{}", resStr);
            return;
        }

        //使用packageParse解析(利旧,但会有多余的解析)
        TPackLog packLog = null;
        if (handleMsgCode == HandleMsgCode.H_32) {
            packLog = packageParse.parse_duobao(resStr);
        } else if (handleMsgCode == HandleMsgCode.H_36) {
            packLog = packageParse.parse_DT(resStr);
        }

        if (packLog == null) {
            logger.info("[IOT]初解报文失败:{}", resStr);
            return;
        }

        IotDataModel iotDataModel = null;

        Date sampleTime = packLog.getDatatime();
        String key = tempModel.getImei() + "_" + sampleTime.getTime();
        if (iotDataCache.containsKey(key)) {
            iotDataModel = iotDataCache.get(key);
            iotDataModel.addMessage(handleMsgCode, packLog);
        } else {
            iotDataModel = new IotDataModel(tempModel.getImei(), packLog, handleMsgCode);
            iotDataCache.put(key, iotDataModel);
        }

        if (checkMessageComplete(iotDataModel)) {//都处理完成了
            iotDataCache.remove(key);
        }
    }

    /**
     * 检测报文是否已接收完整
     *
     * @param iotDataModel
     */
    private boolean checkMessageComplete(IotDataModel iotDataModel) {
        /* 利旧处理,解析仍借用ParseThread类,因此需要转换为FzBaowenModel */
        try {
            if (iotDataModel.getH32MessageCount() == iotDataModel.getH32Messages().size()) {//32报文完整了
                FzBaowenModel baowenModel = iotDataModel.convertToFzBaowenModel(HandleMsgCode.H_32);
                StoreBlockingQueue.baoweoQueue.put(SerializationUtils.clone(baowenModel));//注意要深复制(下面代码会修改引用)
                iotDataModel.setH32MessageCount(-1);//表示处理过了
                iotDataModel.getH32Messages().clear();
            } else if (iotDataModel.getH36MessageCount() == iotDataModel.getH36Messages().size()) {//36报文完整了
                FzBaowenModel baowenModel = iotDataModel.convertToFzBaowenModel(HandleMsgCode.H_36);
                StoreBlockingQueue.baoweoQueue.put(SerializationUtils.clone(baowenModel));//注意要深复制(下面代码会修改引用)
                iotDataModel.setH36MessageCount(-1);//表示处理过了
                iotDataModel.getH36Messages().clear();
            }

            return iotDataModel.getH32MessageCount() == -1 && iotDataModel.getH36MessageCount() == -1;
        } catch (Exception e) {
            logger.error("[IOT]checkMessageComplete:{}", e);
        }
        return false;
    }

    /**
     * 处理超时数据
     */
    private void handleTimeOutData() {
        for (String key : iotDataCache.keySet()) {
            IotDataModel iotDataModel = iotDataCache.get(key);

            if (DateUtil.getMinsbetweenTime(iotDataModel.getInitTime(), new Date()) < TIMEOUT_MINUTE) {
                continue;
            }
            try {
                //超过 TIMEOUT_MINUTE 分钟了,仍未接收完整
                if (iotDataModel.getH32Messages().size() > 0) {
                    logger.info("[IOT]超时未收到完整包:{}", iotDataModel.getDeviceSn());
                    FzBaowenModel baowenModel = iotDataModel.convertToFzBaowenModel(HandleMsgCode.H_32);
                    StoreBlockingQueue.errorQueue.put(baowenModel);
                }
                if (iotDataModel.getH36Messages().size() > 0) {
                    logger.info("[IOT]超时未收到完整包:{}", iotDataModel.getDeviceSn());
                    FzBaowenModel baowenModel = iotDataModel.convertToFzBaowenModel(HandleMsgCode.H_36);
                    StoreBlockingQueue.errorQueue.put(baowenModel);
                }
                iotDataCache.remove(key);
            } catch (Exception e) {
                logger.error("[IOT]handleTimeOutData:{}", e);
            }
        }
    }
}


