package org.parboiled.transform.asm;

import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.MethodInsnNode;

import javax.annotation.Nonnull;
import java.lang.reflect.Method;
import java.util.Map;

public final class ClassHelper
{
    private final Type classType;
    private final boolean isInterface;
    private final int methodOpcode;
    private final Map<String, MethodDescriptor> methods = Maps.newHashMap();

    ClassHelper(@Nonnull final Class<?> c)
    {
        Preconditions.checkNotNull(c);
        classType = Type.getType(c);
        isInterface = c.isInterface();
        methodOpcode = isInterface ? Opcodes.INVOKEINTERFACE
            : Opcodes.INVOKEVIRTUAL;

        for (final Method method: c.getDeclaredMethods())
            methods.put(method.getName(), new MethodDescriptor(method));
    }

    public MethodInsnNode methodCall(@Nonnull final String name,
        @Nonnull final Type returnType, @Nonnull final Type... arguments)
    {
        Preconditions.checkNotNull(name);
        Preconditions.checkNotNull(returnType);
        Preconditions.checkNotNull(arguments);

        final MethodDescriptor descriptor = methods.get(name);
        if (descriptor == null)
            throw new IllegalArgumentException("no method by name " + name
                + " in class " + classType.getClassName());

        return new MethodInsnNode(methodOpcode, classType.getInternalName(),
            name, descriptor.toString(), isInterface);
    }

    private static final class MethodDescriptor
    {
        private final Type returnType;
        private final Type[] argumentTypes;

        private MethodDescriptor(final Method method)
        {
            returnType = Type.getReturnType(method);
            argumentTypes = Type.getArgumentTypes(method);
        }

        @Override
        public String toString()
        {
            return Type.getMethodDescriptor(returnType, argumentTypes);
        }
    }
}

@@ -293,16 +293,41 @@
 Method;%0A
+import java.util.Arrays;%0A
 import j
@@ -340,16 +340,16 @@
 il.Map;%0A
-
 %0Apublic 
@@ -1093,21 +1093,16 @@
 e... arg
-ument
 s)%0A    %7B
@@ -1230,21 +1230,16 @@
 Null(arg
-ument
 s);%0A%0A   
@@ -1464,16 +1464,69 @@
 Name());
+%0A        descriptor.checkArguments(returnType, args);
 %0A%0A      
@@ -1783,25 +1783,16 @@
 pe%5B%5D arg
-umentType
 s;%0A%0A    
@@ -1919,25 +1919,16 @@
      arg
-umentType
 s = Type
@@ -1947,24 +1947,371 @@
 Types(method
+);%0A        %7D%0A%0A        private void checkArguments(final Type returnType, final Type... args)%0A        %7B%0A            Preconditions.checkArgument(this.returnType.equals(returnType),%0A                %22wrong return type specified%22);%0A            Preconditions.checkArgument(Arrays.equals(this.args, args),%0A                %22wrong argument types specified%22
 );%0A        %7D
@@ -2365,32 +2365,32 @@
 ing()%0A        %7B%0A
+
             retu
@@ -2432,25 +2432,16 @@
 ype, arg
-umentType
 s);%0A    

add argument checker to method invocation