package nxm.nettest.prim;

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

import nxm.nettest.inc.SampleInterface;
import nxm.nettest.intr.Rmiinterface;
import nxm.sys.inc.InternalUseOnly;
import nxm.sys.lib.Midas;
import nxm.sys.lib.Primitive;

/** Java's RMI server (implementing {@link SampleInterface} as a primitive.
    @author Neon Ngo
    @since NeXtMidas 3.1.0
*/
@InternalUseOnly("Since 3.5.2")
public class rmiserver extends Primitive {

  /** RMI registry name to bind to for implementation of remote server object */
  private String bindName     = "SampleRMIServer";
  /** RMI registry hostname */
  private String registryHost = "localhost";
  /** RMI registry port */
  private int    registryPort = Registry.REGISTRY_PORT;
  /** Use RMI Security Manager? */
//  private boolean useSecurityManager = false;
  /** reference to RMI Registry ({@link Registry}) */
  private Registry rmiRegistry;

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

  public int open() {
    bindName     = MA.getCS("NAME", bindName);
    registryHost = MA.getCS("HOST", registryHost);
    registryPort = MA.getL("PORT",  registryPort);
//    useSecurityManager = MA.getState("/SECURITY", false);
    pollTime     = MA.getD("/POLL", 1.0); // don't need to poll very often for this primitive

    runServer();
    return NORMAL;
  }

  public int process () {
    return NOOP;
  }

  public int close() {
    try {
      if (verbose) M.info("Unbinding RMI remote object from server...");
      rmiRegistry.unbind(bindName);
    } catch (Exception e) {
      M.warning("Got exception unbinding ["+bindName+"] : "+ e);
    }

    return NORMAL;
  }

  /** tries to find existing RMI registry at specified host/port,, if not found and host is null or localhost
      then try to create/start the RMI registry on that port.
      @param host RMI registry host (null or blank will be treated as localhost)
      @param port RMI registry port
      @return reference to RMI Registry (null if enable to locate existing or create one locally).
   */
  Registry getOrCreateRegistry (String host, int port) {
    Registry registry = Rmiinterface.findRegistry(host, port, M, false); // first try to locate existing RMI registry

    if ((registry == null) &&                       // could not find existing RMI registry
        (host == null) || host.equals("") || host.equals("localhost") || host.equals("127.0.0.1")) {
      try {                                         // create RMI registry if localhost
        registry = LocateRegistry.createRegistry(port);
        M.info("RMI Registry created on port: "+port);
      } catch (RemoteException re) {
        if (verbose) M.info("Could not create Java RMI registry on localhost at port="+ port);
      }
    }

    if (registry == null) {
      M.warning("Unable to get RMI registry at host="+host+" port="+port);
    }

    return registry;
  }

  /** launch RMI server for {@link SampleInterfaceImpl}. */
  void runServer () {
    if (verbose) M.info("Starting Java RMI server...");

    // following line removed since SecurityManager is deprecated for removal in JDK17+
    //Rmiinterface.initRMISecurity(useSecurityManager, M);
    rmiRegistry = getOrCreateRegistry(registryHost, registryPort);

    try {
      if (verbose) M.info("Creating RMI sample remote (server) object instance...");
      SampleInterfaceImpl obj = new SampleInterfaceImpl();

      if (verbose) M.info("Binding RMI remote instance to name: ["+bindName+"]");
      rmiRegistry.rebind(bindName, obj); // Bind this object instance to the specified bindName (overriding any existing)

      M.info("RMI remote (server) instance bounded in RMI registry, ready to serve clients. bindName="+bindName);
    } catch (Throwable e) {
      M.printStackTrace("Java RMI server got exception", e);
    }
  }

  /** For running in stand-alone mode outside of NeXtMidas (RMI Server of SampleInterface Remote Object implementation)
      Command-line args: [bindName;DEF=SampleRMIServer] [RmiRegistryHost;DEF=localhost] [RmiRegistryPort;DEF=1099]
      @param args command line arguments
   */
  public static void main (String args[]) {
    rmiserver rmiServer = new rmiserver();
    rmiServer.setVerbose(true);
    rmiServer.M = new Midas(); // need this so that info/debug/warn output have a place to print to

    // parse command-line arguments
    if (args.length>1) { rmiServer.bindName     = args[0]; }
    if (args.length>2) { rmiServer.registryHost = args[1]; }
    if (args.length>3) { rmiServer.registryPort = Integer.parseInt(args[2]); }

    rmiServer.runServer();
  }
}
