package com.genersoft.iot.vmp.jt1078.codec.decode;

import com.genersoft.iot.vmp.jt1078.proc.Header;
import com.genersoft.iot.vmp.jt1078.proc.factory.CodecFactory;
import com.genersoft.iot.vmp.jt1078.proc.request.Re;
import com.genersoft.iot.vmp.jt1078.proc.response.Rs;
import com.genersoft.iot.vmp.jt1078.service.Ijt1078Service;
import com.genersoft.iot.vmp.jt1078.session.Session;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.CompositeByteBuf;
import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author QingtaiJiang
 * @date 2023/4/27 18:10
 * @email qingtaij@163.com
 */
@Slf4j
public class Jt808Decoder extends ByteToMessageDecoder {

    private ApplicationEventPublisher applicationEventPublisher = null;
    private Ijt1078Service service = null;

    public Jt808Decoder(ApplicationEventPublisher applicationEventPublisher, Ijt1078Service service ) {
        this.applicationEventPublisher = applicationEventPublisher;
        this.service = service;
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        Session session = ctx.channel().attr(Session.KEY).get();
        log.info("> {} hex: 7e{}7e", session, ByteBufUtil.hexDump(in));
        try {
            // 按照部标定义执行校验和转义
            ByteBuf buf = unEscapeAndCheck(in);
            buf.retain();
            Header header = new Header();
            header.setMsgId(ByteBufUtil.hexDump(buf.readSlice(2)));
            header.setMsgPro(buf.readUnsignedShort());
            // 从消息属性中读取是否存在分包
            boolean isSubpackage = (header.getMsgPro() >>> 13 & 1) == 1;
            if (header.is2019Version()) {
                header.setVersion(buf.readUnsignedByte());
                String devId = ByteBufUtil.hexDump(buf.readSlice(10));
                header.setPhoneNumber(devId.replaceFirst("^0*", ""));
            } else {
                header.setPhoneNumber(ByteBufUtil.hexDump(buf.readSlice(6)).replaceFirst("^0*", ""));
            }
            header.setSn(buf.readUnsignedShort());
            if (isSubpackage) {
                int packageCount = buf.readUnsignedShort();
                int packageNumber = buf.readUnsignedShort();
                log.debug("[分包消息] header: {}, 序号: {}, 总数: {}", header, packageNumber, packageCount);
                // 缓存带合并的分包消息
                ByteBuf intactBuf = MultiPacketManager.INSTANCE.add(header, packageCount, buf);
                if (intactBuf == null) {
                    return;
                }
                buf = intactBuf;
            }
            Re handler = CodecFactory.getHandler(header.getMsgId());
            if (handler == null) {
                log.error("get msgId is null {}", header.getMsgId());
                buf.release();
                return;
            }

            Rs decode = handler.decode(buf, header, session, service);
            ApplicationEvent applicationEvent = handler.getEvent();
            if (applicationEvent != null) {
                applicationEventPublisher.publishEvent(applicationEvent);
            }
            if (decode != null) {
                out.add(decode);
            }
        } finally {
            in.skipBytes(in.readableBytes());
        }
    }


    /**
     * 转义与验证校验码
     *
     * @param byteBuf 转义Buf
     * @return 转义好的数据
     */
    public ByteBuf unEscapeAndCheck(ByteBuf byteBuf) throws Exception {
        int low = byteBuf.readerIndex();
        int high = byteBuf.writerIndex();
        byte checkSum = 0;
        int calculationCheckSum = 0;

        byte aByte = byteBuf.getByte(high - 2);
        byte protocolEscapeFlag7d = 0x7d;
        //0x7d转义
        byte protocolEscapeFlag01 = 0x01;
        //0x7e转义
        byte protocolEscapeFlag02 = 0x02;
        if (aByte == protocolEscapeFlag7d) {
            byte b2 = byteBuf.getByte(high - 1);
            if (b2 == protocolEscapeFlag01) {
                checkSum = protocolEscapeFlag7d;
            } else if (b2 == protocolEscapeFlag02) {
                checkSum = 0x7e;
            } else {
                log.error("转义1异常:{}", ByteBufUtil.hexDump(byteBuf));
                throw new Exception("转义错误");
            }
            high = high - 2;
        } else {
            high = high - 1;
            checkSum = byteBuf.getByte(high);
        }
        List<ByteBuf> bufList = new ArrayList<>();
        int index = low;
        while (index < high) {
            byte b = byteBuf.getByte(index);
            if (b == protocolEscapeFlag7d) {
                byte c = byteBuf.getByte(index + 1);
                if (c == protocolEscapeFlag01) {
                    ByteBuf slice = slice0x01(byteBuf, low, index);
                    bufList.add(slice);
                    b = protocolEscapeFlag7d;
                } else if (c == protocolEscapeFlag02) {
                    ByteBuf slice = slice0x02(byteBuf, low, index);
                    bufList.add(slice);
                    b = 0x7e;
                } else {
                    log.error("转义2异常:{}", ByteBufUtil.hexDump(byteBuf));
                    throw new Exception("转义错误");
                }
                index += 2;
                low = index;
            } else {
                index += 1;
            }
            calculationCheckSum = calculationCheckSum ^ b;
        }

        if (calculationCheckSum == checkSum) {
            if (bufList.isEmpty()) {
                return byteBuf.slice(low, high);
            } else {
                bufList.add(byteBuf.slice(low, high - low));
                return new CompositeByteBuf(UnpooledByteBufAllocator.DEFAULT, false, bufList.size(), bufList);
            }
        } else {
            log.info("{} 解析校验码:{}--计算校验码:{}", ByteBufUtil.hexDump(byteBuf), checkSum, calculationCheckSum);
            throw new Exception("校验码错误!");
        }
    }


    private ByteBuf slice0x01(ByteBuf buf, int low, int sign) {
        return buf.slice(low, sign - low + 1);
    }

    private ByteBuf slice0x02(ByteBuf buf, int low, int sign) {
        buf.setByte(sign, 0x7e);
        return buf.slice(low, sign - low + 1);
    }
}
