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.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 org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceMethodVisitor;


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

/** transformer for net.minecraft.client.renderer.EntityRenderer */
public class SMCCTEntityRenderer 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);		
		}

		@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 MethodVisitor visitMethod(int access, String name, String desc,
				String signature, String[] exceptions) 
		{
			if (Names.entityRenderer_disableLightmap.equalsNameDesc(name,desc))
			{
				SMCLog.finer("  patch method %s.%s%s",classname,name,desc);
				return new MVdisableLightmap(cv.visitMethod(access, name, desc, signature, exceptions));
			} else
			if (Names.entityRenderer_enableLightmap.equalsNameDesc(name,desc))
			{
				SMCLog.finer("  patch method %s.%s%s",classname,name,desc);
				return new MVenableLightmap(cv.visitMethod(access, name, desc, signature, exceptions));
			} else
			if (Names.entityRenderer_updateFogColor.equalsNameDesc(name,desc))
			{
				SMCLog.finer("  patch method %s.%s%s",classname,name,desc);
				return new MVupdateFogColor(cv.visitMethod(access, name, desc, signature, exceptions));
			} else
			if (Names.entityRenderer_setFogColorBuffer.equalsNameDesc(name,desc))
			{
				SMCLog.finer("  patch method %s.%s%s",classname,name,desc);
				return new MVsetFogColorBuffer(cv.visitMethod(access, name, desc, signature, exceptions));
			} else
			if (Names.entityRenderer_setupFog.equalsNameDesc(name,desc))
			{
				SMCLog.finer("  patch method %s.%s%s",classname,name,desc);
				return new MVsetupFog(cv.visitMethod(access, name, desc, signature, exceptions));
			} else
			if (Names.entityRenderer_renderCloudsCheck.equalsNameDesc(name,desc))
			{
				SMCLog.finer("  patch method %s.%s%s",classname,name,desc);
				return new MVrenderCloudsCheck(cv.visitMethod(access, name, desc, signature, exceptions));
			} else
			if (Names.entityRenderer_renderHand.equalsNameDesc(name,desc))
			{
				SMCLog.finer("  patch method %s.%s%s",classname,name,desc);
				//access = access & (~ACC_PRIVATE & ~ACC_PROTECTED) | ACC_PUBLIC;
				return new MVrenderHand(cv.visitMethod(access, name, desc, signature, exceptions));
			} else
			if (Names.entityRenderer_renderWorld.equalsNameDesc(name,desc))
			{
				SMCLog.finer("  patch method %s.%s%s",classname,name,desc);
				return new MVrenderWorld(
					cv.visitMethod(access, name, desc, signature, exceptions));
			} else
			if (Names.entityRenderer_renderWorldPass.equalsNameDesc(name,desc))
			{
				SMCLog.finer("  patch method %s.%s%s",classname,name,desc);
				return new MVrenderWorldPass(
					cv.visitMethod(access, name, desc, signature, exceptions));
			}
			return cv.visitMethod(access, name, desc, signature, exceptions);
		}

		@Override
		public void visitEnd() {
			AccessorGenerator.generateFieldAccessor(cv, "frameCount", Names.entityRenderer_frameCount);
			AccessorGenerator.generatePrivateMethodAccessor(cv, "renderHand", Names.entityRenderer_renderHand);
			AccessorGenerator.generatePrivateMethodAccessor(cv, "setupCameraTransform", Names.entityRenderer_setupCameraTransform);
			super.visitEnd();
		}
		
	}

	private static class MVdisableLightmap extends MethodVisitor
	{
		public MVdisableLightmap(MethodVisitor mv) {
			super(Opcodes.ASM4, mv);
		}
		@Override
		public void visitInsn(int opcode) {
			if (opcode==RETURN) {
				mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "disableLightmap", "()V");
			}
			mv.visitInsn(opcode);
		}
	}

	private static class MVenableLightmap extends MethodVisitor
	{
		public MVenableLightmap(MethodVisitor mv) {
			super(Opcodes.ASM4, mv);
		}
		@Override
		public void visitInsn(int opcode) {
			if (opcode==RETURN) {
				mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "enableLightmap", "()V");
			}
			mv.visitInsn(opcode);
		}
	}

	private static class MVupdateFogColor extends MethodVisitor
	{
		public MVupdateFogColor(MethodVisitor mv) {
			super(Opcodes.ASM4, mv);
		}
		
		@Override
		public void visitMethodInsn(int opcode, String owner, String name,
				String desc) {
			if (Names.glStateManager_clearColor.equals(owner,name,desc))
			{
				mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "setClearColor", "(FFFF)V");
				return;
			}
			mv.visitMethodInsn(opcode, owner, name, desc);
		}
	}

	private static class MVsetFogColorBuffer extends MethodVisitor
	{
		public MVsetFogColorBuffer(MethodVisitor mv) {
			super(Opcodes.ASM4, mv);
		}
		@Override
		public void visitCode() {
			mv.visitCode();
			mv.visitVarInsn(FLOAD, 1);
			mv.visitVarInsn(FLOAD, 2);
			mv.visitVarInsn(FLOAD, 3);
			mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "setFogColor", "(FFF)V");
		}
	}

	private static class MVsetupFog extends MethodVisitor
	{
		public MVsetupFog(MethodVisitor mv) {
			super(Opcodes.ASM4, mv);
		}
		@Override
		public void visitMethodInsn(int opcode, String owner, String name,
				String desc) {
			if (Names.glStateManager_setFog.equals(owner,name,desc)) {
				mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "setFog", "(I)V");
				return;
			}
			mv.visitMethodInsn(opcode, owner, name, desc);
		}
	}

	private static class MVrenderCloudsCheck extends MethodVisitor
	{
		public MVrenderCloudsCheck(MethodVisitor mv) {
			super(Opcodes.ASM4, mv);
		}
		@Override
		public void visitMethodInsn(int opcode, String owner, String name,
				String desc) {
			if (Names.gameSettings_shouldRenderClouds.equals(owner,name,desc)) {
				mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "shouldRenderClouds", "("+Names.gameSettings_.desc+")Z");
				return;
			} else
			if (Names.renderGlobal_renderClouds.equals(owner,name,desc)) {
				mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "beginClouds", "()V");
				mv.visitMethodInsn(opcode, owner, name, desc);
				mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "endClouds", "()V");
				return;
			}
			mv.visitMethodInsn(opcode, owner, name, desc);
		}
	}

	private static class MVrenderHand extends MethodVisitor
	{
		Label la1, la2;
		public MVrenderHand(MethodVisitor mv) {
			super(Opcodes.ASM4, mv);
			la1 = new Label();
			la2 = new Label();
		}
		@Override
		public void visitMethodInsn(int opcode, String owner, String name,
				String desc) {
			if (Names.equals("org/lwjgl/util/glu/Project","gluPerspective","(FFFF)V",owner,name,desc)) {
				mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "applyHandDepth", "()V");
				mv.visitMethodInsn(opcode, owner, name, desc);
				return;
			} else
			if (Names.glStateManager_pushMatrix.equals(owner,name,desc)) {
				mv.visitFieldInsn(GETSTATIC, "shadersmod/client/Shaders", "isSleeping", "Z");
				mv.visitVarInsn(ISTORE, 4);
				mv.visitFieldInsn(GETSTATIC, "shadersmod/client/Shaders", "isHandRendered", "Z");
				mv.visitJumpInsn(IFNE, la1);
				mv.visitMethodInsn(opcode, owner, name, desc);
				return;
			} else
			if (Names.glStateManager_popMatrix.equals(owner,name,desc)) {
				mv.visitMethodInsn(opcode, owner, name, desc);
				mv.visitLabel(la1);
				mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
				mv.visitFieldInsn(GETSTATIC, "shadersmod/client/Shaders", "isCompositeRendered", "Z");
				mv.visitJumpInsn(IFNE, la2);
				mv.visitInsn(RETURN);
				mv.visitLabel(la2);
				mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
				mv.visitVarInsn(ALOAD, 0);
				//mv.visitVarInsn(FLOAD, 1);
				//mv.visitInsn(F2D);
				mv.visitMethodInsn(INVOKEVIRTUAL, Names.entityRenderer_disableLightmap.clasName, Names.entityRenderer_disableLightmap.name, Names.entityRenderer_disableLightmap.desc);
				return;
			} else if (Names.itemRenderer_renderItemInFirstPerson.equals(owner, name, desc)) {
				mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/ShadersRender", "renderItemFP", "("+Names.itemRenderer_.desc+"F)V");
				return;
			}
			mv.visitMethodInsn(opcode, owner, name, desc);
		}
	}

	private static class MVrenderWorld extends MethodVisitor
	{
		public MVrenderWorld(MethodVisitor mv) {
			super(Opcodes.ASM4, mv);
		}
		
		int state = 0;
		
		
		@Override
		public void visitMethodInsn(int opcode, String owner, String name,
				String desc) {
			switch (state)
			{
			case 0:
				if (Names.entityRenderer_getMouseOver.equals(owner,name,desc))
				{
					++state;
					mv.visitMethodInsn(opcode, owner, name, desc);
					mv.visitVarInsn(ALOAD, 0);
					mv.visitFieldInsn(GETFIELD, Names.entityRenderer_mc.clasName, Names.entityRenderer_mc.name, Names.entityRenderer_mc.desc);
					mv.visitVarInsn(FLOAD, 1);
					mv.visitVarInsn(LLOAD, 2);
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "beginRender", "("+Names.minecraft_.desc+"FJ)V");
					return;
				}
				break;
//			case 1:
//			case 2:
//			case 3:
//				if (Names.entityRenderer_renderWorldPass.equals(owner,name,desc)) {
//					++state;
//					mv.visitMethodInsn(opcode, owner, name, desc);
//					return;
//				}
//				break;
//				
			}
			super.visitMethodInsn(opcode, owner, name, desc);
		}

	}

	private static class MVrenderWorldPass extends MethodVisitor
	{
		MethodVisitor mvOut;
		Label labelNoSky = null;
		Label labelEndRender = null;
		
		public MVrenderWorldPass(MethodVisitor mv) {
			super(Opcodes.ASM4);
			mvOut = mv;
			setNextState(new State0());
		}
		
		void setNextState(MethodVisitor next) {
			mv = next;
		}

		class MVState extends MethodVisitor {
			public MVState() {
				super(ASM4, MVrenderWorldPass.this.mvOut);
			}
			@Override
			public void visitEnd() {
				if (this.getClass()!=StateEnd.class)
					SMCLog.severe("  ends in bad state %s",this.getClass().getSimpleName());
				mv.visitEnd();
			}
		}
		
		class State0 extends MVState {
			@Override
			public void visitCode() {
				mv.visitCode();
				mv.visitVarInsn(ILOAD, 1);
				mv.visitVarInsn(FLOAD, 2);
				mv.visitVarInsn(LLOAD, 3);
				mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "beginRenderPass", "(IFJ)V");
			}
			@Override
			public void visitMethodInsn(int opcode, String owner, String name, String desc) {
				if (Names.glStateManager_viewport.equals(owner,name,desc)) {
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "setViewport", "(IIII)V");
					setNextState(new State1());
				} else {
					mv.visitMethodInsn(opcode, owner, name, desc);
				}
			}
		}

		class State1 extends MVState {
			@Override
			public void visitMethodInsn(int opcode, String owner, String name, String desc) {
				if (Names.glStateManager_clear.equals(owner,name,desc)) {
					mv.visitMethodInsn(opcode, owner, name, desc);
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "clearRenderBuffer", "()V");
					setNextState(new State2());
				} else {
					mv.visitMethodInsn(opcode, owner, name, desc);
				}
			}
		}

		class State2 extends MVState {
			@Override
			public void visitMethodInsn(int opcode, String owner, String name, String desc) {
				if (Names.entityRenderer_setupCameraTransform.equals(owner,name,desc)) {
					mv.visitMethodInsn(opcode, owner, name, desc);
					mv.visitVarInsn(FLOAD, 2);
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "setCamera", "(F)V");
					setNextState(new State2a());
				} else {
					mv.visitMethodInsn(opcode, owner, name, desc);
				}
			}
		}
		class State2a extends MVState {
			@Override
			public void visitMethodInsn(int opcode, String owner, String name, String desc) {
				if (Names.equals(Names.frustum_.clasName,"<init>","()V",owner,name,desc)) {
					final boolean mod = false;
					if (mod) {
						mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/ClippingHelperShadow", "getInstance", "()Lnet/minecraft/client/renderer/culling/ClippingHelper;", false);
						mv.visitMethodInsn(opcode, owner, name, "("+Names.clippingHelper_.desc+")V");
					} else {
						mv.visitMethodInsn(opcode, owner, name, desc);
					}
					setNextState(new State2b());
				} else {
					mv.visitMethodInsn(opcode, owner, name, desc);
				}
			}
		}
		class State2b extends MVState {
			@Override
			public void visitMethodInsn(int opcode, String owner, String name, String desc) {
				if (Names.iCamera_setPosition.equals(owner,name,desc) || Names.frustrum_setPosition.equals(owner,name,desc)) {
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/ShadersRender", "setFrustrumPosition", "("+Names.frustum_.desc+"DDD)V");
					setNextState(new State3());
				} else {
					mv.visitMethodInsn(opcode, owner, name, desc);
				}
			}
		}
		class State3 extends MVState {
			@Override
			public void visitFieldInsn(int opcode, String owner, String name, String desc) {
				if (Names.gameSettings_renderDistance.equals(owner,name)) {
					mv.visitFieldInsn(opcode, owner, name, desc);
					setNextState(new State4());
				} else {
					mv.visitFieldInsn(opcode, owner, name, desc);
				}
			}
			@Override
			public void visitMethodInsn(int opcode, String owner, String name, String desc) {
				if (Names.equals("Config","isSkyEnabled","()Z",owner,name,desc)) {
					mv.visitFieldInsn(GETSTATIC, "shadersmod/client/Shaders", "isShadowPass", "Z");
					labelNoSky = new Label();
					mv.visitJumpInsn(IFNE, labelNoSky);
					mv.visitMethodInsn(opcode, owner, name, desc);
					setNextState(new State6());
				} else {
					mv.visitMethodInsn(opcode, owner, name, desc);
				}
			}
		}
		class State4 extends MVState {
			@Override
			public void visitJumpInsn(int opcode, Label label) {
				if (opcode==IF_ICMPLT) {
					mv.visitInsn(POP2);
					mv.visitFieldInsn(GETSTATIC, "shadersmod/client/Shaders", "isShadowPass", "Z");
					mv.visitJumpInsn(IFNE, label);
					setNextState(new State5());
				} else {
					mv.visitJumpInsn(opcode, label);
				}
			}
		}
		class State5 extends MVState {
			@Override
			public void visitMethodInsn(int opcode, String owner, String name, String desc) {
				if (Names.renderGlobal_renderSky.equals(owner,name,desc)) {
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "beginSky", "()V");
					mv.visitMethodInsn(opcode, owner, name, desc);
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "endSky", "()V");
					setNextState(new State8());
				} else {
					mv.visitMethodInsn(opcode, owner, name, desc);
				}
			}
		}
		class State6 extends MVState { // optifine
			@Override
			public void visitMethodInsn(int opcode, String owner, String name, String desc) {
				if (Names.renderGlobal_renderSky.equals(owner,name,desc)) {
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "beginSky", "()V");
					mv.visitMethodInsn(opcode, owner, name, desc);
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "endSky", "()V");
					setNextState(new State7());
				} else {
					mv.visitMethodInsn(opcode, owner, name, desc);
				}
			}
		}
		class State7 extends State8 { // optifine
			@Override
			public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
				mv.visitLabel(labelNoSky); labelNoSky = null;
				super.visitFrame(type, nLocal, local, nStack, stack);
			}
		}
		class State8 extends MVState { // vanilla
			@Override
			public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
				mv.visitFrame(type, nLocal, local, nStack, stack);
				setNextState(new State9());
//				mv.visitVarInsn(ALOAD, 0);
//				mv.visitVarInsn(ILOAD, 1);
//				mv.visitVarInsn(FLOAD, 2);
//				mv.visitVarInsn(LLOAD, 3);
//				mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/ShadersRender", "renderShadowMap", "("+Names.entityRenderer_.desc+"IFJ)V");
			}
		}
		class State9 extends MVState {
			@Override
			public void visitMethodInsn(int opcode, String owner, String name, String desc) {
				if (Names.renderGlobal_setupTerrain.equals(owner,name,desc)) {
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/ShadersRender", "setupTerrain", "("+Names.renderGlobal_.desc+Names.entity_.desc+"D"+Names.iCamera_.desc+"IZ)V");
					setNextState(new State10());
				} else {
					mv.visitMethodInsn(opcode, owner, name, desc);
				}
			}
		}
		class State10 extends MVState {
			@Override
			public void visitMethodInsn(int opcode, String owner, String name, String desc) {
				if (Names.renderGlobal_updateChunks.equals(owner,name,desc)) {
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/ShadersRender", "updateChunks", "("+Names.renderGlobal_.desc+"J)V");
					setNextState(new State11());
				} else {
					mv.visitMethodInsn(opcode, owner, name, desc);
				}
			}
		}
		class State11 extends MVState {
			@Override
			public void visitFrame(int type, int nLocal, Object[] local,
					int nStack, Object[] stack) {
				mv.visitFrame(type, nLocal, local, nStack, stack);
//				mv.visitVarInsn(ALOAD, 0);
//				mv.visitVarInsn(ILOAD, 1);
//				mv.visitVarInsn(FLOAD, 2);
//				mv.visitVarInsn(LLOAD, 3);
//				mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/ShadersRender", "renderShadowMap", "("+Names.entityRenderer_.desc+"IFJ)V");
				setNextState(new State12());
			}
		}
		class State12 extends MVState {
			@Override
			public void visitMethodInsn(int opcode, String owner, String name, String desc) {
				if (Names.renderGlobal_renderBlockLayer4.equals(owner,name,desc)) {
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/ShadersRender", "beginTerrainSolid", "()V");
					mv.visitMethodInsn(opcode, owner, name, desc);
					setNextState(new State12a());
				} else {
					mv.visitMethodInsn(opcode, owner, name, desc);
				}
			}
		}
		class State12a extends MVState {
			@Override
			public void visitMethodInsn(int opcode, String owner, String name, String desc) {
				if (Names.renderGlobal_renderBlockLayer4.equals(owner,name,desc)) {
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/ShadersRender", "beginTerrainCutoutMipped", "()V");
					mv.visitMethodInsn(opcode, owner, name, desc);
					setNextState(new State12b());
				} else {
					mv.visitMethodInsn(opcode, owner, name, desc);
				}
			}
		}
		class State12b extends MVState {
			@Override
			public void visitMethodInsn(int opcode, String owner, String name, String desc) {
				if (Names.renderGlobal_renderBlockLayer4.equals(owner,name,desc)) {
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/ShadersRender", "beginTerrainCutout", "()V");
					mv.visitMethodInsn(opcode, owner, name, desc);
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/ShadersRender", "endTerrain", "()V");
					setNextState(new State12c());
				} else {
					mv.visitMethodInsn(opcode, owner, name, desc);
				}
			}
		}
		class State12c extends MVState {
			@Override
			public void visitMethodInsn(int opcode, String owner, String name, String desc) {
				if (Names.renderGlobal_renderEntities.equals(owner,name,desc)) {
					mv.visitMethodInsn(opcode, owner, name, desc);
					setNextState(new State13());
				} else if (owner.equals(Names.ofReflector_.clasName)) {
					SMCEnvironment.hasOptiFine = true;
					mv.visitMethodInsn(opcode, owner, name, desc);
				} else if (owner.equals(Names.forgeHooksClient_.clasName)) {
					SMCEnvironment.hasForge = true;
					mv.visitMethodInsn(opcode, owner, name, desc);
				} else {
					mv.visitMethodInsn(opcode, owner, name, desc);
				}
			}
		}
		class State13 extends MVState {
			@Override
			public void visitMethodInsn(int opcode, String owner, String name, String desc) {
				if (Names.effectRenderer_renderLitParticles.equals(owner,name,desc)) {
					// vanilla or forge. not optfine
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "beginLitParticles", "()V");
					mv.visitMethodInsn(opcode, owner, name, desc);
					setNextState(new State14());
				} else {
					mv.visitMethodInsn(opcode, owner, name, desc);
				}
			}
		}
		class State14 extends MVState {
			@Override
			public void visitMethodInsn(int opcode, String owner, String name, String desc) {
				if (Names.effectRenderer_renderParticles.equals(owner,name,desc)) {
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "beginParticles", "()V");
					mv.visitMethodInsn(opcode, owner, name, desc);
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "endParticles", "()V");
					setNextState(new State16());
				} else {
					mv.visitMethodInsn(opcode, owner, name, desc);
				}
			}
		}
		class State16 extends MVState {
			@Override
			public void visitMethodInsn(int opcode, String owner, String name, String desc) {
				if (Names.entityRenderer_renderRainSnow.equals(owner,name,desc)) {
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "beginWeather", "()V");
					mv.visitMethodInsn(opcode, owner, name, desc);
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "endWeather", "()V");
					setNextState(new State17());
				} else {
					mv.visitMethodInsn(opcode, owner, name, desc);
				}
			}
		}
		class State17 extends MVState {
			@Override
			public void visitMethodInsn(int opcode, String owner, String name, String desc) {
				if (Names.glStateManager_depthMask.equals(owner,name,desc)) {
					mv.visitMethodInsn(opcode, owner, name, desc);
					mv.visitVarInsn(ALOAD, 0);
					mv.visitVarInsn(FLOAD, 2);
					mv.visitVarInsn(ILOAD, 1);
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/ShadersRender", "renderHand0", "("+Names.entityRenderer_.desc+"FI)V");
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "preWater", "()V");
					setNextState(new State18());
				} else {
					mv.visitMethodInsn(opcode, owner, name, desc);
				}
			}
		}
		class State18 extends MVState {
			@Override
			public void visitMethodInsn(int opcode, String owner, String name, String desc) {
				if (Names.renderGlobal_renderBlockLayer4.equals(owner,name,desc)) {
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "beginWater", "()V");
					mv.visitMethodInsn(opcode, owner, name, desc);
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "endWater", "()V");
					setNextState(new State19());
				} else {
					mv.visitMethodInsn(opcode, owner, name, desc);
				}
			}
		}
		class State19 extends MVState {
			@Override
			public void visitMethodInsn(int opcode, String owner, String name, String desc) {
				if (Names.renderGlobal_renderBlockLayer4.equals(owner,name,desc)) {
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "beginWater", "()V");
					mv.visitMethodInsn(opcode, owner, name, desc);
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "endWater", "()V");
					setNextState(new State21());
				} else {
					mv.visitMethodInsn(opcode, owner, name, desc);
				}
			}
		}
		class State21 extends MVState {
			@Override
			public void visitMethodInsn(int opcode, String owner, String name, String desc) {
				if (Names.entityRenderer_renderCloudsCheck.equals(owner,name,desc)) {
					mv.visitMethodInsn(opcode, owner, name, desc);
					setNextState(new State22());
				} else {
					mv.visitMethodInsn(opcode, owner, name, desc);
				}
			}
		}
		class State22 extends MVState {
			@Override
			public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
				mv.visitFrame(type, nLocal, local, nStack, stack);
				setNextState(new State23());
			}
		}
		class State23 extends MVState {
			@Override
			public void visitFieldInsn(int opcode, String owner, String name, String desc) {
				//SMCLog.finest("    %d F %d %s.%s %s", state, opcode, owner, name, desc);
				if (Names.entityRenderer_shouldRenderHand.equals(owner,name)) {
					mv.visitFieldInsn(opcode, owner, name, desc);
					setNextState(new State28());
				} else {
					mv.visitFieldInsn(opcode, owner, name, desc);
				}
			}
		}

		class State28 extends MVState {
			@Override
			public void visitJumpInsn(int opcode, Label label) {
				if (opcode==IFEQ) {
					mv.visitJumpInsn(opcode, label);
					mv.visitFieldInsn(GETSTATIC, "shadersmod/client/Shaders", "isShadowPass", "Z");
					mv.visitJumpInsn(IFNE, label);
					mv.visitVarInsn(ALOAD, 0);
					mv.visitVarInsn(FLOAD, 2);
					mv.visitVarInsn(ILOAD, 1);
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/ShadersRender", "renderHand1", "("+Names.entityRenderer_.desc+"FI)V");
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "renderCompositeFinal", "()V");
					setNextState(new State29());
				} else {
					mv.visitJumpInsn(opcode, label);
				}
			}
		}
		class State29 extends MVState {
			@Override
			public void visitVarInsn(int opcode, int var) {
				if (opcode==ALOAD) {
					mv.visitVarInsn(opcode, var);
					setNextState(new State30());
				} else {
					mv.visitVarInsn(opcode, var);
				}
			}
		}
		class State30 extends MVState {
			@Override
			public void visitMethodInsn(int opcode, String owner, String name, String desc) {
				if (Names.entityRenderer_renderHand.equals(owner,name,desc)) {
					mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/ShadersRender", "renderFPOverlay", "("+Names.entityRenderer_.desc+"FI)V");
					setNextState(new State31());
				} else {
					mv.visitMethodInsn(opcode, owner, name, desc);
				}
			}
		}
		class State31 extends MVState {
			public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
				mv.visitFrame(type, nLocal, local, nStack, stack);
				mv.visitMethodInsn(INVOKESTATIC, "shadersmod/client/Shaders", "endRender", "()V");
				setNextState(new StateEnd());
			}
		}
		class State99 extends MVState {
			void a() {
				if (true) {
					setNextState(new State99());
				}
			}
		}
		class StateEnd extends MVState {
			@Override
			public void visitEnd() {
				mv.visitEnd();
			}
		}
	}
	
}
