/*
 * Copyright (c) 2013, Regents of the University of California
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package edu.uci.python.nodes.call;

import static com.oracle.truffle.api.CompilerDirectives.*;

import java.io.*;

import org.python.core.*;

import com.oracle.truffle.api.frame.*;

import edu.uci.python.nodes.*;
import edu.uci.python.nodes.literal.*;
import edu.uci.python.runtime.*;
import edu.uci.python.runtime.function.*;
import edu.uci.python.runtime.standardtype.*;

public class UninitializedCallFunctionNode extends CallFunctionNode {

    @Child protected PNode callee;

    public UninitializedCallFunctionNode(PNode callee, PNode[] arguments, KeywordLiteralNode[] keywords, PythonContext context) {
        super(arguments, keywords, context);
        this.callee = callee;
    }

    @Override
    public PNode getCallee() {
        return callee;
    }

    @Override
    public Object execute(VirtualFrame frame) {
        transferToInterpreterAndInvalidate();
        Object calleeObj = callee.execute(frame);
        if (calleeObj instanceof PythonCallable) {
            PythonCallable callable = (PythonCallable) calleeObj;
            callable.arityCheck(arguments.length, keywords.length, getKeywordNames());

            if (keywords.length == 0) {
                DispatchCallNode callNode = DispatchCallNode.create(getContext(), callable, callee, arguments, keywords);
                replace(callNode);
                return callNode.execute(frame);
            } else {
                CallFunctionNode callFunction = CallFunctionNodeFactory.create(arguments, keywords, getContext(), callee);
                replace(callFunction);
                return callFunction.execute(frame);
            }
        } else if (calleeObj instanceof PythonClass) {
            CallConstructorNode specialized = new CallConstructorNode(getCallee(), arguments);
            replace(specialized);
            Object[] args = CallFunctionNode.executeArguments(frame, arguments);
            return specialized.callConstructor(frame, (PythonClass) calleeObj, args);
        } else {
            if ((calleeObj instanceof PyObject) && (PythonOptions.TraceJythonRuntime)) {
                // CheckStyle: stop system..print check
                PrintStream ps = System.out;
                ps.println("[ZipPy]: calling jython runtime function " + calleeObj);
                // CheckStyle: resume system..print check
            }
            CallFunctionNode callFunction = CallFunctionNodeFactory.create(arguments, keywords, getContext(), callee);
            replace(callFunction);
            return callFunction.execute(frame);
        }
    }

    private String[] getKeywordNames() {
        String[] keywordNames = new String[keywords.length];

        for (int i = 0; i < keywords.length; i++) {
            keywordNames[i] = keywords[i].getName();
        }

        return keywordNames;
    }

}

@@ -2539,28 +2539,37 @@
 if (
-keywords.length == 0
+callable instanceof PFunction
 ) %7B%0A
@@ -2786,32 +2786,322 @@
        %7D else %7B%0A
+                if (keywords.length == 0) %7B%0A                    DispatchCallNode callNode = DispatchCallNode.create(getContext(), callable, callee, arguments, keywords);%0A                    replace(callNode);%0A                    return callNode.execute(frame);%0A                %7D else %7B%0A    
                 
@@ -3215,32 +3215,36 @@
                 
+    
 replace(callFunc
@@ -3242,32 +3242,36 @@
 (callFunction);%0A
+    
                 
@@ -3298,32 +3298,50 @@
 execute(frame);%0A
+                %7D%0A
             %7D%0A  

Use DispatchCallNode to handle calls to PFunction with keyword args