/*
 * This file is part of Sponge, licensed under the MIT License (MIT).
 *
 * Copyright (c) SpongePowered <https://www.spongepowered.org>
 * Copyright (c) contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.spongepowered.common.event.filter.delegate;

import static org.objectweb.asm.Opcodes.ACONST_NULL;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ARETURN;
import static org.objectweb.asm.Opcodes.GETFIELD;
import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;

import java.lang.reflect.Method;

public abstract class SubtypeFilterDelegate implements FilterDelegate {

    public void createFields(ClassWriter cw) {
        FieldVisitor fv = cw.visitField(0, "classes", "Ljava/util/Set;", "Ljava/util/Set<Ljava/lang/Class<*>;>;", null);
        fv.visitEnd();
    }

    @Override
    public int write(String name, ClassWriter cw, MethodVisitor mv, Method method, int locals) {

        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(GETFIELD, name, "classes", "Ljava/util/Set;");
        mv.visitVarInsn(ALOAD, 1);
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
        mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "contains", "(Ljava/lang/Object;)Z", true);
        Label success = new Label();
        mv.visitJumpInsn(getJumpOp(), success);
        mv.visitInsn(ACONST_NULL);
        mv.visitInsn(ARETURN);
        mv.visitLabel(success);
        return locals;
    }

    public abstract void writeCtor(String name, ClassWriter cw, MethodVisitor mv);

    protected abstract int getJumpOp();

}

@@ -1487,24 +1487,215 @@
 Opcodes.
-GETFIELD
+ASTORE;%0Aimport static org.objectweb.asm.Opcodes.CHECKCAST;%0Aimport static org.objectweb.asm.Opcodes.GETFIELD;%0Aimport static org.objectweb.asm.Opcodes.GOTO;%0Aimport static org.objectweb.asm.Opcodes.IFNE
 ;%0Aimport
@@ -2371,254 +2371,646 @@
 ) %7B%0A
-%0A        mv.visitVarInsn(ALOAD, 0);%0A        mv.visitFieldInsn(GETFIELD, name, %22classes%22, %22Ljava/util/Set;%22);%0A        mv.visitVarInsn(ALOAD, 1);%0A        mv.visitMethodInsn(INVOKEVIRTUAL, %22java/lang/Object%22, %22getClass%22, %22()Ljava/lang/Class;%22, false
+        // TODO could do an optimization here to inline a single instanceof if%0A        // the set would contain only a single member%0A        mv.visitVarInsn(ALOAD, 0);%0A        mv.visitFieldInsn(GETFIELD, name, %22classes%22, %22Ljava/util/Set;%22);%0A        // Loop through the classes set's members%0A        mv.visitMethodInsn(INVOKEINTERFACE, %22java/util/Set%22, %22iterator%22, %22()Ljava/util/Iterator;%22, true);%0A        mv.visitVarInsn(ASTORE, locals);%0A        Label continueLabel = new Label();%0A        mv.visitJumpInsn(GOTO, continueLabel);%0A        Label loopStart = new Label();%0A        mv.visitLabel(loopStart);%0A        mv.visitVarInsn(ALOAD, locals
 );%0A 
@@ -3067,28 +3067,30 @@
 til/
-Set%22, %22contains
+Iterator%22, %22next
 %22, %22(
+)
 Ljav
@@ -3107,10 +3107,8 @@
 ect;
-)Z
 %22, t
@@ -3125,34 +3125,196 @@
     
-Label success = new Label(
+mv.visitTypeInsn(CHECKCAST, %22java/lang/Class%22);%0A        mv.visitVarInsn(ALOAD, 1);%0A        mv.visitMethodInsn(INVOKEVIRTUAL, %22java/lang/Class%22, %22isInstance%22, %22(Ljava/lang/Object;)Z%22, false
 );%0A 
@@ -3350,23 +3350,29 @@
 mpOp(), 
-success
+continueLabel
 );%0A     
@@ -3458,15 +3458,195 @@
 bel(
-success
+continueLabel);%0A        mv.visitVarInsn(ALOAD, locals);%0A        mv.visitMethodInsn(INVOKEINTERFACE, %22java/util/Iterator%22, %22hasNext%22, %22()Z%22, true);%0A        mv.visitJumpInsn(IFNE, loopStart
 );%0A 
@@ -3665,16 +3665,20 @@
 n locals
+ + 1
 ;%0A    %7D%0A

Change SubtypeFilterDelegate to a loop, fixes #643