

package io.crate.expression.scalar;

import io.crate.data.Input;
import io.crate.metadata.NodeContext;
import io.crate.metadata.Scalar;
import io.crate.metadata.TransactionContext;
import io.crate.metadata.functions.BoundSignature;
import io.crate.metadata.functions.Signature;
import io.crate.types.DataType;

import java.util.function.Function;


/**
 * Scalar implementation that wraps another function: f(T) -> R
 * <br />
 * null values will result in null as output
 */
public class UnaryScalar<R, T> extends Scalar<R, T> {

    private final Function<T, R> func;
    private final Signature signature;
    private final BoundSignature boundSignature;
    private final DataType<T> type;


    public UnaryScalar(Signature signature,
                       BoundSignature boundSignature,
                       DataType<T> type,
                       Function<T, R> func) {
        assert boundSignature.argTypes().get(0).id() == type.id() :
            "The bound argument type of the signature must match the type argument";
        this.signature = signature;
        this.boundSignature = boundSignature;
        this.type = type;
        this.func = func;
    }

    @Override
    public Signature signature() {
        return signature;
    }

    @Override
    public BoundSignature boundSignature() {
        return boundSignature;
    }

    @SafeVarargs
    @Override
    public final R evaluate(TransactionContext txnCtx, NodeContext nodeCtx, Input<T>... args) {
        assert args.length == 1 : "UnaryScalar expects exactly 1 argument, got: " + args.length;
        T value = type.sanitizeValue(args[0].value());
        if (value == null) {
            return null;
        }
        return func.apply(value);
    }
}
