package net.mcft.copy.core.network;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;

import java.util.EnumMap;
import java.util.List;

import net.mcft.copy.core.copycore;
import net.mcft.copy.core.util.ClientUtils;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.network.INetHandler;
import net.minecraft.network.NetHandlerPlayServer;
import net.minecraft.network.PacketBuffer;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.network.FMLEmbeddedChannel;
import cpw.mods.fml.common.network.FMLIndexedMessageToMessageCodec;
import cpw.mods.fml.common.network.FMLOutboundHandler;
import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.relauncher.Side;

@ChannelHandler.Sharable
public class ChannelHandler extends FMLIndexedMessageToMessageCodec<AbstractPacket> {
	
	private final EnumMap<Side, FMLEmbeddedChannel> channels;
	
	public ChannelHandler(String name) {
		channels = NetworkRegistry.INSTANCE.newChannel(name, this);
	}
	
	@Override
	public void encodeInto(ChannelHandlerContext context, AbstractPacket packet, ByteBuf target) throws Exception {
		packet.encode(context, new PacketBuffer(target));
	}
	
	@Override
	public void decodeInto(ChannelHandlerContext context, ByteBuf source, AbstractPacket packet) {
		try {
			packet.decode(context, new PacketBuffer(source));
		} catch (Exception t) {
			copycore.getLogger().error("Error decoding packet '{}':", packet.getClass().getSimpleName());
			t.printStackTrace();
			return;
		}
		if (FMLCommonHandler.instance().getEffectiveSide() == Side.SERVER) {
			INetHandler netHandler = context.channel().attr(NetworkRegistry.NET_HANDLER).get();
			EntityPlayer player = ((NetHandlerPlayServer)netHandler).playerEntity;
			packet.handleServerSide(player);
		} else packet.handleClientSide(ClientUtils.getLocalPlayer());
	}
	
	// Sending packets
	
	/** Sends a packet to the server. */
	public void sendToServer(AbstractPacket packet) {
		FMLEmbeddedChannel channel = channels.get(Side.CLIENT);
		channel.attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.TOSERVER);
		channel.writeAndFlush(channel);
	}
	
	/** Sends a packet to a player. */
	public void sendToPlayer(EntityPlayer player, AbstractPacket packet) {
		FMLEmbeddedChannel channel = channels.get(Side.SERVER);
		channel.attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.PLAYER);
		channel.attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).set(player);
		channel.writeAndFlush(packet);
	}
	
	/** Sends a packet to everyone near a certain position in the world. */
	public void sendToEveryoneNear(World world, double x, double y, double z,
	                               double distance, AbstractPacket packet) {
		FMLEmbeddedChannel channel = channels.get(Side.SERVER);
		channel.attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.ALLAROUNDPOINT);
		channel.attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).set(new NetworkRegistry.TargetPoint(world.provider.dimensionId, x, y, z, distance));
		channel.writeAndFlush(packet);
	}
	
	/** Sends a packet to everyone tracking this entity. */
	public void sendToEveryoneTracking(Entity entity, AbstractPacket packet) {
		((WorldServer)entity.worldObj).getEntityTracker().func_151247_a(
				entity, channels.get(Side.SERVER).generatePacketFrom(packet));
	}
	
	/** Sends a packet to everyone near a certain position in the world except for one player. */
	public void sendToEveryoneNear(World world, double x, double y, double z, double distance,
	                               EntityPlayer except, AbstractPacket packet) {
		for (EntityPlayer player : (List<EntityPlayer>)world.playerEntities) {
			double dx = x - player.posX;
			double dy = y - player.posY;
			double dz = z - player.posZ;
            if ((dx * dx + dy * dy + dz * dz) < (distance * distance))
            	sendToPlayer(player, packet);
            	
		}
	}
	
}

@@ -62,16 +62,63 @@
 yteBuf;%0A
+import io.netty.channel.ChannelFutureListener;%0A
 import i
@@ -156,16 +156,16 @@
 ontext;%0A
-
 %0Aimport 
@@ -1544,17 +1544,18 @@
 ception 
-t
+ex
 ) %7B%0A%09%09%09c
@@ -1650,17 +1650,18 @@
 ());%0A%09%09%09
-t
+ex
 .printSt
@@ -2327,16 +2327,77 @@
 channel)
+.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE)
 ;%0A%09%7D%0A%09%0A%09
@@ -2747,38 +2747,100 @@
 l.writeAndFlush(
-packet
+channel).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE
 );%0A%09%7D%0A%09%0A%09/** Sen
@@ -3389,286 +3389,76 @@
 ush(
-packet);%0A%09%7D%0A%09%0A%09/** Sends a packet to everyone tracking this entity. */%0A%09public void sendToEveryoneTracking(Entity entity, AbstractPacket packet) %7B%0A%09%09((WorldServer)entity.worldObj).getEntityTracker().func_151247_a(%0A%09%09%09%09entity, channels.get(Side.SERVER).generatePacketFrom(packet)
+channel).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE
 );%0A%09
@@ -3799,16 +3799,51 @@
 ties) %7B%0A
+%09%09%09if (player == except) continue;%0A
 %09%09%09doubl
@@ -4048,25 +4048,283 @@
 t);%0A
-            %09%0A%09%09%7D
+%09%09%7D%0A%09%7D%0A%09%0A%09/** Sends a packet to everyone tracking this entity. */%0A%09public void sendToEveryoneTracking(Entity entity, AbstractPacket packet) %7B%0A%09%09((WorldServer)entity.worldObj).getEntityTracker().func_151247_a(%0A%09%09%09%09entity, channels.get(Side.SERVER).generatePacketFrom(packet));
 %0A%09%7D%0A

Fix ChannelHandler using writeAndFlush(packet)