package shadersmod.transform;

import java.util.Iterator;
import java.util.ListIterator;

import org.lwjgl.opengl.GL12;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import static org.objectweb.asm.Opcodes.*;

import org.objectweb.asm.commons.Remapper;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.MethodNode;

import shadersmod.common.SMCLog;
import net.minecraft.launchwrapper.IClassTransformer;

// ModelRenderer

public class SMCCTModelRenderer implements IClassTransformer {

	@Override
	public byte[] transform(String par1, String par2, byte[] par3) {
		SMCLog.fine("transforming %s %s",par1,par2);
		ClassReader cr = new ClassReader(par3);
		ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
		CVTransform cv = new CVTransform(cw);
		cr.accept(cv,0);
		return cw.toByteArray();
	}

	private static class CVTransform extends ClassVisitor
	{
		String classname;
		public CVTransform(ClassVisitor cv)
		{
			super(Opcodes.ASM4, cv);		
		}

		boolean fieldsAdded = false;

		@Override
		public void visit(int version, int access, String name,
				String signature, String superName, String[] interfaces) {
			classname = name;
			//SMCLog.info(" class %s",name);
			cv.visit(version, access, name, signature, superName, interfaces);
		}

		@Override
		public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
//			if (Names.modelRenderer_compiled.name.equals(name) || 
//				Names.modelRenderer_displayList.name.equals(name)) {
//				access = access & ~(ACC_PUBLIC|ACC_PRIVATE|ACC_PROTECTED) | ACC_PUBLIC;
//			}
			if ("savedScale".equals(name)) {
				return null;
			}
			return cv.visitField(access, name, desc, signature, value);
		}
		
		@Override
		public MethodVisitor visitMethod(int access, String name, String desc,
				String signature, String[] exceptions) 
		{
			if (!fieldsAdded) {
				fieldsAdded = true;
				FieldVisitor fv = cv.visitField(0, "savedScale", "F", null, null);
				fv.visitEnd();
			}
			//SMCLog.info("  method %s.%s%s = %s",classname,name,desc,remappedName);
//			if (Names.modelRenderer_render.equalsNameDesc(name, desc))
//			{
//				//SMCLog.finer("  patching method %s.%s%s = %s",classname,name,desc,remappedName);
//				return new MVrender(
//						cv.visitMethod(access, name, desc, signature, exceptions));
//			}
//			else
//			if (Names.modelRenderer_renderWithRotation.equalsNameDesc(name, desc))
//			{
//				//SMCLog.finer("  patching method %s.%s%s = %s",classname,name,desc,remappedName);
//				return new MVrender(
//						cv.visitMethod(access, name, desc, signature, exceptions));
//			}
//			else
			if (Names.modelRenderer_compileDisplayList.equalsNameDesc(name, desc))
			{
				//SMCLog.finer("  patching method %s.%s%s = %s",classname,name,desc,remappedName);
				return new MVcompile(
						cv.visitMethod(access, name, desc, signature, exceptions));
			}
			else
			if ("resetDisplayList".equals(name) && "()V".equals(desc)
				|| "getCompiled".equals(name) && "()Z".equals(desc)
				|| "getDisplayList".equals(name) && "()I".equals(desc)
					) {
				return null;
			}
			return cv.visitMethod(access, name, desc, signature, exceptions);
		}

