package protocolsupport.protocol.pipeline.common;

import java.io.IOException;
import java.nio.channels.ClosedChannelException;
import java.text.MessageFormat;
import java.util.HashSet;

import org.bukkit.Bukkit;

import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.timeout.ReadTimeoutException;
import protocolsupport.api.events.ConnectionCloseEvent;
import protocolsupport.api.events.ConnectionOpenEvent;
import protocolsupport.api.events.PlayerDisconnectEvent;
import protocolsupport.protocol.ConnectionImpl;
import protocolsupport.protocol.storage.ProtocolStorage;
import protocolsupport.zplatform.ServerPlatform;
import protocolsupport.zplatform.network.NetworkManagerWrapper;

public class LogicHandler extends ChannelDuplexHandler {

	private static final HashSet<Class<? extends Throwable>> ignoreExceptions = new HashSet<>();
	static {
		ignoreExceptions.add(ClosedChannelException.class);
		ignoreExceptions.add(ReadTimeoutException.class);
		ignoreExceptions.add(IOException.class);
	}

	private final ConnectionImpl connection;
	public LogicHandler(ConnectionImpl connection) {
		this.connection = connection;
	}

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		if (connection.handlePacketReceive(msg)) {
			super.channelRead(ctx, msg);
		}
	}

	@Override
	public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
		if (connection.handlePacketSend(msg)) {
			super.write(ctx, msg, promise);
		} else {
			promise.setSuccess();
		}
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) throws Exception {
		if (ServerPlatform.get().getMiscUtils().isDebugging() && !ignoreExceptions.contains(e.getClass())) {
			super.exceptionCaught(ctx, new NetworkException(e, connection));
		} else {
			super.exceptionCaught(ctx, e);
		}
	}

	private static final class NetworkException extends Exception {
		private static final long serialVersionUID = 1L;

		public NetworkException(Throwable original, ConnectionImpl connection) {
			super(MessageFormat.format(
				"Network exception occured(address: {0}, username: {1}, version: {2})",
				connection.getAddress(),
				connection.getNetworkManagerWrapper().getUserName(),
				connection.getVersion()
			), original);
		}
	}

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		super.channelActive(ctx);
		Bukkit.getPluginManager().callEvent(new ConnectionOpenEvent(connection));
	}

	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		super.channelInactive(ctx);
		NetworkManagerWrapper networkmanager = connection.getNetworkManagerWrapper();
		String username = networkmanager.getUserName();
		if (username != null) {
			Bukkit.getPluginManager().callEvent(new PlayerDisconnectEvent(connection, username));
		}
		Bukkit.getPluginManager().callEvent(new ConnectionCloseEvent(connection));
		ProtocolStorage.removeConnection(networkmanager.getRawAddress());
	}

}

@@ -205,16 +205,58 @@
 .Bukkit;
+%0Aimport org.bukkit.plugin.java.JavaPlugin;
 %0A%0Aimport
@@ -432,24 +432,64 @@
 tException;%0A
+import protocolsupport.ProtocolSupport;%0A
 import proto
@@ -2269,16 +2269,49 @@
 t(%0A%09%09%09%09%22
+ProtocolSupport(buildinfo: %7B0%7D): 
 Network 
@@ -2338,17 +2338,17 @@
 dress: %7B
-0
+1
 %7D, usern
@@ -2353,17 +2353,17 @@
 rname: %7B
-1
+2
 %7D, versi
@@ -2371,14 +2371,78 @@
 n: %7B
-2
+3
 %7D)%22,%0A
+%09%09%09%09JavaPlugin.getPlugin(ProtocolSupport.class).getBuildInfo(),%0A
 %09%09%09%09

Include ps buildinfo in debug exception message