package cloud.swiftnode.ksecurity.module.kvaccine;

import cloud.swiftnode.ksecurity.KSecurity;
import cloud.swiftnode.ksecurity.abstraction.StorageCountDownLatch;
import cloud.swiftnode.ksecurity.abstraction.mock.MockPlugin;
import cloud.swiftnode.ksecurity.module.Module;
import cloud.swiftnode.ksecurity.module.kgui.abstraction.gui.KAlert;
import cloud.swiftnode.ksecurity.module.kvaccine.abstraction.intercepter.KHandlerList;
import cloud.swiftnode.ksecurity.module.kvaccine.abstraction.intercepter.KOperatorMap;
import cloud.swiftnode.ksecurity.module.kvaccine.abstraction.intercepter.KOperatorSet;
import cloud.swiftnode.ksecurity.module.kvaccine.abstraction.intercepter.KProxySelector;
import cloud.swiftnode.ksecurity.module.kvaccine.abstraction.processor.VirusScanProcessor;
import cloud.swiftnode.ksecurity.util.Lang;
import cloud.swiftnode.ksecurity.util.Reflections;
import cloud.swiftnode.ksecurity.util.Static;
import cloud.swiftnode.ksecurity.util.injector.HandlerInjector;
import cloud.swiftnode.ksecurity.util.injector.OpInterceptInjector;
import cloud.swiftnode.ksecurity.util.injector.PluginManagerInjector;
import cloud.swiftnode.ksecurity.util.injector.ProxyInjector;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.event.HandlerList;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;

import java.lang.reflect.Field;
import java.net.ProxySelector;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;


/**
 * Created by Junhyeong Lim on 2017-01-31.
 */
public class KVaccine extends Module {
    private final CountDownLatch latch = new CountDownLatch(1);

    public KVaccine(JavaPlugin parent) {
        super(parent);
    }

    @Override
    public void onEnable() throws Exception {
        // Virus Scan
        Static.runTaskAsync(new VirusScanProcessor()::process);
        // Delayed
        Static.runTask(() -> OpInterceptInjector.inject(latch));
    }

    @Override
    public String getSimpleVersion() {
        return "1.1";
    }

    @Override
    public void onLoad() throws Exception {
        // ETC
        try {
            inject();
        } catch (Exception ex) {
            // Ignore
        }

        // PluginManager
        HashStorage hash = new HashStorage();
        hash.setHash(PluginManagerInjector.process());
        startWatcherThread(hash);
    }

    @SuppressWarnings("unchecked")
    private void startWatcherThread(HashStorage storage) throws Exception {
        Object playerList = Reflections.getDecFieldObj(Bukkit.getServer(), "playerList");
        Class playerListCls = playerList.getClass().getSuperclass();
        Field fieldA = playerListCls.getDeclaredField("operators");
        fieldA.setAccessible(true);
        Field fieldB = Bukkit.getServer().getClass().getDeclaredField("pluginManager");
        fieldB.setAccessible(true);

        new Thread(() -> {
            try {
                if (latch != null
                        && latch.getClass() == CountDownLatch.class) {
                    latch.await(30, TimeUnit.SECONDS);
                }
            } catch (InterruptedException e) {
                // Ignore
            }
            while (true) {
                try {
                    Object objA = fieldA.get(playerList);
                    Object objB = fieldB.get(Bukkit.getServer());
                    Object objC = getOperatorMap(objA);
                    boolean list = false;
                    Plugin findPlugin = new MockPlugin(false);

                    for (HandlerList handler : HandlerList.getHandlerLists()) {
                        if (!(handler instanceof KHandlerList)) {
                            list = true;
                            break;
                        }
                    }

                    for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) {
                        if (plugin.equals(KSecurity.inst)) {
                            findPlugin = plugin;
                        }
                    }

                    if (!(ProxySelector.getDefault() instanceof KProxySelector)) {
                        ProxySelector.setDefault(new KProxySelector(ProxySelector.getDefault()));
                    }

                    if (!(objA instanceof KOperatorSet) && !(objC instanceof KOperatorMap)
                            || objB.hashCode() != storage.getHash()
                            || KSecurity.inst == null
                            || !KSecurity.inst.isEnabled()
                            || !findPlugin.isEnabled()
                            || list) {
                        detect(Lang.DAMAGE_DETECT.builder(), storage);
                    }
                    Thread.sleep(5000);
                } catch (InterruptedException ex) {
                    Static.consoleMsg(ex);
                    shutdown();
                } catch (Exception ex) {
                    Static.consoleMsg(ex);
                    detect(Lang.DAMAGE_EXCEPTION_DETECT.builder().addKey(Lang.Key.VALUE).addVal(ex.toString()), storage);
                }
            }
        }).start();
    }

    private void detect(Lang.MessageBuilder builder, HashStorage storage) {
        try {
            StorageCountDownLatch<Optional<ButtonType>> latch = new StorageCountDownLatch<>(1);
            Static.log(builder);
            new KAlert().setType(Alert.AlertType.ERROR)
                    .setButton(ButtonType.YES, ButtonType.NO)
                    .setContextText(builder.flatBuild())
                    .showAndWait(latch);
            latch.await();
            Optional<ButtonType> btn = latch.getValue();

            if (btn.isPresent() && btn.get() == ButtonType.YES) {
                shutdown();
            } else {
                inject();

                storage.setHash(PluginManagerInjector.process());
                PluginManager manager = Bukkit.getPluginManager();
                Plugin plugin = manager.getPlugin("K-Security");
                List<Plugin> plugins = new ArrayList<>(Arrays.asList(Bukkit.getPluginManager().getPlugins()));
                if (plugin == null) {
                    plugin = new MockPlugin();
                    plugin.onEnable();
                }
                if (!plugins.contains(plugin)) {
                    addPlugin(manager, plugin);
                }
                if (!plugin.isEnabled()) {
                    Reflections.setDecField(JavaPlugin.class, plugin, "isEnabled", true);
                }
                KSecurity.inst = plugin;
            }
        } catch (InterruptedException ex) {
            // Ignore
        } catch (Exception ex) {
            shutdown();
        }
    }

    @SuppressWarnings("unchecked")
    public void addPlugin(PluginManager manager, Plugin plugin) throws NoSuchFieldException, IllegalAccessException {
        List<Plugin> plugins = (List<Plugin>) Reflections.getDecFieldObj(manager, "plugins");
        plugins.add(plugin);
        Reflections.setDecField(parent, "plugins", plugins);
    }

    private void inject() throws Exception {
        ProxyInjector.inject();
        HandlerInjector.inject();
        OpInterceptInjector.inject();
    }

    private void shutdown() {
        Bukkit.savePlayers();
        for (World world : Bukkit.getWorlds()) {
            world.save();
        }
        Bukkit.shutdown();
    }

    private Object getOperatorMap(Object playerList) {
        try {
            return Reflections.getDecFieldObj(playerList.getClass().getSuperclass(), playerList, "d");
        } catch (Exception ex) {
            return new Object();
        }
    }

    private class HashStorage {
        private int hash;

        public int getHash() {
            return hash;
        }

        public void setHash(int hash) {
            this.hash = hash;
        }
    }
}

@@ -3448,32 +3448,132 @@
           try %7B%0A
+                    if (!Static.isRunning())%0A                        continue;%0A                    %0A
                 

Add is server running check condition