importClass(net.minecraftforge.common.MinecraftForge);
importPackage(net.minecraftforge.fml.common.eventhandler);
importPackage(org.objectweb.asm);
importClass(java.lang.ClassLoader);
importClass(org.apache.commons.lang3.RandomStringUtils);
importClass(java.util.function.Consumer);
importClass(net.minecraftforge.fml.common.eventhandler.EventPriority);

const L=(s)=>`L${s};`;
const LoadedInsts=[];

function defineClassBytes(name,bytes){
const classLoader=Packages.com.chattriggers.ctjs.CTJS.class.getClassLoader();

let defClass;
ClassLoader.class.getDeclaredMethods().forEach((m)=>{
if(m.toString()==="protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int) throws java.lang.ClassFormatError"){
defClass=m;
}
});

defClass.setAccessible(true);

const n=new java.lang.String(name);
const o=new java.lang.Integer(0);
const s=new java.lang.Integer(bytes.length);
return defClass.invoke(classLoader,n,bytes,o,s);
}

const registerForge=(e,priority=EventPriority.NORMAL,cb)=>{
const cw=new ClassWriter(0);

const event=Type.getType(e.class).internalName;
const name=RandomStringUtils.randomAlphabetic(7);

const consumer=Type.getType(Consumer.class).internalName;
const mcForge=Type.getType(MinecraftForge.class).internalName;
const eventBus=Type.getType(EventBus.class).internalName;
const subscribeEvent=Type.getType(SubscribeEvent.class).internalName;
const obj=Type.getType(java.lang.Object.class).internalName;

cw.visit(Opcodes.V1_8,Opcodes.ACC_PUBLIC,name,null,obj,null);


cw.visitField(Opcodes.ACC_PRIVATE+Opcodes.ACC_FINAL,"callback",L(consumer),L(consumer+"<"+L(event)+">"),null).visitEnd();


const con=cw.visitMethod(Opcodes.ACC_PUBLIC,"<init>","("+L(consumer)+")V","("+L(consumer+"<"+L(event)+">")+")V",null);
con.visitCode();
con.visitVarInsn(Opcodes.ALOAD,0);
con.visitMethodInsn(Opcodes.INVOKESPECIAL,obj,"<init>","()V",false);

con.visitVarInsn(Opcodes.ALOAD,0);
con.visitVarInsn(Opcodes.ALOAD,1);
con.visitFieldInsn(Opcodes.PUTFIELD,name,"callback",L(consumer));
con.visitFieldInsn(Opcodes.GETSTATIC,mcForge,"EVENT_BUS",L(eventBus));
con.visitVarInsn(Opcodes.ALOAD,0);
con.visitMethodInsn(Opcodes.INVOKEVIRTUAL,eventBus,"register","("+L(obj)+")V",false);
con.visitInsn(Opcodes.RETURN);
con.visitMaxs(2,2);
con.visitEnd();


const mv=cw.visitMethod(Opcodes.ACC_PUBLIC,"on","("+L(event)+")V",null,null);

const av=mv.visitAnnotation(L(subscribeEvent),true);
av.visitEnum("priority","Lnet/minecraftforge/fml/common/eventhandler/EventPriority;",priority.name());
av.visitEnd();

mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD,0);
mv.visitFieldInsn(Opcodes.GETFIELD,name,"callback",L(consumer));
mv.visitVarInsn(Opcodes.ALOAD,1);
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE,consumer,"accept","("+L(obj)+")V",true);
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(2,2);
mv.visitEnd();

cw.visitEnd();

const inst=defineClassBytes(name,cw.toByteArray()).
getDeclaredConstructor(Consumer.class).
newInstance(new java.util.function.Consumer({
accept:function(t){cb(t)}}));

LoadedInsts.push(inst);
return inst;
};

const unregisterForge=(inst)=>{
MinecraftForge.EVENT_BUS.unregister(inst);
};

register("gameUnload",()=>{
LoadedInsts.forEach(unregisterForge);
LoadedInsts.length=0;
});

export{registerForge,unregisterForge};