package net.glowstone;

import com.google.common.io.PatternFilenameFilter;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.logging.Level;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Opcodes;

public class GlowPluginTypeDetector {

    public List<File> bukkitPlugins = new ArrayList<>();
    public List<File> spongePlugins = new ArrayList<>();
    public List<File> canaryPlugins = new ArrayList<>();
    public List<File> forgefPlugins = new ArrayList<>();
    public List<File> forgenPlugins = new ArrayList<>();
    public List<File> unrecognizedPlugins = new ArrayList<>();

    private File directory;

    public GlowPluginTypeDetector(File directory) {
        this.directory = directory;
    }

    public void scan() {
        GlowServer.logger.info("Scanning plugins...");
        File[] files = directory.listFiles(new PatternFilenameFilter(".+\\.jar"));
        if (files == null || files.length == 0) {
            return;
        }

        for (File file : files) {
            scanFile(file);
        }

        GlowServer.logger.info("PluginTypeDetector: found " + bukkitPlugins.size() + " Bukkit, " + spongePlugins.size() + " Sponge, " + (forgefPlugins.size() + forgenPlugins.size()) + " Forge, " + canaryPlugins.size() + " Canary, " + unrecognizedPlugins.size() + " unknown plugins (total " + files.length + ")");

        if (!unrecognizedPlugins.isEmpty()) {
            for (File file : unrecognizedPlugins) {
                GlowServer.logger.warning("Unrecognized plugin: " + file.getPath());
            }
        }
    }

    private void scanFile(File file) {
        boolean isBukkit = false;
        boolean isSponge = false;
        boolean isCanary = false;
        boolean isForgeF = false;
        boolean isForgeN = false;
        URL url;

        try {
            url = file.toURI().toURL();
        } catch (MalformedURLException e) {
            GlowServer.logger.log(Level.WARNING, "PluginTypeDetector: Malformed URL: " + file, e);
            return;
        }

        try (ZipFile zip = new ZipFile(file)) {
            Enumeration<? extends ZipEntry> entries = zip.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entryIn = entries.nextElement();
                String name = entryIn.getName();

                if (name.equals("plugin.yml")) {
                    isBukkit = true;
                }

                if (name.equals("Canary.inf")) {
                    isCanary = true;
                }

                if (name.endsWith(".class") && !entryIn.isDirectory()) {
                    // Analyze class file
                    ClassReader classReader = new ClassReader(zip.getInputStream(entryIn));
                    GlowVisitor visitor = new GlowVisitor();
                    classReader.accept(visitor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);

                    if (visitor.isSponge) {
                        isSponge = true;
                    }

                    if (visitor.isForgeF) {
                        isForgeF = true;
                    }

                    if (visitor.isForgeN) {
                        isForgeN = true;
                    }
                }
            }
        } catch (IOException ex) {
            GlowServer.logger.log(Level.WARNING, "PluginTypeDetector: Error reading " + url, ex);
        }

        if (isBukkit) {
            bukkitPlugins.add(file);
        }
        if (isSponge) {
            spongePlugins.add(file);
        }
        if (isCanary) {
            canaryPlugins.add(file);
        }
        if (isForgeF) {
            forgefPlugins.add(file);
        }
        if (isForgeN) {
            forgenPlugins.add(file);
        }

        if (!isBukkit && !isSponge && !isCanary && !isForgeF && !isForgeN) {
            unrecognizedPlugins.add(file);
        }
    }

    private static class GlowVisitor extends ClassVisitor {

        public boolean isSponge;
        public boolean isForgeF;
        public boolean isForgeN;

        public GlowVisitor() {
            super(Opcodes.ASM5);
        }

        @Override
        public AnnotationVisitor visitAnnotation(String name, boolean visible) {
            switch (name) {
                case "Lorg/spongepowered/api/plugin/Plugin;":
                    isSponge = true;
                    break;
                case "Lcpw/mods/fml/common/Mod;":  // older versions
                    isForgeF = true;
                    break;
                case "Lnet/minecraftforge/fml/common/Mod;":  // newer
                    isForgeN = true;
                    break;
            }

            return null;
        }
    }
}

@@ -1015,24 +1015,103 @@
 ory;%0A    %7D%0A%0A
+    /**%0A     * Scans all jars in the plugin directory for their types.%0A     */%0A
     public v
@@ -1119,24 +1119,24 @@
 id scan() %7B%0A
-
         Glow
@@ -1450,38 +1450,185 @@
 nfo(
-%22PluginTypeDetector: found %22 +
+String.format(%0A                %22PluginTypeDetector: found %25d Bukkit, %25d Sponge, %25d Forge, %25d Canary, %22%0A                        + %22%25d unknown plugins (total %25d)%22,%0A               
  buk
@@ -1648,24 +1648,25 @@
 ze()
- + %22 Bukkit, %22 +
+,%0A               
  spo
@@ -1686,26 +1686,26 @@
 ze()
- + %22 Sponge, %22 + (
+,%0A                
 forg
@@ -1747,24 +1747,25 @@
 ze()
-) + %22 Forge, %22 +
+,%0A               
  can
@@ -1785,24 +1785,25 @@
 ze()
- + %22 Canary, %22 +
+,%0A               
  unr
@@ -1829,39 +1829,25 @@
 ze()
- + %22 unknown plugins (total %22 +
+,%0A               
  fil
@@ -1859,14 +1859,9 @@
 ngth
- + %22)%22
+)
 );%0A%0A
@@ -3349,16 +3349,44 @@
 KIP_CODE
+%0A                           
  %7C Class
@@ -5145,32 +5145,32 @@
 sForgeN = true;%0A
-
                 
@@ -5172,32 +5172,91 @@
          break;%0A
+                default:%0A                    // do nothing%0A
             %7D%0A%0A 

Fix CheckStyle issues in GlowPluginTypeDetector.java (#711)