package com.its.dsrc.xnettcp.dsrc.codec;

import com.beanit.asn1bean.ber.types.BerOctetString;
import com.beanit.asn1dsrc.dsrc.C2CAuthenticatedMessage;
import com.beanit.asn1dsrc.dsrc.DatexDataPacket;
import com.its.app.utils.NettyUtils;
import com.its.app.utils.SysUtils;
import com.its.dsrc.config.RunningConfig;
import com.its.dsrc.entity.TbRseCtlr;
import com.its.dsrc.global.AppRepository;
import com.its.dsrc.xnettcp.dsrc.handler.DsrcAsn1ServerIdleStatePacketHandler;
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.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.List;

@Slf4j
@RequiredArgsConstructor
public class DsrcTcpServerDecoder extends ByteToMessageDecoder {

    private final RunningConfig runningConfig;

    private static final int DSRC_ASN1_PACKET_MIN_SIZE = 5;
    private static final int DSRC_ASN1_PACKET_MAX_SIZE = 3000;

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

        //String tcpAddress = NettyUtils.getTcpAddress(ctx.channel());
        String ipAddress = NettyUtils.getRemoteIpAddress(ctx.channel());
        Channel channel = ctx.channel();
        TbRseCtlr obj = AppRepository.getInstance().getCtlrIpMap().get(ipAddress);
        if (obj == null) {
            log.error("TcpServerDecoder.decode: Unknown Controller IP: {}. will be close.", ipAddress);
            DsrcAsn1ServerIdleStatePacketHandler.disconnectChannel(null, channel);
            return;
        }

        MDC.put("id", obj.getLogKey());

        if (!channel.isOpen() || !channel.isActive()) {
            log.error("TcpServerDecoder.decode: {}, isOpen: {}, isActive: {}. [{}]", ipAddress, channel.isOpen(), channel.isActive(), obj.getLogKey());
            DsrcAsn1ServerIdleStatePacketHandler.disconnectChannel(obj, channel);
            return;
        }

        int readableBytes = byteBuf.readableBytes();
        log.debug("RECV_0: [{}]. ReadableBytes: {} Bytes, ReaderIndex: {}", ipAddress, readableBytes, byteBuf.readerIndex());
        if (readableBytes == 0) {
            log.error("RECV: {}. byteBuf.readableBytes() == 0. [{]{}]", ipAddress, obj.getLogKey());
            return;
        }
        //log.debug("RECV: Decoder: {}. [Readable:{}, RIDX:{}-WIDX:{}]", ipAddress, readableBytes, byteBuf.readerIndex(), byteBuf.writerIndex());

        if (this.runningConfig.isDumpRecv()) {
            byte[] debugBytes = new byte[byteBuf.readableBytes()];
            byteBuf.getBytes(byteBuf.readerIndex(), debugBytes);
            log.debug("RECV: [{}], {} Bytes. {}", ipAddress, debugBytes.length, SysUtils.byteArrayToHex(debugBytes));
        }

        int pktLength = 0;
        int authLength= 0;
        //int readerIndex = byteBuf.readerIndex();
        while (readableBytes > DsrcTcpServerDecoder.DSRC_ASN1_PACKET_MIN_SIZE) {

            byteBuf.markReaderIndex();
            byte[] inBytes = new byte[byteBuf.readableBytes()];
            byteBuf.readBytes(inBytes);
            DatexDataPacket datexDataPacket = new DatexDataPacket();
            try {
                try {
                    pktLength = datexDataPacket.decode(new ByteArrayInputStream(inBytes, 0, inBytes.length));
                }
                catch(IOException e) {
                    //log.warn("TcpServerDecoder.decode: RECV: packet length. {}, inBytes: {}, pktLength: {}", ipAddress, inBytes, pktLength);
                    break;
                }

                if (pktLength > readableBytes) {
                    log.warn("RECV: Decoder: {}. [pktLength > readableBytes]", ipAddress);
                    log.warn("RECV: Decoder: {}. [Readable:{}, RIDX:{}-WIDX:{}]. Packet Remain.", ipAddress, readableBytes, byteBuf.readerIndex(), byteBuf.writerIndex());
                    break;
                }

                // 패킷 읽은 만큼 이동
                byteBuf.readerIndex(pktLength);
                byteBuf.markReaderIndex();
                byteBuf.discardReadBytes();

                BerOctetString berOctetString = datexDataPacket.getDatexData();
                C2CAuthenticatedMessage c2c = new C2CAuthenticatedMessage();
                authLength = c2c.decode(new ByteArrayInputStream(berOctetString.value, 0, berOctetString.value.length));
                //log.info("RECV: Decoder: {}. ASN1 PACKET: {} Bytes, decodeLength: {} Bytes", ipAddress, pktLength, authLength);
                //log.info("RECV: Decoder: {}, C2CAuth: {}", ipAddress, c2c);
                obj.setLastRecvTime();
                list.add(c2c);

                readableBytes = byteBuf.readableBytes();
            }
            catch(Exception e) {
                log.error("TcpServerDecoder.decode: RECV: {}. Exception Error1: will be closed: {}. [{}]", ipAddress, e.toString(), obj.getLogKey());
                DsrcAsn1ServerIdleStatePacketHandler.disconnectChannel(obj, channel);
            }
        }
        byteBuf.resetReaderIndex();
        MDC.remove(obj.getLogKey());
        MDC.clear();
    }
}