		@Override
		public void visitEnd() {
			MethodVisitor mv;
			{
				mv = cv.visitMethod(ACC_PUBLIC, "getCompiled", "()Z", null, null);
				mv.visitCode();
				mv.visitVarInsn(ALOAD, 0);
				mv.visitFieldInsn(GETFIELD, Names.modelRenderer_compiled.clasName, Names.modelRenderer_compiled.name, Names.modelRenderer_compiled.desc);
				mv.visitInsn(IRETURN);
				mv.visitMaxs(1, 1);
				mv.visitEnd();
			}
			{
				mv = cv.visitMethod(ACC_PUBLIC, "getDisplayList", "()I", null, null);
				mv.visitCode();
				mv.visitVarInsn(ALOAD, 0);
				mv.visitFieldInsn(GETFIELD, Names.modelRenderer_displayList.clasName, Names.modelRenderer_displayList.name, Names.modelRenderer_displayList.desc);
				mv.visitInsn(IRETURN);
				mv.visitMaxs(1, 1);
				mv.visitEnd();
			}
			{
				mv = cv.visitMethod(ACC_PUBLIC, "resetDisplayList", "()V", null, null);
				mv.visitCode();
				mv.visitVarInsn(ALOAD, 0);
				mv.visitFieldInsn(GETFIELD, Names.modelRenderer_compiled.clasName, Names.modelRenderer_compiled.name, Names.modelRenderer_compiled.desc);
				Label l1 = new Label();
				mv.visitJumpInsn(IFEQ, l1);
				//mv.visitVarInsn(ALOAD, 0);
				//mv.visitFieldInsn(GETFIELD, Names.modelRenderer_displayList.clas, Names.modelRenderer_displayList.name, Names.modelRenderer_displayList.desc);
				//mv.visitMethodInsn(INVOKESTATIC, Names.glAllocation_deleteDisplayLists.clas, Names.glAllocation_deleteDisplayLists.name, Names.glAllocation_deleteDisplayLists.desc);
				//mv.visitVarInsn(ALOAD, 0);
				//mv.visitInsn(ICONST_0);
				//mv.visitFieldInsn(PUTFIELD, Names.modelRenderer_displayList.clas, Names.modelRenderer_displayList.name, Names.modelRenderer_displayList.desc);
				mv.visitVarInsn(ALOAD, 0);
				mv.visitInsn(ICONST_0);
				mv.visitFieldInsn(PUTFIELD, Names.modelRenderer_compiled.clasName, Names.modelRenderer_compiled.name, Names.modelRenderer_compiled.desc);
				mv.visitVarInsn(ALOAD, 0);
				mv.visitVarInsn(ALOAD, 0);
				mv.visitFieldInsn(GETFIELD, Names.modelRenderer_.clasName, "savedScale", "F");
				mv.visitMethodInsn(INVOKESPECIAL, Names.modelRenderer_compileDisplayList.clasName, Names.modelRenderer_compileDisplayList.name, Names.modelRenderer_compileDisplayList.desc);
				mv.visitLabel(l1);
				mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
				mv.visitInsn(RETURN);
				mv.visitMaxs(2, 1);
				mv.visitEnd();
			}
			super.visitEnd();
		}
		
	}

	private static class MVcompile extends MethodVisitor
	{
		public MVcompile(MethodVisitor mv) {
			super(Opcodes.ASM4, mv);
		}
		int state = 0;
		Label label1;

		@Override
		public void visitVarInsn(int opcode, int var) {
			if (state==0 && opcode==ALOAD && var==0) {
				++state;
				mv.visitVarInsn(ALOAD, 0);
				mv.visitFieldInsn(GETFIELD, Names.modelRenderer_displayList.clasName, Names.modelRenderer_displayList.name, Names.modelRenderer_displayList.desc);
				label1 = new Label();
				mv.visitJumpInsn(IFNE, label1);
				mv.visitVarInsn(ALOAD, 0);
				mv.visitVarInsn(FLOAD, 1);
				mv.visitFieldInsn(PUTFIELD, Names.modelRenderer_.clasName, "savedScale", "F");
				mv.visitVarInsn(ALOAD, 0);
				return;
			}
			super.visitVarInsn(opcode, var);
		}

		@Override
		public void visitFieldInsn(int opcode, String owner, String name,
				String desc) {
			if (state==1 && Names.modelRenderer_displayList.equals(owner,name,desc) && opcode==PUTFIELD) {
				++state;
				mv.visitFieldInsn(opcode, owner, name, desc);
				mv.visitLabel(label1); label1 = null;
				mv.visitFrame(F_SAME,0,null,0,null);
				return;
			}
			mv.visitFieldInsn(opcode, owner, name, desc);
		}

	}
}
