package net.md_5.itag;

import com.comphenix.protocol.Packets;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.ConnectionSide;
import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketEvent;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.Futures;
import java.util.Set;
import java.util.concurrent.Callable;
import lombok.Getter;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.kitteh.tag.PlayerReceiveNameTagEvent;
import org.kitteh.tag.TagAPI;

public class iTag extends JavaPlugin
{

    @Getter
    private static iTag instance;
    private TagAPI tagAPI;

    @Override
    public void onEnable()
    {
        instance = this;
        tagAPI = new TagAPI();
        tagAPI.install( this );

        ProtocolLibrary.getProtocolManager().addPacketListener( new PacketAdapter( this, ConnectionSide.SERVER_SIDE, ListenerPriority.NORMAL, Packets.Server.NAMED_ENTITY_SPAWN )
        {
            @Override
            public void onPacketSending(PacketEvent event)
            {
                event.getPacket().getSpecificModifier( String.class ).write( 0, getSentName(
                        event.getPacket().getSpecificModifier( int.class ).read( 0 ),
                        event.getPacket().getSpecificModifier( String.class ).read( 0 ),
                        event.getPlayer() ) );
            }
        } );
    }

    @Override
    public void onDisable()
    {
        ProtocolLibrary.getProtocolManager().removePacketListeners( this );
        tagAPI.uninstall();

        tagAPI = null;
        instance = null;
    }

    String getSentName(int sentEntityId, String sentName, Player destinationPlayer)
    {
        Player namedPlayer = null;
        for ( Player player : getServer().getOnlinePlayers() )
        {
            if ( player.getEntityId() == sentEntityId )
            {
                namedPlayer = player;
                break;
            }
        }

        final PlayerReceiveNameTagEvent event = new PlayerReceiveNameTagEvent( destinationPlayer, namedPlayer, sentName );
        if ( getServer().isPrimaryThread() )
        {
            getServer().getPluginManager().callEvent( event );
        } else
        {
            Futures.getUnchecked( getServer().getScheduler().callSyncMethod( this, new Callable<Void>()
            {
                public Void call() throws Exception
                {
                    getServer().getPluginManager().callEvent( event );
                    return null;
                }
            } ) );
        }

        return event.getTag().substring( 0, Math.min( event.getTag().length(), 16 ) );
    }

    public void refreshPlayer(Player player)
    {
        Preconditions.checkState( isEnabled(), "Not Enabled!" );
        Preconditions.checkNotNull( player, "player" );

        for ( Player playerFor : getServer().getOnlinePlayers() )
        {
            refreshPlayer( player, playerFor );
        }
    }

    public void refreshPlayer(final Player player, final Player forWhom)
    {
        Preconditions.checkState( isEnabled(), "Not Enabled!" );
        Preconditions.checkNotNull( player, "player" );
        Preconditions.checkNotNull( forWhom, "forWhom" );

        if ( player != forWhom && player.getWorld() == forWhom.getWorld() && forWhom.canSee( player ) )
        {
            forWhom.hidePlayer( player );
            getServer().getScheduler().scheduleSyncDelayedTask( this, new Runnable()
            {
                public void run()
                {
                    if ( player.isOnline() && forWhom.isOnline() && player.getWorld() == forWhom.getWorld() )
                    {
                        forWhom.showPlayer( player );
                    }
                }
            }, 2 );
        }
    }

    public void refreshPlayer(Player player, Set<Player> forWhom)
    {
        Preconditions.checkState( isEnabled(), "Not Enabled!" );
        Preconditions.checkNotNull( player, "player" );
        Preconditions.checkNotNull( forWhom, "forWhom" );

        for ( Player playerFor : forWhom )
        {
            refreshPlayer( player, playerFor );
        }
    }
}

@@ -425,19 +425,71 @@
 va.util.
-Set
+Map;%0Aimport java.util.Set;%0Aimport java.util.WeakHashMap
 ;%0Aimport
@@ -571,24 +571,192 @@
 ity.Player;%0A
+import org.bukkit.event.EventHandler;%0Aimport org.bukkit.event.Listener;%0Aimport org.bukkit.event.player.PlayerJoinEvent;%0Aimport org.bukkit.event.player.PlayerQuitEvent;%0A
 import org.b
@@ -901,16 +901,36 @@
 vaPlugin
+ implements Listener
 %0A%7B%0A%0A    
@@ -997,16 +997,62 @@
  tagAPI;
+%0A    private Map%3CInteger, Player%3E entityIdMap;
 %0A%0A    @O
@@ -1117,16 +1117,74 @@
 = this;%0A
+        entityIdMap = new WeakHashMap%3CInteger, Player%3E();%0A
         
@@ -1235,24 +1235,93 @@
 l( this );%0A%0A
+        getServer().getPluginManager().registerEvents( this, this );%0A
         Prot
@@ -1936,24 +1936,320 @@
 %7D );%0A    %7D%0A%0A
+    @EventHandler%0A    public void onJoin(PlayerJoinEvent event)%0A    %7B%0A        entityIdMap.put( event.getPlayer().getEntityId(), event.getPlayer() );%0A    %7D%0A%0A    @EventHandler%0A    public void onQuit(PlayerQuitEvent event)%0A    %7B%0A        entityIdMap.remove( event.getPlayer().getEntityId() );%0A    %7D%0A%0A
     @Overrid
@@ -2385,24 +2385,81 @@
 install();%0A%0A
+        entityIdMap.clear();%0A        entityIdMap = null;%0A
         tagA
@@ -2505,16 +2505,24 @@
  %7D%0A%0A    
+private 
 String g
@@ -2603,272 +2603,8 @@
   %7B%0A
-        Player namedPlayer = null;%0A        for ( Player player : getServer().getOnlinePlayers() )%0A        %7B%0A            if ( player.getEntityId() == sentEntityId )%0A            %7B%0A                namedPlayer = player;%0A                break;%0A            %7D%0A        %7D%0A%0A
     
@@ -2701,19 +2701,39 @@
 er, 
-namedPlayer
+entityIdMap.get( sentEntityId )
 , se
@@ -3929,51 +3929,8 @@
 m &&
- player.getWorld() == forWhom.getWorld() &&
  for
@@ -4224,51 +4224,8 @@
 ne()
- && player.getWorld() == forWhom.getWorld()
  )%0A 

Use a map to store entity id to player mappings - getPlayers is weird on respawn