package nxm.sys.intr;

import java.net.Authenticator;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.PasswordAuthentication;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;

import javax.swing.JOptionPane;

import nxm.sys.lib.HttpResource;
import nxm.sys.lib.Intrinsic;
import nxm.sys.lib.Table; // using Table library (instead of Intrinsic)
import nxm.sys.libg.MJPopup;
import nxm.sys.net.HttpAuthenticator;

/** Set the default authenticator for HttpURLConnection that requires HTTP/HTTPS authentication.
    The default behavior is to used credential entries added via the ADD function.
    The /GUI switch can be use to enable a popup dialog to prompt username/password,
    if there isn't a matching credential entry.

    @author  Neon Ngo
    @since   NeXtMidas 2.9.0
    @see HttpAuthenticator
 */
public class Httpauth extends Intrinsic {

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

  public int open () {
    boolean showStatus = verbose; // display status when run from command line
    HttpAuthenticator authenticator = HttpAuthenticator.getInstance();

    String func = MA.getS("FUNC");
    String url  = MA.getCS("URL"); // URL prefix (or exact matching URL)
    String user = MA.getCS("USERNAME"); // for ADD
    String pass = MA.getCS("PASSWORD"); // for ADD

    if (MA.isPresent("/GUI")) {
      LoginPromptGUI callback = null;  // null for none
      boolean enableGUI = MA.getState("/GUI", false);
      if (enableGUI) {
        callback = LoginPromptGUI.INSTANCE;
        callback.defaultUsername = MA.getCS("/DEFAULTUSER", MR.getS("ENV.USER"));
        callback.setTitle(MA.getCS("/TITLE", null));
        authenticator.enable(true);      // GUI prompt is enabled, so enable authenticator
      }
      authenticator.setCallback(callback);
    }

    if (func.startsWith("ADD")) {        // Add Login Credential entry: URL,user,password
      if (user == null || user.length()==0) M.error("HTTPAUTH: ADD entry requires USERNAME to be set");
      if (pass == null || pass.length()==0) M.error("HTTPAUTH: ADD entry requires PASSWORD to be set");
      authenticator.put(url, user, pass);
      // Add the following just in case we need to use it with port instead of default port. since 3.9.1
      try {
        URL testUrl = (new URI(url).toURL());
        URL testUrl2 = HttpResource.createURLWithPort(testUrl);

        // only do this if we don't have a port defined.
        if(testUrl2!=testUrl) {
          authenticator.put(testUrl2.toString(), user, pass);
        }

      } catch (MalformedURLException | URISyntaxException e) {
        M.warning("unable to add user/pass to the default port");
      }
    }
    else if (func.startsWith("REM")) {   // Remove Login Credential entry: URL
      authenticator.remove(url);
    }
    else if (func.startsWith("CLEAR")) { // Clear All Login Credential entries
      authenticator.clear();
    }
    else if (func.equals("ENABLE")) {
      authenticator.enable(true);        // enable authenticator for current JVM
    }
    else if (func.equals("DISABLE")) {
      authenticator.enable(false);       // disable authenticator for current JVM
    }
    else if (func.equals("STATUS")) {    // display state of this Authenticator
      showStatus = true;
    }
    else if (func.length()>0) {
      M.error("HTTPAUTH: Unknown function ["+func+"] specified!");
    }

    // the /ENABLE switch will override ENABLE/DISABLE function and /GUI above
    if (MA.isPresent("/ENABLE")) {
      authenticator.enable(MA.getState("/ENABLE", false));
    }

    // the /STATUS switch will override showStatus settings above and INFO function
    if (MA.getState("/STATUS", showStatus)) {
      M.info("HTTPAUTH: status of "+authenticator);
    }
    return FINISH;
  }

  /** Implements HttpAuthenticator.CallbackIF to prompt user for authentication
      credentials (username/password) using {@link MJPopup}'s login dialog.
      The dialog will include the servers prompt string (realm) and actual URL.
      Note: This class does not support username or passwords with trailing spaces.
      @author Neon Ngo
   */
  static class LoginPromptGUI implements HttpAuthenticator.CallbackIF {
    /** singleton instance of this class */
    public static final LoginPromptGUI INSTANCE = new LoginPromptGUI();
    /** Hide (private) constructor */
    private LoginPromptGUI () {}
    /** default username to put in GUI prompt (null for none) */
    String defaultUsername = null;
    /** default window title for login popup dialog */
    static final String DEF_TITLE = "Authorization Required";
    /** Window title for login popup dialog */
    private String title = DEF_TITLE;

    /** Set popup window title
        @param newTitle window title for login popup dialog, null or empty to use default {@link #DEF_TITLE}
     */
    void setTitle (String newTitle) {
      title = (newTitle==null || newTitle.length()==0)? DEF_TITLE : newTitle;
    }

    public PasswordAuthentication authenticate (String host, InetAddress addr, int port,
             String protocol, String prompt, String scheme, URL url, Authenticator.RequestorType reqType) {
      String message = "<html><b>Realm:</b> " + prompt
                     + "<br><b>Url:</b> "     + url
                     + "<br></html>";
      int messageType = JOptionPane.QUESTION_MESSAGE;
      Table userpass = MJPopup.showLogin(null, title, messageType, message, defaultUsername);
      if (userpass != null) {
        String user = userpass.getS("USER");
        String pass = userpass.getS("PASS");
        if (user.length()>0 && pass.length()>0) {
          return new PasswordAuthentication(user, pass.toCharArray());
        }
      }
      return null;
    }

    public String toString () {
      return "LoginPromptGUI("+defaultUsername+")";
    }
  } // end class HttpAuthGUI

}
