package org.deuce.transform.asm;

import java.util.HashMap;


import org.deuce.objectweb.asm.AnnotationVisitor;
import org.deuce.objectweb.asm.Attribute;
import org.deuce.objectweb.asm.Label;
import org.deuce.objectweb.asm.MethodVisitor;
import org.deuce.objectweb.asm.Opcodes;
import org.deuce.objectweb.asm.commons.AnalyzerAdapter;
import org.deuce.objectweb.asm.commons.Method;

public class MethodTransformer implements MethodVisitor{

	private MethodVisitor originalMethod;

	final private MethodVisitor copyMethod;
	final private String className;
	final private String methodName;
	final private String descriptor; // original descriptor
	final private HashMap<Label, Label> labelMap = new HashMap<Label, Label>();
	final private boolean isStatic;
	final private Method newMethod;

	public MethodTransformer(MethodVisitor originalMethod, MethodVisitor copyMethod, 
			String className, int access, String methodName, String descriptor, Method newMethod) {
		this.originalMethod = originalMethod;
		this.newMethod = newMethod;
		this.isStatic = (access & Opcodes.ACC_STATIC) != 0;
		
		// The AnalyzerAdapter delegates the call to the DuplicateMethod, while the DuplicateMethod uses
		// the analyzer for stack state in the original method.
		DuplicateMethod duplicateMethod = new DuplicateMethod( copyMethod, isStatic, newMethod);
		AnalyzerAdapter analyzerAdapter = new AnalyzerAdapter( className, access, methodName, descriptor, duplicateMethod);
		duplicateMethod.setAnalyzer( analyzerAdapter);
		
		this.copyMethod = analyzerAdapter;
		this.className = className;
		this.methodName = methodName;
		this.descriptor = descriptor;
	}

	public void visitCode() {
		originalMethod.visitCode();
		copyMethod.visitCode();
	}

	public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
		
		// FIXME we might saw other annotations before and we need to put it on the new AtomicMethod
		// need to create an atomic method from the original method
		if( AtomicMethod.ATOMIC_DESCRIPTOR.equals(desc) & !(originalMethod instanceof AtomicMethod))
			originalMethod = new AtomicMethod( originalMethod, className, methodName,
					descriptor, newMethod, isStatic);

		if( !desc.contains("org/junit")) // TODO find another way
			return new MethodAnnotationVisitor( originalMethod.visitAnnotation(desc, visible),
					copyMethod.visitAnnotation(desc, visible));
		else	
			return originalMethod.visitAnnotation(desc, visible);
	}

	public AnnotationVisitor visitAnnotationDefault() {
		return new MethodAnnotationVisitor( originalMethod.visitAnnotationDefault(), 
				copyMethod.visitAnnotationDefault());
	}

	public void visitAttribute(Attribute attr) {
		originalMethod.visitAttribute(attr);
		copyMethod.visitAttribute(attr);
	}

	public void visitEnd() {
		originalMethod.visitEnd();
		copyMethod.visitEnd();
	}

	public void visitFieldInsn(int opcode, String owner, String name, String desc) {
		originalMethod.visitFieldInsn(opcode, owner, name, desc);
		copyMethod.visitFieldInsn(opcode, owner, name, desc);
	}

	public void visitFrame(int type, int local, Object[] local2, int stack, Object[] stack2) {
		originalMethod.visitFrame(type, local, local2, stack, stack2);
		copyMethod.visitFrame(type, local, local2, stack, stack2);
	}

	public void visitIincInsn(int var, int increment) {
		originalMethod.visitIincInsn(var, increment);
		copyMethod.visitIincInsn(var, increment);
	}

	public void visitInsn(int opcode) {
		originalMethod.visitInsn(opcode);
		copyMethod.visitInsn(opcode);
	}

	public void visitIntInsn(int opcode, int operand) {
		originalMethod.visitIntInsn(opcode, operand);
		copyMethod.visitIntInsn(opcode, operand);
	}

	public void visitJumpInsn(int opcode, Label label) {
		originalMethod.visitJumpInsn(opcode, label);
		copyMethod.visitJumpInsn(opcode, getLabel(label));
	}

	public void visitLabel(Label label) {
		originalMethod.visitLabel(label);
		copyMethod.visitLabel(getLabel(label));
	}

	public void visitLdcInsn(Object cst) {
		originalMethod.visitLdcInsn(cst);
		copyMethod.visitLdcInsn(cst);
	}


	public void visitLineNumber(int line, Label start) {
		originalMethod.visitLineNumber(line, start);
		copyMethod.visitLineNumber(line, getLabel(start));
	}


	public void visitLocalVariable(String name, String desc, String signature, Label start,
			Label end, int index) {
		originalMethod.visitLocalVariable(name, desc, signature, start, end, index);
		copyMethod.visitLocalVariable(name, desc, signature, getLabel(start), getLabel(end), index);
	}


	public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
		originalMethod.visitLookupSwitchInsn(dflt, keys, labels);
		copyMethod.visitLookupSwitchInsn( getLabel(dflt), keys, getCopyLabels(labels));
	}


	public void visitMaxs(int maxStack, int maxLocals) {
		originalMethod.visitMaxs(maxStack, maxLocals);
		copyMethod.visitMaxs(maxStack, maxLocals);
	}


	public void visitMethodInsn(int opcode, String owner, String name, String desc) {
		originalMethod.visitMethodInsn(opcode, owner, name, desc);
		copyMethod.visitMethodInsn(opcode, owner, name, desc);
	}

	public void visitMultiANewArrayInsn(String desc, int dims) {
		originalMethod.visitMultiANewArrayInsn(desc, dims);
		copyMethod.visitMultiANewArrayInsn(desc, dims);

	}

	public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
		return new MethodAnnotationVisitor( originalMethod.visitParameterAnnotation(parameter, desc, visible),
				copyMethod.visitParameterAnnotation(parameter, desc, visible));
	}


	public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
		originalMethod.visitTableSwitchInsn(min, max, dflt, labels);
		copyMethod.visitTableSwitchInsn(min, max, getLabel(dflt), getCopyLabels(labels));
	}

	public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
		originalMethod.visitTryCatchBlock(start, end, handler, type);
		copyMethod.visitTryCatchBlock(getLabel(start), getLabel(end), getLabel(handler), type);
	}


	public void visitTypeInsn(int opcode, String type) {
		originalMethod.visitTypeInsn(opcode, type);
		copyMethod.visitTypeInsn(opcode, type);

	}

	public void visitVarInsn(int opcode, int var) {
		originalMethod.visitVarInsn(opcode, var);
		copyMethod.visitVarInsn(opcode, var);
	}

	private Label[] getCopyLabels(Label[] labels) {
		Label[] copyLabels = new Label[ labels.length];
		for( int i=0; i<labels.length ;++i) {
			copyLabels[i] = getLabel(labels[i]);
		}
		return copyLabels;
	}

	private Label getLabel( Label label){
		Label duplicateLabel = labelMap.get( label);
		if( duplicateLabel == null) {
			duplicateLabel = new Label();
			labelMap.put(label, duplicateLabel);
		}
		return duplicateLabel;
	}
}

@@ -2060,16 +2060,17 @@
 (desc) &
+&
  !(origi

change & to &&