package com.prolog.rdc.tms.util;

import java.util.List;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.TooLongFrameException;

/**
 * @Description
 * @Author liq
 * @Date 2021/11/11 11:13
 **/
public class StartAndEndDelimiterDecoder extends ByteToMessageDecoder {

	// 最大包长度
	private final int maxFrameLength;
	// 开始分隔符
	private final ByteBuf startDelimiter;
	// 结束分隔符
	private final ByteBuf endDelimiter;
	// 去除分隔符 默认：false
	private final boolean stripDelimiter;

	public StartAndEndDelimiterDecoder(int maxFrameLength, ByteBuf startDelimiter, ByteBuf endDelimiter) {
		this(maxFrameLength, startDelimiter, endDelimiter, false);
	}

	public StartAndEndDelimiterDecoder(int maxFrameLength, ByteBuf startDelimiter, ByteBuf endDelimiter, boolean stripDelimiter) {
		validateMaxFrameLength(maxFrameLength);
		validateDelimiter(startDelimiter, endDelimiter);
		this.maxFrameLength = maxFrameLength;
		this.startDelimiter = startDelimiter;
		this.endDelimiter = endDelimiter;
		this.stripDelimiter = stripDelimiter;
	}

	/**
	 * decode() 会根据接收的数据，被调用多次 如果 list 不为空，就会将 list 的内容传递给下一个 handler
	 *
	 * @param ctx 上下文对象
	 * @param in  入站后的 ByteBuf
	 * @param out 将解码后的数据传递给下一个 handler
	 * @throws Exception
	 */
	@Override
	protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
		Object decoded = decode(ctx, in);
		if (decoded != null) {
			out.add(decoded);
		}
	}

	protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
		int startDelimLength = startDelimiter.capacity();
		int endDelimLength = endDelimiter.capacity();
		// System.out.println("begin----bufferlength:" +
		// ByteBufUtil.getBytes(buffer).length + " startDelimLength:" + startDelimLength
		// + " endDelimLength:" + endDelimLength + " buffer:" + new
		// String(ByteBufUtil.getBytes(buffer)));
		// 查找结束符
		int frameLength = this.indexOf(0, buffer, endDelimiter);
		// 开始符合结束符一样，则需要查找第二个结束符 （从第一个开始符以后的字符去找）
		if (startDelimiter.equals(endDelimiter)) {
			frameLength = this.indexOf(startDelimLength, buffer, endDelimiter);
		}

		// 包长度超过最大长度，丢弃包
		if (frameLength > 0 && frameLength > maxFrameLength) {
			buffer.skipBytes(frameLength);
			fail(frameLength);
			return null;
		}

		// 处理包数据
		if (frameLength >= 0) {
			// 当前要处理的数据包（不包含结束符）
			ByteBuf frameBuf = Unpooled.copiedBuffer(ByteBufUtil.getBytes(buffer, buffer.readerIndex(), frameLength, true));
			// 查找开始字符
			int startIndex = ByteBufUtil.indexOf(startDelimiter, frameBuf);

			if (startIndex < 0) {
				// 未找到开始字符，丢弃数据不处理。 开始字符结束字符不一致：丢弃整个带结束符的数据长度
				if (startDelimiter.equals(endDelimiter)) {
					buffer.skipBytes(frameLength);
				} else {
					buffer.skipBytes(frameLength + endDelimLength);
				}
				return null;
			} else {
				// 如果开始字符车结束字符一样，并且包没有内容，则不处理包。并且减去一个相同字符
				if (startDelimiter.equals(endDelimiter) && frameLength == startDelimLength) {
					buffer.skipBytes(startDelimLength);
					return null;
				}
			}

			// System.out.println("substring---->>>>bufferlength:" +
			// ByteBufUtil.getBytes(buffer).length + " frameLength:" + frameLength + "
			// startIndex:" + startIndex + " endDelimLength:" + endDelimLength + " buffer:"
			// + new String(ByteBufUtil.getBytes(buffer)) + " frameBuf:" + new
			// String(ByteBufUtil.getBytes(frameBuf)));
			// 获取完整包数据
			ByteBuf frame;
			if (stripDelimiter) {
				frame = buffer.readRetainedSlice(frameLength);
				buffer.skipBytes(endDelimLength);
			} else {
				frame = buffer.readRetainedSlice(frameLength + endDelimLength);
			}

			// 丢弃开始字符之前的数据
			frame.skipBytes(startIndex);
			if (stripDelimiter) {
				frame.skipBytes(startDelimLength);
			}
			return frame;
		}
		return null;
	}

	private static int indexOf(int skipLeng, ByteBuf buffer, ByteBuf delimiter) {
		for (int i = buffer.readerIndex() + skipLeng; i < buffer.writerIndex(); i++) {
			int bufferIndex = i;
			int delimIndex;
			for (delimIndex = 0; delimIndex < delimiter.capacity(); delimIndex++) {
				if (buffer.getByte(bufferIndex) != delimiter.getByte(delimIndex)) {
					break;
				} else {
					bufferIndex++;
					if (bufferIndex == buffer.writerIndex() && delimIndex != delimiter.capacity() - 1) {
						return -1;
					}
				}
			}

			if (delimIndex == delimiter.capacity()) {
				// Found the needle from the haystack!
				return i - buffer.readerIndex();
			}
		}
		return -1;
	}

	private void fail(long frameLength) {
		if (frameLength > 0) {
			throw new TooLongFrameException("frame length exceeds " + maxFrameLength + ": " + frameLength + " - discarded");
		} else {
			throw new TooLongFrameException("frame length exceeds " + maxFrameLength + " - discarding");
		}
	}

	// 验证起始分隔符
	private static void validateDelimiter(ByteBuf startDelimiter, ByteBuf endDelimiter) {
		if (startDelimiter == null) {
			throw new NullPointerException("startDelimiter");
		}
		if (!startDelimiter.isReadable()) {
			throw new IllegalArgumentException("empty startDelimiter");
		}

		if (endDelimiter == null) {
			throw new NullPointerException("endDelimiter");
		}
		if (!endDelimiter.isReadable()) {
			throw new IllegalArgumentException("empty endDelimiter");
		}
	}

	// 验证最大长度
	private static void validateMaxFrameLength(int maxFrameLength) {
		if (maxFrameLength <= 0) {
			throw new IllegalArgumentException("maxFrameLength must be a positive integer: " + maxFrameLength);
		}
	}

}
