package shadersmod.transform;

import java.util.Iterator;

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.MethodNode;

import shadersmod.client.SVertexBuilder;
import shadersmod.common.SMCLog;


import net.minecraft.launchwrapper.IClassTransformer;

/** for WorldRenderer */
public class SMCCTWorldRenderer 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
	{
		public CVTransform(ClassVisitor cv)
		{
			super(Opcodes.ASM4, cv);		
		}

		boolean endFields = false;

		@Override
		public void visit(int version, int access, String name,
				String signature, String superName, String[] interfaces) {
			//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 ("sVertexBuilder".equals(name)) {
				return null;
			}
			access = access & ~(ACC_PRIVATE|ACC_PROTECTED) | ACC_PUBLIC;
			return cv.visitField(access, name, desc, signature, value);
		}

		@Override
		public MethodVisitor visitMethod(int access, String name, String desc,
				String signature, String[] exceptions) 
		{
			if (!endFields) {
				endFields = true;
				FieldVisitor fv;
				fv = cv.visitField(ACC_PUBLIC, "sVertexBuilder", "Lshadersmod/client/SVertexBuilder;", null, null);
				fv.visitEnd();
			}
			if (Names.equalsNameDesc("<init>","(I)V",name, desc))
			{
				//SMCLog.finer("  patching method %s.%s%s = %s",classname,name,desc,nameM);
				return new MVinit(cv.visitMethod(access, name, desc, signature, exceptions));
			} else
			if (Names.worldRenderer_growBuffer.equalsNameDesc(name, desc))
			{
				//SMCLog.finer("  patching method %s.%s%s = %s",classname,name,desc,nameM);
				return new MVgrow(cv.visitMethod(access, name, desc, signature, exceptions));
			} else
			if (Names.worldRenderer_setVertexFormat.equalsNameDesc(name, desc))
			{
				//SMCLog.finer("  patching method %s.%s%s = %s",classname,name,desc,nameM);
				return new MVsetVertexFormat(cv.visitMethod(access, name, desc, signature, exceptions));
			} else
			if (Names.worldRenderer_addVertexData.equalsNameDesc(name, desc))
			{
				//SMCLog.finer("  patching method %s.%s%s = %s",classname,name,desc,nameM);
				return new MVaddData(cv.visitMethod(access, name, desc, signature, exceptions));
			} else
			if (Names.worldRenderer_addVertex.equalsNameDesc(name, desc))
			{
				//SMCLog.finer("  patching method %s.%s%s = %s",classname,name,desc,nameM);
				return new MVaddVertex(cv.visitMethod(access, name, desc, signature, exceptions));
			} else
			return cv.visitMethod(access, name, desc, signature, exceptions);
		}

		@Override
		public void visitEnd() {
			AccessorGenerator.generateFieldAccessor(cv,"rawIntBuffer"  ,Names.worldRenderer_rawIntBuffer);
			AccessorGenerator.generateFieldAccessor(cv,"rawFloatBuffer",Names.worldRenderer_rawFloatBuffer);
			AccessorGenerator.generateFieldAccessor(cv,"vertexCount"   ,Names.worldRenderer_vertexCount);
			AccessorGenerator.generateFieldAccessor(cv,"rawBufferIndex",Names.worldRenderer_rawBufferIndex);
			AccessorGenerator.generateFieldAccessor(cv,"drawMode"      ,Names.worldRenderer_drawMode);
			super.visitEnd();
		}
		
	}

	private static class MVinit extends MethodVisitor
	{
		public MVinit(MethodVisitor mv) {
			super(Opcodes.ASM4, mv);
		}
		@Override
		public void visitCode() {
			mv.visitCode();
			mv.visitVarInsn(ILOAD, 1);
			mv.visitInsn(ICONST_2);
			mv.visitInsn(IMUL);
			mv.visitVarInsn(ISTORE, 1);
		}
		@Override
		public void visitInsn(int opcode) {
			if (opcode==RETURN) {
				mv.visitVarInsn(ALOAD, 0);
				mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/SVertexBuilder", "initVertexBuilder", "("+Names.worldRenderer_.desc+")V");
			}
			mv.visitInsn(opcode);
		}
	}
	private static class MVgrow extends MethodVisitor
	{
		public MVgrow(MethodVisitor mv) {
			super(Opcodes.ASM4, mv);
		}
		@Override
		public void visitCode() {
			mv.visitCode();
			mv.visitVarInsn(ILOAD, 1);
			mv.visitInsn(ICONST_2);
			mv.visitInsn(IMUL);
			mv.visitVarInsn(ISTORE, 1);
		}
	}
	private static class MVsetVertexFormat extends MethodVisitor
	{
		public MVsetVertexFormat(MethodVisitor mv) {
			super(Opcodes.ASM4, mv);
		}
		@Override
		public void visitInsn(int opcode) {
			if (opcode==RETURN) {
				mv.visitVarInsn(ALOAD, 0);
				mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/SVertexBuilder", "endSetVertexFormat", "("+Names.worldRenderer_.desc+")V");
			}
			mv.visitInsn(opcode);
		}
	}
	private static class MVaddData extends MethodVisitor
	{
		public MVaddData(MethodVisitor mv) {
			super(Opcodes.ASM4, mv);
		}
		@Override
		public void visitCode() {
			mv.visitCode();
			mv.visitVarInsn(ALOAD, 0);
			mv.visitVarInsn(ALOAD, 1);
			mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/SVertexBuilder", "beginAddVertexData", "("+Names.worldRenderer_.desc+"[I)V");
		}
		@Override
		public void visitInsn(int opcode) {
			if (opcode==RETURN) {
				mv.visitVarInsn(ALOAD, 0);
				mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/SVertexBuilder", "endAddVertexData", "("+Names.worldRenderer_.desc+")V");
			}
			mv.visitInsn(opcode);
		}
	}
	private static class MVaddVertex extends MethodVisitor
	{
		public MVaddVertex(MethodVisitor mv) {
			super(Opcodes.ASM4, mv);
		}
		@Override
		public void visitCode() {
			mv.visitCode();
			mv.visitVarInsn(ALOAD, 0);
			mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/SVertexBuilder", "beginAddVertex", "("+Names.worldRenderer_.desc+")V");
		}
		@Override
		public void visitInsn(int opcode) {
			if (opcode==RETURN) {
				mv.visitVarInsn(ALOAD, 0);
				mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/SVertexBuilder", "endAddVertex", "("+Names.worldRenderer_.desc+")V");
			}
			mv.visitInsn(opcode);
		}
	}
}
