package org.graalphp.nodes.assign;

import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.graalphp.exception.PhpException;
import org.graalphp.nodes.PhpExprNode;
import org.graalphp.runtime.array.PhpArray;

/**
 * Node which assigns a value by reference instead of default behavior which is by value
 * This represents the & operator for assignments
 * <p>
 * We currently implement a subset, namely assign for Arrays
 * <p>
 * Ideas to implement assign by reference for other types;
 * - Wrap integer within BoxedInteger and pass reference around
 *
 * @author abertschi
 */
@NodeChild(value = "source", type = PhpExprNode.class)
public abstract class AssignByReferenceNode extends AssignSemanticNode {

    public static AssignByReferenceNode createWithoutChild() {
        return AssignByReferenceNodeGen.create(null);
    }

    protected abstract Node getSource();

    @Specialization
    protected Object forwardArray(
            PhpArray array) {
        // XXX: We currently support assign by reference of Arrays
        return array;
    }

    @Specialization(guards = "notArray(val)")
    protected Object forwardObject(Object val) {
        throw new PhpException("Assign by reference not yet supported for "
                + val.getClass().getSimpleName(), this);
    }

    protected final boolean notArray(Object o) {
        return !(o instanceof PhpArray);
    }

    @Override
    public String toString() {
        return "AssignByReferenceNode{" + getSource() + "}";
    }

}
