package php.runtime.invoke;

import php.runtime.common.Messages;
import php.runtime.exceptions.support.ErrorType;
import php.runtime.env.Environment;
import php.runtime.env.TraceInfo;
import php.runtime.ext.core.classes.WrapInvoker;
import php.runtime.lang.ForeachIterator;
import php.runtime.Memory;
import php.runtime.memory.ObjectMemory;
import php.runtime.reflection.ParameterEntity;

abstract public class Invoker {
    protected final Environment env;
    protected TraceInfo trace;
    protected boolean pushCallTrace = true;

    protected Invoker(Environment env, TraceInfo trace) {
        this.env = env;
        this.trace = trace;
    }

    public Environment getEnvironment() {
        return env;
    }

    public void setPushCallTrace(boolean pushCallTrace) {
        this.pushCallTrace = pushCallTrace;
    }

    public void check(String name, TraceInfo trace){

    }

    abstract public ParameterEntity[] getParameters();

    abstract public String getName();

    abstract public int getArgumentCount();

    abstract protected void pushCall(TraceInfo trace, Memory[] args);

    abstract protected Memory invoke(Memory... args) throws Throwable;

    final public Memory call(Memory... args) throws Throwable {
        trace = trace == null ? (env == null ? TraceInfo.UNKNOWN : env.trace()) : trace;
        /*pushCall(trace, args);
        try {*/
            return invoke(args);
        /*} finally {
            popCall();
        }*/
    }

    public Memory callNoThrow(Memory... args){
        try {
            return call(args);
        } catch (RuntimeException e){
            throw e;
        } catch (Throwable e){
            throw new RuntimeException(e);
        }
    }

    protected void popCall(){
        env.popCall();
    }

    public void setTrace(TraceInfo trace) {
        this.trace = trace;
    }

    public static Invoker valueOf(Environment env, Memory method){
        return valueOf(env, env.peekCall(0).trace, method);
    }

    abstract public int canAccess(Environment env);

    public static Invoker valueOf(Environment env, TraceInfo trace, Memory method){
        method = method.toValue();
        if (method.isObject()){
            if (method.toValue(ObjectMemory.class).value instanceof WrapInvoker)
                return method.toObject(WrapInvoker.class).getInvoker();

            return DynamicMethodInvoker.valueOf(env, trace, method);
        } else if (method.isArray()){
            Memory one = null, two = null;
            ForeachIterator iterator = method.getNewIterator(env, false, false);
            while (iterator.next()){
                if (one == null)
                    one = iterator.getValue();
                else if (two == null)
                    two = iterator.getValue();
                else
                    break;
            }

            if (one == null || two == null) {
                if (trace == null) {
                    return null;
                }
                env.error(trace, ErrorType.E_ERROR, Messages.ERR_CALL_TO_UNDEFINED_FUNCTION.fetch(method.toString()));
            }

            assert one != null;
            assert two != null;
            String methodName = two.toString();
            if (one.isObject()) {
                return DynamicMethodInvoker.valueOf(env, trace, one.toValue(), methodName);
            } else {
                String className = one.toString();
                return StaticMethodInvoker.valueOf(env, trace, className, methodName);
            }
        } else {
            String methodName = method.toString();
            int p;
            if ((p = methodName.indexOf("::")) > -1) {
                String className = methodName.substring(0, p);
                methodName = methodName.substring(p + 2, methodName.length());
                return StaticMethodInvoker.valueOf(env, trace, className, methodName);
            } else {
                return FunctionInvoker.valueOf(env, trace, methodName);
            }
        }
    }
}

@@ -58,16 +58,65 @@
 ssages;%0A
+import php.runtime.exceptions.CriticalException;%0A
 import p
@@ -375,32 +375,83 @@
 y.ObjectMemory;%0A
+import php.runtime.memory.support.MemoryOperation;%0A
 import php.runti
@@ -1557,32 +1557,729 @@
      %7D*/%0A    %7D%0A%0A
+    final public Memory callAny(Object... args) %7B%0A        if (args != null && args.length %3E 0) %7B%0A            Memory%5B%5D passed = new Memory%5Bargs.length%5D;%0A%0A            for (int i = 0; i %3C passed.length; i++) %7B%0A                MemoryOperation operation = MemoryOperation.get(args%5Bi%5D.getClass(), args%5Bi%5D.getClass().getGenericSuperclass());%0A                if (operation == null) %7B%0A                    throw new CriticalException(%22Unsupported bind type - %22 + args%5Bi%5D.getClass().toString());%0A                %7D%0A%0A                passed%5Bi%5D = operation.unconvert(env, trace, args%5Bi%5D);%0A            %7D%0A%0A            return callNoThrow(passed);%0A        %7D else %7B%0A            return callNoThrow();%0A        %7D%0A    %7D%0A%0A
     public Memor

Add Invoker.callAny method for calling with any type arguments