package nxm.nettest.intr;

import java.rmi.AlreadyBoundException;
import java.rmi.NotBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

import nxm.sys.inc.InternalUseOnly;
import nxm.sys.lib.Intrinsic;
import nxm.sys.lib.Midas;
import nxm.sys.lib.MidasException;

/** Java's Remote Method Invocation (RMI) interface using the {@link java.rmi.registry.Registry}
    class for NeXtMidas.
    @author Neon Ngo
    @since NeXtMidas 3.1.0
*/
@InternalUseOnly("Since 3.5.2")
public class Rmiinterface extends Intrinsic {

  private static final String funcList = "LIST,LOOKUP,BIND,REBIND,UNBIND";
  private Registry rmiregistry;

  /** default constructor */
  public Rmiinterface () {}

  public int process () {
    String func  = MA.getSelection("FUNC", funcList, "LIST");
    String label = MA.getU("LABEL", ""); // output for LIST & LOOKUP; input for BIND & REBIND
    String bindName = MA.getCS("NAME", "");
    String registryHost = MA.getCS("HOST");
    int    registryPort = MA.getL("PORT",  Registry.REGISTRY_PORT);
    boolean useSecurityManager = MA.getState("/SECURITY", false);
    if (useSecurityManager) M.warning("SecurityManager is deprecated for removal. " +
      "The /SECURITY is being ignored.");

    Midas midas = (verbose ? M : null);
//    initRMISecurity(useSecurityManager, midas); // removed in 4.1.3 because SecurityManager is deprecated for removal
    rmiregistry = findRegistry(registryHost, registryPort, midas, true);

    try {
      if (func.equals("LIST")) {
        list(label);
      }
      else if (func.equals("LOOKUP")) {
        lookup(bindName, label);
      }
      else if (func.equals("BIND") || func.equals("REBIND")) {
        Object obj = MA.getO("LABEL"); // input for BIND/REBIND
        bindHelper(func, bindName, obj);
      }
      else if (func.equals("UNBIND")) {
        rmiregistry.unbind(bindName);
      }
      else {
        M.warning("Unsupported function: "+func);
      }
    } catch (Exception e) {
      M.error(e);
    }

    return FINISH;
  }

// JDK 17+ have SecurityManager deprecated for removal
//  /** Initialize the System Security manager with a security manager if one
//      one doesn't exists and createIfNone is true.
//      @param createIfNone true to install {@link SecurityManager} is none exists
//      @param midas Midas context to print info statement (if not null) for debugging/info
//      @return instance of current system Security Manager (null if none).
//   */
//  public synchronized static SecurityManager initRMISecurity (boolean createIfNone, Midas midas) {
//    SecurityManager securityMan = System.getSecurityManager();
//    if (securityMan == null) {
//      if (createIfNone) {
//        // Create and install a security manager
//        securityMan = new SecurityManager();
//        System.setSecurityManager(securityMan);
//        if (midas != null) midas.info("Security manager installed. " + securityMan);
//      }
//    } else {
//      if (midas != null) midas.info("Security manager already exists. " + securityMan);
//    }
//    return securityMan;
//  }

  /** Get/locate RMI registry at specified host/port.
      @param host RMI registry host (null is treated as localhost)
      @param port RMI registry port
      @param midas Midas context to print info statement (if not null) when registry is located
      @param throwException if a MidasException should be thrown if registration is not found
      @return reference to RMI registry if not found at specified host/port
      @throws MidasException if cannot find RMI registry and throwException is true
      @see LocateRegistry#getRegistry(String, int)
   */
  public static Registry findRegistry (String host, int port, Midas midas, boolean throwException) {
    Registry registry = null;
    try {
      registry = LocateRegistry.getRegistry(host, port);
      if (midas != null) midas.info("Found Java RMI registry on host=" + host + " port=" + port);
    } catch (RemoteException re) {
      if (throwException) {
        throw new MidasException("Unable to locate RMI registry at host=" + host + " port=" + port);
      }
    }
    return registry;
  }

  private void list (String label) throws RemoteException {
    String[] results = rmiregistry.list();
    MR.put(label, results);
    if (verbose || (label == null) || (label.length()==0)) {
      M.type("Names bounded in the RMI registry at URL ["+name+"]");
      int ii = 0;
      for (String item : results) {
        ++ii;
        M.type("  "+ ii + ". " + item);
      }
    }
  }

  private void lookup (String name, String label) throws NotBoundException, RemoteException {
    Remote remoteObj = rmiregistry.lookup(name);
    MR.put(label, remoteObj);
  }

  private void bindHelper (String func, String name, Object obj) throws AlreadyBoundException, RemoteException {
    if (!(obj instanceof Remote)) {
      M.error("Cannot "+func+" when given an Object that is NOT an instanceof java.rmi.Remote : " + obj);
    }
    Remote remoteObj = (Remote) obj;;
    if (func.equals("BIND")) {
      rmiregistry.bind(name, remoteObj);
    }
    else if (func.equals("REBIND")) {
      rmiregistry.rebind(name, remoteObj);
    }
  }
}
