package nxm.sys.prim;

import java.io.File;

import nxm.sys.inc.Chainable;
import nxm.sys.lib.Primitive;
import nxm.sys.lib.Shell;
import nxm.sys.lib.Table;
import nxm.sys.lib.Time;
import nxm.sys.net.HFiles;
import nxm.sys.net.HPage;
import nxm.sys.net.HServer;
import nxm.sys.net.HSource;

/** Primitive which runs an HTTP server in the background. The primitive must
    be started with the name of a directory which contains the files to serve.
    The directory can be specified using either an absolute path or a path
    relative to NMROOT.<br>
    <br>
    If the DIR flag begins with a path separator character then it assumed to
    be an absolute path. Otherwise it is assumed to be relative to NMROOT.

    @author  J. Czechowski IV (updated version) / Neon Ngo
 */
public class httpserv extends Primitive implements Chainable {
  /** The HServer object for the instance. */
  protected HServer server;

  /** The thread that the server executes in. */
  protected Thread srvThread;

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

  public int open() {
    int    port     = MA.getL("PORT");  // 0 for any free port
    String dirName  = MA.getCS("NAME"); // Default of "help" is provided by commands.cnf
    String dir      = MA.getCS("DIR");  // Default of "htdocs" is provided by commands.cnf
    String lname    = MA.getCS("/MNAME", null);
    String homepage = MA.getCS("/HOMEPAGE", null);
    String auxlist  = MA.getS("/AUXLIST", null);
    int    prange   = MA.getL("/PRANGE", 1); // range of port numbers above port to use if already allocated
    HServer.setHttps(MA.getState("/HTTPS", MA.find("/HTTPS"))); // Is it HTTPS, set it in HServer  since 3.9.0
    HServer.setOneWay(MA.getState("/ONEWAY", MA.find("/ONEWAY"))); // Is it insecure (one way auth). set it in HServer since 3.9.0
    boolean isWin = Shell.isWindows();
    if(isWin) {
      // windows fully qualified path will start with C:, not a File.separator
      if(dir.startsWith(File.separator)) {
        dir = "C:"+ dir;
      }
      // If it doesn't start with C:, we need to prepend nmroot to get full path.
      else if(!dir.startsWith("C:")) {
        dir = Shell.getNmRoot() + dir;
      }
    }
    // Linux full path will start with a File.separator
    else if (!dir.startsWith(File.separator)) {
      dir = Shell.getNmRoot() + dir;
    }

    // Create the server and register a new DirectorySource with it.
    // Don't forget to put the leading slash.
    server = HServer.launch(this, "HTTP Server", port, prange, homepage, auxlist);
    server.setParent(this);
    server.addSource(new HFiles(dirName, dir));
    if ((lname != null) && (lname.length() > 0)) {
      if (getMessageHandler() == null) {
        M.warning("HTTPSERV: Found /MNAME= but missing /MSGID=.");
      } else {
        boolean isRoot = lname.equals("/"); // /MNAME is the root on this server and should override all
        server.addSource(new HSListener(lname), isRoot);
      }
    }

    return NORMAL;
  }

  public int process () {
    if (server.isRunning()) return NOOP;
    else                    return FINISH;
  }

  public int close() {
    server.stopThread();
    return NORMAL;
  }

  /** Listens for requests. */
  private class HSListener extends HSource {
    public HSListener(String name) {
      super(name);
    }

    /** Invoked to write the data from this source into the specified HPage.
        @param uri The Request string containing the request parameters.
        @param hp The HPage to write the data into.
     */
    public void handleRequest (String uri, HPage hp) {
      Table tbl = new Table();
      tbl.put("PARAMETERS", hp.getParameters());
      tbl.put("SOCKET", hp);
      sendMessage("HTTP"+hp.getMethod(), 0, tbl);
      while (!hp.isClosed()) {
        Time.sleep(0.5); // Wait for the request to be handled.
      }
    }
  }

  public Object getNextLink() { return server; }
  public Object getPrevLink() { return null; }
}
