package com.its.vms.xnettcp.vms.codec;

import com.its.vms.config.CommunicationConfig;
import com.its.vms.dto.TbVmsCtlrDto;
import com.its.vms.service.AppRepositoryService;
import com.its.vms.xnettcp.vms.handler.VmsServerIdleStateHandler;
import com.its.vms.xnettcp.vms.protocol.VmsProtocolConst;
import com.its.vms.xnettcp.vms.protocol.impl.VmsResFramePacket;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;

import java.util.List;

@Slf4j
@RequiredArgsConstructor
public class VmsTcpServerDleStuffingDecoder extends ByteToMessageDecoder {

    private final CommunicationConfig communicationConfig;
    private final AppRepositoryService repoService;
    private final TbVmsCtlrDto vmsObj;

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> list) throws Exception {

        Channel channel = ctx.channel();
        String logKey = String.valueOf(this.vmsObj.getLogKey());

        try {
            MDC.put("id", logKey);

            int readableBytes = byteBuf.readableBytes();
            log.info("RECV: VMS {}, VmsTcpServerDleStuffingDecoder.decode: ReadableBytes: {} Bytes, ReaderIndex: {}.",
                    this.vmsObj.getVmsCtlrNmbr(), readableBytes, byteBuf.readerIndex());

            if (!channel.isOpen() || !channel.isActive()) {
                log.error("RECV: VMS {}, VmsTcpServerDleStuffingDecoder.decode: isOpen: {}, isActive: {}. [{}]",
                        this.vmsObj.getVmsCtlrNmbr(), channel.isOpen(), channel.isActive(), this.vmsObj.getLogKey());
                VmsServerIdleStateHandler.disconnectChannel(this.vmsObj, channel);
                return;
            }

            byteBuf.markReaderIndex();
            byte[] recvBytes = new byte[readableBytes];
            byte[] packets = new byte[readableBytes];
            byteBuf.readBytes(recvBytes);

            int msgSize = 0;        // 순수 데이터 사이즈
            boolean foundDle = false;
            for (int readIdx = 0; readIdx < readableBytes; readIdx++) {
                if (readIdx == 2) {
                    if (recvBytes[0] != VmsProtocolConst.VMS_DLE || recvBytes[1] != VmsProtocolConst.VMS_STX) {
                        log.error("RECV: VMS {}, VmsTcpServerDleStuffingDecoder: DLE/STX Data Error. {}/{}", this.vmsObj.getVmsCtlrNmbr(), recvBytes[0], recvBytes[1]);
                        VmsServerIdleStateHandler.disconnectChannel(this.vmsObj, channel);
                        return;
                    }
                }

                byte data = recvBytes[readIdx];
                if (foundDle) {
                    foundDle = false;
                    if (data == VmsProtocolConst.VMS_DLE) {
                        // Skip DLE Stuffing Data
                        log.info("RECV: VMS {}, VmsTcpServerDleStuffingDecoder: ReadableBytes: {} Bytes, ReadIndex: {}, DLE Stuffing Data Recv.",
                                this.vmsObj.getVmsCtlrNmbr(), readableBytes, readIdx);
                    } else if (data == VmsProtocolConst.VMS_STX) {
                        // Packet
                        packets[msgSize++] = data;
                    } else if (data == VmsProtocolConst.VMS_ETX) {
                        packets[msgSize++] = data;

                        // 이전데이터가 DLE 이고 현재 데이터가 ETX 이면 패킷의 끝임.
                        // 다음 2바이트는 CRC 데이터임, 남아 있는 데이터가 CRC 2바이트를 포함한 크기보다 작으면
                        // 하나의 패킷을 완전히 수신하지 못한 것임
                        if ((readIdx + 2) > (readableBytes)) {
                            // 하나의 패킷을 읽지 못함
                            log.error("RECV: VMS {}, ReadableBytes: {} Bytes, ReadIndex: {}, DLE Data Remain.", this.vmsObj.getVmsCtlrNmbr(), readableBytes, readIdx);
                            break;
                        }
                        packets[msgSize++] = recvBytes[readIdx+1];  // CRC1
                        packets[msgSize++] = recvBytes[readIdx+2];  // CRC2

                        // 하나의 패킷을 읽었음 ***************
                        byteBuf.readerIndex(readIdx+3);
                        byteBuf.markReaderIndex();
                        byteBuf.discardReadBytes();

                        list.add(new VmsResFramePacket(this.vmsObj, packets, msgSize));
                        break;
                    } else {
                        // DLE 다음에 와야할 데이터가 들어오지 않았다. 패킷에 오류가 발생함.
                        log.error("RECV: VMS {}, VmsTcpServerDleStuffingDecoder: ReadableBytes: {} Bytes, ReadIndex: {}, DLE Next Data Recv Error.",
                                this.vmsObj.getVmsCtlrNmbr(), readableBytes, readIdx);
                        VmsServerIdleStateHandler.disconnectChannel(this.vmsObj, channel);
                        return;
                    }
                } else {
                    packets[msgSize++] = data;
                    if (data == VmsProtocolConst.VMS_DLE) {
                        foundDle = true;
                    }
                }
            }
        }
        finally {
            byteBuf.resetReaderIndex();
            MDC.remove(logKey);
            MDC.clear();
        }

    }
}
