package com.ttaylorr.uhc.pvp.util;

import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;

import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class PlayerDataManager implements Listener {
    private Map<Player, PlayerData> playerDataMap;
    private Map<Class<?>, Factory<?>> factories;
    public PlayerDataManager() {
        playerDataMap = new HashMap<>();
        factories = new HashMap<>();
    }
    public <T> T get(Player player, Class<T> klass) {
        PlayerData playerData = get(player);
        return playerData.get(klass);
    }
    public PlayerData get(Player player) {
        return playerDataMap.get(player);
    }
    public <T> void set(Player player, T data) {
        PlayerData playerData = playerDataMap.get(player);
        playerData.set(data);
    }
    public <T> void setFactory(Class<T> klass, Factory<T> factory) {
        factories.put(klass, factory);
    }

    @EventHandler
    private void onPlayerJoin(PlayerJoinEvent pje) {
        playerDataMap.put(pje.getPlayer(), new PlayerData(this, pje.getPlayer()));
    }

    @EventHandler
    private void onPlayerQuit(PlayerQuitEvent pqe) {
        playerDataMap.remove(pqe.getPlayer());
    }

    @SuppressWarnings("unchecked")
    public <T> Factory<T> getFactory(Class<T> klass) {
        if(!factories.containsKey(klass)) {
            factories.put(klass, getDefaultFactory(klass));
        }
        return (Factory<T>) factories.get(klass);
    }

    private <T> Factory<T> getDefaultFactory(Class<T> klass) {
        Factory<T> defaultFactory = tryConstructFactory(klass);
        if(defaultFactory == null)
            defaultFactory = new NullFactory<>();
        return defaultFactory;
    }

    @SuppressWarnings("unchecked")
    public static <T> Factory<T> tryConstructFactory(Class<T> klass) {
        for(Constructor<T> constructor : (Constructor<T>[])klass.getConstructors()) {
            Class<?>[] parameterTypes = constructor.getParameterTypes();
            if(parameterTypes.length == 0)
                return new DefaultFactory<>(constructor);
            else if(parameterTypes.length == 1 && parameterTypes[0].equals(Player.class))
                return new DefaultFactoryWithPlayer<>(constructor);
        }
        return new NullFactory<>();
    }

    static class DefaultFactory<T> implements Factory<T> {
        Constructor<T> constructor;

        private DefaultFactory(Constructor<T> constructor) {
            this.constructor = constructor;
        }

        @Override
        public T construct(Player player) throws Exception {
            return constructor.newInstance();
        }
    }

    static class DefaultFactoryWithPlayer<T> implements Factory<T> {
        Constructor<T> constructor;

        private DefaultFactoryWithPlayer(Constructor<T> constructor) {
            this.constructor = constructor;
        }

        @Override
        public T construct(Player player) throws Exception {
            return constructor.newInstance(player);
        }
    }

    static class NullFactory<T> implements Factory<T> {
        @Override
        public T construct(Player player) throws Exception {
            return null;
        }
    }

    static interface Factory<V> {
        V construct(Player player) throws Exception;
    }
}

@@ -29,16 +29,71 @@
 .util;%0A%0A
+import org.bukkit.Bukkit;%0Aimport org.bukkit.ChatColor;%0A
 import o
@@ -151,24 +151,63 @@
 entHandler;%0A
+import org.bukkit.event.EventPriority;%0A
 import org.b
@@ -276,16 +276,65 @@
 nEvent;%0A
+import org.bukkit.event.player.PlayerLoginEvent;%0A
 import o
@@ -721,24 +721,110 @@
 ashMap%3C%3E();%0A
+        for(Player player : Bukkit.getOnlinePlayers())%0A            subscribe(player);%0A
     %7D%0A    pu
@@ -1318,182 +1318,1020 @@
     
-@EventHandler%0A    private void onPlayerJoin(PlayerJoinEvent pje) %7B%0A        playerDataMap.put(pje.getPlayer(), new PlayerData(this, pje.getPlayer()));%0A    %7D%0A%0A    @EventHandler
+private void subscribe(Player player) %7B%0A        Debug.info(%22PlayerDataManager: %22%0A                + ChatColor.GREEN + ChatColor.BOLD + %22+ %22%0A                + ChatColor.RESET + player.getName());%0A        playerDataMap.put(player, new PlayerData(this, player));%0A    %7D%0A%0A    private void unsubscribe(Player player) %7B%0A        Debug.info(%22PlayerDataManager: %22%0A                + ChatColor.RED + ChatColor.BOLD + %22- %22%0A                + ChatColor.RESET + player.getName());%0A        playerDataMap.remove(player);%0A    %7D%0A%0A    /**%0A     * Create player data before the PlayerJoinEvent.%0A     * @param ple The event%0A     */%0A    @EventHandler(priority = EventPriority.MONITOR)%0A    private void onPlayerLogin(PlayerLoginEvent ple) %7B%0A        if(ple.getResult() != PlayerLoginEvent.Result.ALLOWED)%0A            return;%0A        Player player = ple.getPlayer();%0A        subscribe(player);%0A    %7D%0A%0A    /**%0A     * Clean up player data when he logs out%0A     * @param pqe The event%0A     */%0A    @EventHandler(priority = EventPriority.MONITOR)
 %0A   
@@ -2392,44 +2392,67 @@
     
-p
+P
 layer
-DataMap.remove(pqe.getPlayer()
+ player = pqe.getPlayer();%0A        unsubscribe(player
 );%0A 
@@ -2730,16 +2730,23 @@
 private 
+static 
 %3CT%3E Fact
@@ -3548,32 +3548,40 @@
 %3E();%0A    %7D%0A%0A    
+private 
 static class Def
@@ -3843,32 +3843,151 @@
 ows Exception %7B%0A
+            Debug.info(%22PlayerDataManager: Constructed for player %22 + player.getName() + %22 %22 + constructor.getName());%0A
             retu
@@ -4029,32 +4029,40 @@
    %7D%0A    %7D%0A%0A    
+private 
 static class Def

Fix some NPE's in PlayerDataManager Add logging in PlayerDataManager