package com.concurnas.runtime;

import java.util.LinkedHashSet;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import com.concurnas.compiler.ast.FuncType;
import com.concurnas.runtime.cps.mirrors.ClassMirror;
import com.concurnas.runtime.cps.mirrors.ConstructorMirror;
import com.concurnas.runtime.cps.mirrors.MethodMirror;

public class ClassRefInterfaceGennerator implements Opcodes {

	private static byte[] create(String name, LinkedHashSet<String> constructors, String superclass) throws Exception {

		ClassWriter cw = new ClassWriter(0);
		MethodVisitor mv;

		cw.visit(V1_8, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE, name, null, "java/lang/Object", superclass==null?null:new String[]{FuncType.classRefIfacePrefix  +superclass +  FuncType.classRefIfacePostfix });
		for(String desc: constructors){
			
			desc = desc.substring(0, desc.lastIndexOf("V")) + "Ljava/lang/Object;";
			
			mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "apply", desc, null, null);
			mv.visitEnd();
		}
		cw.visitEnd();

		return cw.toByteArray();
	}

	public static byte[] classRefInterfaceGennerator(String source, ConcurnasClassLoader loader, String ifaceName){
		LinkedHashSet<String> publicConstructors = new LinkedHashSet<String>();
		
		try {
			if(source.endsWith("$$ActorIterface")){
				source = source.substring(0, source.length() - 15);
			}
			ClassMirror cm = loader.getDetector().classForName(source);
			if(null == cm){
				throw new Exception("Unable to find definition for: " + source);
			}
			String superclass = cm.getSuperclass();
			if(null != superclass){
				superclass=superclass.replace(".", "/");
			}
			
			boolean isNestedOrActpr = 0 < cm.isNestedClass() || loader.getDetector().isActor(source);
			
			for(ConstructorMirror mm : cm.getDeclaredConstructorsWithoutHiddenArgs()){
				
				if(mm.isPublic() ){
					String md = mm.getMethodDescriptor();
					if(isNestedOrActpr){//(LbytecodeSandbox$Outer;Ljava/lang/String;)V
						//cut off first argument (outer class)
						md = "("+md.substring(md.indexOf(";")+1);
					}
					
					publicConstructors.add(md);
				}
			}
				
			
			//byte[] codex =  create(ifaceName, methods);
			//BytecodePrettyPrinter.print(codex, true);
			
			return create(ifaceName, publicConstructors, superclass);
		} catch (Exception e) {
			throw new RuntimeException("Failure during genneration of actor interface for: " + source, e);
		}
	}
}