package net.glowstone;

import com.google.common.io.PatternFilenameFilter;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Opcodes;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class GlowPluginTypeDetector {

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

    private File directory;
    private Logger logger;

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

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

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

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

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

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

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

        try (InputStream fileIn = url.openStream();
             ZipInputStream zipIn = new ZipInputStream(fileIn)
        ) {
            ZipEntry entryIn;
            while ((entryIn = zipIn.getNextEntry()) != null) {
                String name = entryIn.getName();

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

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

                if (name.endsWith(".class") && !entryIn.isDirectory()) {
                    // Read class file TODO: buffer
                    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                    int b;
                    do {
                        b = zipIn.read();
                        byteArrayOutputStream.write(b);
                    } while (b != -1);

                    ClassReader classReader = new ClassReader(byteArrayOutputStream.toByteArray());
                    GlowVisitor visitor = new GlowVisitor();
                    classReader.accept(visitor, 0);

                    if (visitor.isSponge) {
                        isSponge = true;
                    }
                }
            }
        } catch (IOException ex) {
            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 (!isBukkit && !isSponge && !isCanary) unrecognizedPlugins.add(file);
    }

    private class GlowVisitor extends ClassVisitor {
        public boolean isSponge;

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

        @Override
        public AnnotationVisitor visitAnnotation(String name, boolean visible) {
            if (name.equals("Lorg/spongepowered/api/plugin/Plugin;")) {
                isSponge = true;
            }

            return null;
        }
    }
}

@@ -786,32 +786,146 @@
  ArrayList%3C%3E();%0A
+    public List%3CFile%3E forgefPlugins = new ArrayList%3C%3E();%0A    public List%3CFile%3E forgenPlugins = new ArrayList%3C%3E();%0A
     public List%3C
@@ -1583,24 +1583,101 @@
 Sponge, %22 +%0A
+                (forgefPlugins.size() + forgenPlugins.size()) + %22 Forge, %22 +%0A
             
@@ -2150,32 +2150,100 @@
 Canary = false;%0A
+        boolean isForgeF = false;%0A        boolean isForgeN = false;%0A
         URL url;
@@ -3663,32 +3663,248 @@
                %7D
+%0A%0A                    if (visitor.isForgeF) %7B%0A                        isForgeF = true;%0A                    %7D%0A%0A                    if (visitor.isForgeN) %7B%0A                        isForgeN = true;%0A                    %7D
 %0A               
@@ -4193,16 +4193,110 @@
 d(file);
+%0A        if (isForgeF) forgefPlugins.add(file);%0A        if (isForgeN) forgenPlugins.add(file);
 %0A%0A      
@@ -4336,16 +4336,42 @@
 isCanary
+ && !isForgeF && !isForgeN
 ) unreco
@@ -4487,16 +4487,82 @@
 sSponge;
+%0A        public boolean isForgeF;%0A        public boolean isForgeN;
 %0A%0A      
@@ -4839,32 +4839,269 @@
 e;%0A            %7D
+ else if (name.equals(%22Lcpw/mods/fml/common/Mod;%22)) %7B // older versions%0A                isForgeF = true;%0A            %7D else if (name.equals(%22Lnet/minecraftforge/fml/common/Mod;%22)) %7B // newer%0A                isForgeN = true;%0A            %7D
 %0A%0A            re

Add Forge/FML plugin detection