package com.zivilon.cinder_loe.coremod;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.util.Printer;
import org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceMethodVisitor;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Iterator;

import net.minecraft.launchwrapper.IClassTransformer;

public class OptiFinePatcher implements IClassTransformer {
    @Override
    public byte[] transform(String name, String transformedName, byte[] classBytes) {
        if (transformedName.equals("ItemRendererOF")) {
            System.out.println("[CinderLoE] Transforming ItemRendererOF...");

            ClassReader reader = new ClassReader(classBytes);
            ClassNode classNode = new ClassNode();
            reader.accept(classNode, 0);

		    for (MethodNode method : classNode.methods) {
		        if (method.name.equals("func_78443_a") && method.desc.equals("(Lnet/minecraft/entity/EntityLivingBase;Lnet/minecraft/item/ItemStack;I)V")) {
		            InsnList instructions = method.instructions;
		            AbstractInsnNode currentNode = null;
		            Iterator<AbstractInsnNode> nodes = instructions.iterator();

		            int occurrenceCounter = 0;

		            while (nodes.hasNext()) {
		                currentNode = nodes.next();

		                if (currentNode.getOpcode() == Opcodes.INVOKESTATIC) {
		                    MethodInsnNode methodCall = (MethodInsnNode) currentNode;
		                    if (methodCall.owner.equals("ItemRendererOF") && methodCall.name.equals("func_78439_a") && methodCall.desc.equals("(Lnet/minecraft/client/renderer/Tessellator;FFFFIIF)V")) {
		                        occurrenceCounter++;

                                InsnNode bool = null;

                                if (occurrenceCounter == 1) {
		                            bool = new InsnNode(Opcodes.ICONST_0);
		                            instructions.insertBefore(currentNode, bool);
		                            MethodInsnNode newMethodCall = new MethodInsnNode(Opcodes.INVOKESTATIC, "com/zivilon/cinder_loe/client/render/item/RenderHelper", "customRenderItemIn2D", "(Lnet/minecraft/client/renderer/Tessellator;FFFFIIFZ)V", false);
		                            instructions.set(currentNode, newMethodCall);
		                        }

                                if (occurrenceCounter == 2) {
		                            bool = new InsnNode(Opcodes.ICONST_1);
		                            instructions.insertBefore(currentNode, bool);
		                            MethodInsnNode newMethodCall = new MethodInsnNode(Opcodes.INVOKESTATIC, "com/zivilon/cinder_loe/client/render/item/RenderHelper", "customRenderItemIn2D", "(Lnet/minecraft/client/renderer/Tessellator;FFFFIIFZ)V", false);
		                            instructions.set(currentNode, newMethodCall);
		                        }

		                        // Modify only the second occurrence
		                        if (occurrenceCounter == 3) {
		                            bool = new InsnNode(Opcodes.ICONST_1);
		                            instructions.insertBefore(currentNode, bool);
		                            MethodInsnNode newMethodCall = new MethodInsnNode(Opcodes.INVOKESTATIC, "com/zivilon/cinder_loe/client/render/item/RenderHelper", "customRenderItemIn2D", "(Lnet/minecraft/client/renderer/Tessellator;FFFFIIFZ)V", false);
		                            instructions.set(currentNode, newMethodCall);
		                            break;
		                        }
		                    }
		                }
		            }
                    // Color code here
                    int index = -1;

                    for (int i = 0; i < instructions.size(); i++) {
                        currentNode = instructions.get(i);
                        if (currentNode.getOpcode() == Opcodes.LDC && ((LdcInsnNode) currentNode).cst.equals(0.7599999904632568f)) {
                            System.out.println("[CinderLoE-Coremod] Found Optifine code to remove...");
                            index = i;
                            break;
                        }
                    }

                    if (index != -1) {
                        for (int i = 0; i < 15; i++) {
                            AbstractInsnNode targetNode = instructions.get(index);
                            System.out.println(insnToString(targetNode));
                            instructions.remove(targetNode);
                        }

                        InsnList toInject = new InsnList();
                        toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/zivilon/cinder_loe/CinderLoE_Config", "enchantment_color_red", "F"));
                        toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/zivilon/cinder_loe/CinderLoE_Config", "enchantment_color_green", "F"));
                        toInject.add(new FieldInsnNode(Opcodes.GETSTATIC, "com/zivilon/cinder_loe/CinderLoE_Config", "enchantment_color_blue", "F"));
                        toInject.add(new InsnNode(Opcodes.FCONST_1));
                        toInject.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "org/lwjgl/opengl/GL11", "glColor4f", "(FFFF)V", false));

                        instructions.insertBefore(instructions.get(index), toInject);
                        break;
                    }
		        }
	        }

            ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
            classNode.accept(writer); // Liine 104

            byte[] transformedBytes = writer.toByteArray();
            return transformedBytes;
        }
        return classBytes;
    }

    // Method to convert an instruction node to string
    public static String insnToString(AbstractInsnNode insn){
        insn.accept(new TraceMethodVisitor(new Textifier()));
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        Textifier textifier = new Textifier();
        TraceMethodVisitor tmv = new TraceMethodVisitor(textifier);
        insn.accept(tmv);
        textifier.print(pw);
        return sw.toString();
    }
}
