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

import com.beanit.asn1bean.ber.ReverseByteArrayOutputStream;
import com.beanit.asn1bean.ber.types.BerEnum;
import com.beanit.asn1bean.ber.types.BerOctetString;
import com.beanit.asn1dsrc.dsrc.C2CAuthenticatedMessage;
import com.beanit.asn1dsrc.dsrc.DatexDataPacket;
import com.beanit.asn1dsrc.util.DsrcAsn1Crc16;
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.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;

@Slf4j
@RequiredArgsConstructor
@Component
@ChannelHandler.Sharable
public class DsrcTcpServerEncoder extends MessageToByteEncoder<Object> {

    private final RunningConfig runningConfig;

     @Override
    protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf byteBuf) throws Exception {

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

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

        if (obj.getChannel() != null && (channel != obj.getChannel())) {
            log.error("DsrcTcpServerEncoder.encode: {}, channel error, curr: {}, old: {}", ipAddress, channel.toString(), obj.getChannel().toString());
            DsrcAsn1ServerIdleStatePacketHandler.disconnectChannel(obj, channel);
            return;
        }

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

        if (!(msg instanceof C2CAuthenticatedMessage)) {
            log.error("DsrcTcpServerEncoder.encode: {}, msg is not ByteBuffer. [{}]", ipAddress, obj.getLogKey());
            DsrcAsn1ServerIdleStatePacketHandler.disconnectChannel(obj, channel);
            return;
        }

        C2CAuthenticatedMessage c2c = (C2CAuthenticatedMessage)msg;
        try {
            ReverseByteArrayOutputStream c2cBuff = new ReverseByteArrayOutputStream(2048);
            ReverseByteArrayOutputStream pktBuff = new ReverseByteArrayOutputStream(2048);
            int length = c2c.encode(c2cBuff);
            if (length > 0) {
                DatexDataPacket dataPkt = new DatexDataPacket();
                dataPkt.setDatexVersionNumber(new BerEnum(1));  // version == 1
                dataPkt.setDatexData(new BerOctetString(c2cBuff.getArray()));
                dataPkt.setDatexCrcNbr(new BerOctetString(DsrcAsn1Crc16.getCrc16ToByteArray(c2cBuff.getArray())));  // crc

                int encodeLength = dataPkt.encode(pktBuff);
                byteBuf.writeBytes(pktBuff.getArray());
                //log.info("SEND: Encoder: {}. ASN1 PACKET: {} Bytes, encodeLength: {} Bytes", ipAddress, length, encodeLength);
                //log.info("SEND: Encoder: {}, C2CAuth: {}", ipAddress, c2c);
                if (this.runningConfig.isDumpSend()) {
                    byte[] debugBytes = new byte[byteBuf.readableBytes()];
                    byteBuf.getBytes(byteBuf.readerIndex(), debugBytes);
                    log.info("SEND: [{}], {} Bytes. {}", ipAddress, debugBytes.length, SysUtils.byteArrayToHex(debugBytes));
                }
            } else {
                log.error("SEND: {}. C2CAuthenticatedMessage encode buffer length zero: will be closed.", ipAddress);
                DsrcAsn1ServerIdleStatePacketHandler.disconnectChannel(obj, channel);
            }
        } catch (Exception e) {
            log.error("DsrcTcpServerEncoder.encode: SEND: {}. Exception Error: will be closed: {}. [{}]", ipAddress, e.toString(), obj.getLogKey());
            DsrcAsn1ServerIdleStatePacketHandler.disconnectChannel(obj, channel);
        }
        //MDC.remove(obj.getLogKey());
        //MDC.clear();
    }
}
