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

import com.its.app.utils.NettyUtils;
import com.its.vms.xnettcp.center.handler.CenterTcpServerIdleStateHandler;
import com.its.vms.xnettcp.center.protocol.CenterProtocol;
import com.its.vms.xnettcp.center.protocol.CenterReqFrameHead;
import com.its.vms.xnettcp.center.protocol.CenterReqFramePacket;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import lombok.extern.slf4j.Slf4j;

import java.util.List;

@Slf4j
public class CenterTcpServerDecoder extends ByteToMessageDecoder {

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> out) throws Exception {
        String ipAddress = NettyUtils.getRemoteIpAddress(ctx.channel());
        Channel channel = ctx.channel();
        try {
            if (!channel.isOpen() || !channel.isActive()) {
                log.error("CenterTcpServerDecoder.decode: {}, isOpen: {}, isActive: {}", ipAddress, channel.isOpen(), channel.isActive());
                CenterTcpServerIdleStateHandler.disconnectChannel(channel);
                return;
            }

            int readableBytes = byteBuf.readableBytes();
            log.info("CENTER_RECV_0: [{}]. ReadableBytes: {} Bytes, ReaderIndex: {}", ipAddress, readableBytes, byteBuf.readerIndex());
            if (readableBytes == 0) {
                log.error("CENTER_RECV_0: {}. byteBuf.readableBytes() == 0.", ipAddress);
                return;
            }

            int packetCnt = 0;
            int headLength = CenterProtocol.HEADER_SIZE;
            int bodyLength = 0;
            int remainLength = 0;
            while (readableBytes >= headLength) {
                byteBuf.markReaderIndex();
                byte[] headBuffer = new byte[headLength];
                byteBuf.readBytes(headBuffer);

                // 헤더 데이터 먼저 읽는다.
                CenterReqFrameHead head = new CenterReqFrameHead(headBuffer);
                bodyLength = head.getLength();
                remainLength = readableBytes - headLength;
                if (head.getSender() != (byte)CenterProtocol.INT_ID_RSE_OPER) {
                    log.error("CenterTcpServerDecoder.decode: {}, Sender Error: {}", ipAddress, head.getSender());
                    CenterTcpServerIdleStateHandler.disconnectChannel(channel);
                    return;
                }
                if (head.getReceiver() != (byte)CenterProtocol.INT_ID_RSE_SERVER) {
                    log.error("CenterTcpServerDecoder.decode: {}, Receiver Error: {}", ipAddress, head.getReceiver());
                    CenterTcpServerIdleStateHandler.disconnectChannel(channel);
                    return;
                }

                if (bodyLength > remainLength) {
                    // 패킷을 한번에 수신하지 못하였다.
                    log.warn("CENTER_RECV_0: [{}]. ReadableBytes: {} Bytes, ReaderIndex: {}, The packet was not completed. Body/Remain: {}/{}.",
                            ipAddress, readableBytes, byteBuf.readerIndex(), bodyLength, remainLength);
                    break;
                }
                if (bodyLength == 0) {
                    // body length 는 항상 0보다 커야 정상적인 패킷이다.
                    // 현재 읽어들인 데이터를 모두 버려버리자.
                    byteBuf.readerIndex(byteBuf.readerIndex());
                    byteBuf.markReaderIndex();
                    byteBuf.discardReadBytes();
                    break;
                }

                // 버퍼를 헤더 크기만큼 이동
                byteBuf.readerIndex(headLength);
                byteBuf.markReaderIndex();
                byteBuf.discardReadBytes();

                // Body 데이터를 읽어 들인다.
                byte[] bodyBuffer = new byte[bodyLength];
                byteBuf.readBytes(bodyBuffer);

                // 버퍼를 Body 크기만큼 이동
                byteBuf.readerIndex(bodyLength);
                byteBuf.markReaderIndex();
                byteBuf.discardReadBytes();

                // 한 패킷이 완성되었음.
                CenterReqFramePacket body = new CenterReqFramePacket(head, bodyBuffer);
                out.add(body);

                readableBytes = byteBuf.readableBytes();
                packetCnt++;
                log.info("CENTER_RECV_{}: [{}]. ReadableBytes: {} Bytes, ReaderIndex: {}, ===> packet encoding ok.", packetCnt, ipAddress, readableBytes, byteBuf.readerIndex());
            }
        }
        finally {
            byteBuf.resetReaderIndex();
        }
    }
}
