package io.ray.runtime.functionmanager;

import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.util.Optional;

/** Represents a Ray function (either a Method or a Constructor in Java) and its metadata. */
public class RayFunction {

  /** The executor object, can be either a Method or a Constructor. */
  public final Executable executable;

  /** This function's class loader. */
  public final ClassLoader classLoader;

  /** Function's metadata. */
  public final JavaFunctionDescriptor functionDescriptor;

  public RayFunction(
      Executable executable, ClassLoader classLoader, JavaFunctionDescriptor functionDescriptor) {
    this.executable = executable;
    this.classLoader = classLoader;
    this.functionDescriptor = functionDescriptor;
  }

  /** Returns True if it's a constructor, otherwise it's a method. */
  public boolean isConstructor() {
    return executable instanceof Constructor;
  }

  /** Returns The underlying constructor object. */
  public Constructor<?> getConstructor() {
    return (Constructor<?>) executable;
  }

  /** Returns The underlying method object. */
  public Method getMethod() {
    return (Method) executable;
  }

  public JavaFunctionDescriptor getFunctionDescriptor() {
    return functionDescriptor;
  }

  /** Returns Whether this function has a return value. */
  public boolean hasReturn() {
    if (isConstructor()) {
      return true;
    } else {
      return !getMethod().getReturnType().equals(void.class);
    }
  }

  /** Returns Return type. */
  public Optional<Class<?>> getReturnType() {
    if (hasReturn()) {
      return Optional.of(((Method) executable).getReturnType());
    } else {
      return Optional.empty();
    }
  }

  @Override
  public String toString() {
    return executable.toString();
  }
}
