package shadersmod.transform;

import static org.objectweb.asm.Opcodes.*;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;

public class AccessorGenerator {

	private static int getVarLoadOpcode(String desc)
	{
		switch(desc.charAt(0)) {
		case 'B' : return ILOAD;
		case 'C' : return ILOAD;
		case 'D' : return DLOAD;
		case 'F' : return FLOAD;
		case 'I' : return ILOAD;
		case 'J' : return LLOAD;
		case 'L' : return ALOAD;
		case 'S' : return ILOAD;
		case 'Z' : return ILOAD;
		case '[' : return ALOAD;
		default  : 
		case 'V' : return NOP;
		}
	}
	private static int getReturnOpcode(String desc)
	{
		switch(desc.charAt(0)) {
		case 'B' : return IRETURN;
		case 'C' : return IRETURN;
		case 'D' : return DRETURN;
		case 'F' : return FRETURN;
		case 'I' : return IRETURN;
		case 'J' : return LRETURN;
		case 'L' : return ARETURN;
		case 'S' : return IRETURN;
		case 'Z' : return IRETURN;
		case '[' : return ARETURN;
		default  : 
		case 'V' : return RETURN;
		}
	}
	
	public static void generateFieldAccessor(ClassVisitor cv, String accessorName, Names.Fiel fiel) {
		MethodVisitor mv;
		mv = cv.visitMethod(ACC_PUBLIC + ACC_STATIC, "put_"+accessorName, "(L"+fiel.clasName+";"+fiel.desc+")V", null, null);
		mv.visitCode();
		mv.visitVarInsn(ALOAD, 0);
		mv.visitVarInsn(getVarLoadOpcode(fiel.desc), 1);
		mv.visitFieldInsn(PUTFIELD, fiel.clasName, fiel.name, fiel.desc);
		mv.visitInsn(RETURN);
		mv.visitMaxs(2, 2);
		mv.visitEnd();
		mv = cv.visitMethod(ACC_PUBLIC + ACC_STATIC, "get_"+accessorName, "(L"+fiel.clasName+";)"+fiel.desc, null, null);
		mv.visitCode();
		mv.visitVarInsn(ALOAD, 0);
		mv.visitFieldInsn(GETFIELD, fiel.clasName, fiel.name, fiel.desc);
		mv.visitInsn(getReturnOpcode(fiel.desc));
		mv.visitMaxs(1, 1);
		mv.visitEnd();
	}

	public static void generatePrivateMethodAccessor(ClassVisitor cv, String accessorName, Names.Meth meth) {
		MethodVisitor mv;
		String methDesc = meth.desc;
		int indexCloseParenthesis = methDesc.indexOf(')');
		String returnType = methDesc.substring(indexCloseParenthesis+1);
		String allParams  = methDesc.substring(1,indexCloseParenthesis);
		mv = cv.visitMethod(ACC_PUBLIC + ACC_STATIC, "invoke_"+accessorName, "(L"+meth.clasName+";"+allParams+")"+returnType, null, null);
		mv.visitCode();
		mv.visitVarInsn(ALOAD, 0);
		int paramCount = 1;
		for (int i=1; methDesc.charAt(i) != ')'; ) {
			int startIndex = i;
			while(methDesc.charAt(i)=='[') ++i;
			if(methDesc.charAt(i)=='L') {
				++i;
				while(methDesc.charAt(i)!=';') ++i;
			}
			++i;
			String desc = methDesc.substring(startIndex, i);
			mv.visitVarInsn(getVarLoadOpcode(desc), paramCount);
			char c = desc.charAt(0);
			paramCount += (c=='D' || c=='J')? 2: 1;
		}
		mv.visitMethodInsn(INVOKESPECIAL, meth.clasName, meth.name, meth.desc);
		mv.visitInsn(getReturnOpcode(returnType));
		mv.visitMaxs(paramCount, paramCount);
		mv.visitEnd();
	}

}
