package nxm.sys.prim;

import java.awt.Component;

import javax.swing.JOptionPane;

import nxm.sys.lib.Args;
import nxm.sys.lib.Convert;
import nxm.sys.lib.Message;
import nxm.sys.lib.MidasException;
import nxm.sys.lib.Parser;
import nxm.sys.lib.Table;
import nxm.sys.lib.Time;
import nxm.sys.libg.GPrimitive;
import nxm.sys.libg.MJPopup;
import nxm.sys.libg.MWindow;

/** This provides macros with an easy-to-use interface to {@link JOptionPane} and the related
    Swing pop-ups. This is intended for use by macros only. Primitives should use the Swing
    classes directly.
    <pre>
      <u>File History</u>
      NeXtMidas 2.0.0  - Initial version in UCL.
      NeXtMidas 2.2.0  - Moved to SYS as an intrinsic.
      NeXtMidas 2.3.0  - Converted to a primitive (added non-blocking option).
      NeXtMidas 2.5.0  - Moved most of the code into the {@link MJPopup} library.
    </pre>
    @author  J. Czechowski IV
    @since   NeXtMidas 2.3.0
*/
public class popup extends GPrimitive {
  /** List of options.
      @deprecated Since NeXtMidas 2.5.0 use <code>MJPopup.blockingOptionsList</code>
   */
  @Deprecated public static final String blockingOptionsList = MJPopup.blockingOptionsList;

  private static final int BLOCK_DEFAULT = MJPopup.BLOCK_MOUSE; // Default blocking mask.

  private String      msgName;     // The message name to use.
  private String      result;      // The name of the result to output.
  private String      func;        // The function (type of popup).
  private Component   parent;      // The parent component.
  private MWindow     parentMW;    // The MWindow for the parent component (if available).
  private Object      message;     // The message to display.
  private int         type;        // The message type (see getMessageType(..)).
  private String      defValue;    // The default value for a "Text" input.
  private Object[]    choices;     // The options for a "Choice" input.
  private Table       parameters;  // Parameters for "Group" input.
  private String      title;       // The title for the popup.
  private MJPopup     popup;       // The popup being used.
  private int         blocking;    // Blocking options mask.
  private int[]       position;    // Initial position for dialog.
  private boolean     cancelMsg;   // If CANCEL is a button, send a message if true

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

  /** Need to check the /BLOCKING= switch and manually force POPUP to be backgrounded when the
      Macro option is turned off. Also always turn off the blcock Macro option when /BG has been
      set by the user.
   */
  public void setArgs(Args args) {
    super.setArgs(args);
    String blockStr = args.getS("/BLOCKING", "MOUSE");
    blocking = Parser.mask(MJPopup.blockingOptionsList, blockStr, BLOCK_DEFAULT);

    if ((blocking & MJPopup.BLOCK_MACRO) == 0) {
      args.put("/BG", "TRUE");
    } else if (args.getState("/BG", false)) {
      blocking = blocking & (~MJPopup.BLOCK_MACRO); // Always run non-blocking when backgrounded.
      args.put("/BLOCKING", Parser.mask2s(MJPopup.blockingOptionsList, blocking));
    }
  }

  public int open() {
    String defTitle = (M.macro == null)? "NeXtMidas": M.macro.name;
    int    status   = NORMAL;

    String blockStr = MA.getS("/BLOCKING", "MOUSE");

    result     = MA.getU("RESULT","").toUpperCase();
    func       = MA.getU("FUNC",  "").toUpperCase();
    message    = MA.getCS("MESSAGE");
    type       = getMessageType(MA.getU("TYPE", null));
    choices    = new String[0]; // May be set below
    defValue   = MA.getCS("DEFAULTS", "");
    parameters = MA.getTable("PARAMETERS");
    title      = MA.getCS("/TITLE", defTitle);
    blocking   = Parser.mask(MJPopup.blockingOptionsList, blockStr, BLOCK_DEFAULT);
    msgName    = MA.getS("/MSGNAME", "POPUP");
    position   = getPopupPosition(MA.getS("/WPOS","CENTER"));
    cancelMsg  = MA.getState("/CANCELMSG",true);

    String opts = MA.getCS("OPTIONS",  "");
    if ((opts != null) && (opts.length() > 0)) {
      choices = opts.split("[|,]");
    }

    Object win = MA.getO("/WIN", null);
    if (win instanceof Component) {
      parent = (Component)win;
    } else if (GPrimitive.isInstance(win)) {
      parentMW = GPrimitive.getMWindowViaReflection(win);
      parent   = GPrimitive.getGraphicsContainerViaReflection(win);
    } else {
      if ((win == null) && (M.macro != null)) { win = "PANEL"; }

      win = MR.get("REG."+win);
      if (GPrimitive.isInstance(win)) {
        parentMW = GPrimitive.getMWindowViaReflection(win);
        parent   = GPrimitive.getGraphicsContainerViaReflection(win);
      }
    }
    popup = new MJPopup (MA.getL("/POPWIDTH",80));  // create empty MJPopup first so we can set the width (BUG 2019)
    if ((blocking & MJPopup.BLOCK_MACRO) != 0) {
      showWindow();
      while (!popup.isDone()) { Time.sleep(0.5); } // Wait for input
      status = FINISH;
    } else {
      showWindow();
    }

    return status;
  }

  public int process() {
    int status = NORMAL;

    if ((blocking & MJPopup.BLOCK_MACRO) != 0) { // <-- should never be true, but check just in case
      status = FINISH;
    } else if (popup.isDone()) {
      status = FINISH;
    } else {
      status = NOOP; // Wait for input
    }

    return status;
  }

  public int close() {
    int     info   = popup.getInfo();
    Object  data   = popup.getValue();
    boolean cancel = popup.getCancel();

    if (cancelMsg || !cancel) {
      sendMessage(new Message(msgName, info, data));
    }
    if ((result != null) && (result.length() > 0)) {
      MR.put(result, data);
    } else if (verbose) {
      M.type("input = " + data);
    }
    popup.dispose();
    return NORMAL;
  }

  public int processMessage(Message msg) {
    int status = NORMAL;

    if (msg.name.equals("CANCEL")) {
      popup.cancel();
    } else {
      status = super.processMessage(msg);
    }

    return status;
  }

  /** Displays the popup window in the current thread. */
  private void showWindow() {
    if (func.equals("GROUP")) {
      popup = MJPopup.createGroup(parent,title,type,message,parameters);
    } else if (func.equals("YESNO")) {
      popup = MJPopup.createYesNo(parent,title,type,message);
    } else if (func.equals("YESNOCANCEL")) {
      popup = MJPopup.createYesNoCancel(parent,title,type,message);
    } else if (func.equals("OK")) {
      popup = MJPopup.createOk(parent,title,type,message);
    } else if (func.equals("OKCANCEL")) {
      popup = MJPopup.createOkCancel(parent,title,type,message);
    } else if (func.equals("TEXT")) {
      popup = MJPopup.createText(parent,title,type,message,defValue);
    } else if (func.equals("PASSWORD")) {
      popup = MJPopup.createPassword(parent,title,type,message,defValue);
    } else if (func.equals("CHOICE")) {
      popup = MJPopup.createChoice(parent,title,type,message,choices,defValue);
    } else if (func.equals("MASK")) {
      popup = MJPopup.createIntMask(parent,title,type,message,choices,defValue);
    } else if (func.equals("MASK/S")) {
      popup = MJPopup.createMask(parent,title,type,message,choices,defValue);
    } else if (func.equals("BUTTONS")) {
      popup = MJPopup.createButtons(parent,title,type,message,choices);
    } else if (func.equals("COLOR")) {
      popup = MJPopup.createColor(parent,title,type,message,defValue);
    } else if (func.equals("LOGIN")) {
      popup = MJPopup.createLogin(parent,title,type,message,defValue);
    } else if (func.equals("ALERT")) {
      popup = MJPopup.createAlert(parent,title,type,message);
    } else if (func.equals("FILE") || func.equals("FILE/M")) {
      popup = MJPopup.createFile(parent,title,type,message,defValue,MA.getS("FILTER", null),
              MA.getS("AUXLIST",null),MA.getS("PATHOPT",null));
    } else if (func.equals("FILE/J")) {
      String pathOpt = MA.getS("PATHOPT", "FullPath");
      if (!pathOpt.equalsIgnoreCase("FullPath")) {
        throw new MidasException("Can not use PATHOPT="+pathOpt+" with a FILE/J popup.");
      }
      throw new UnsupportedOperationException("This version of POPUP does not support FILE/J.");
    } else {
      throw new MidasException("Unknown function (FUNC=" +func + ").");
    }
    popup.display(parentMW,blocking,position);  // JWB - BUG 1964 - Added blocking variable
  }

  /** Gets a message type ID for a named message type.
      @param name The name of the message type.
      @return The appropriate ID.
   */
  private int getMessageType(String name) {
    boolean err = true; // ERROR - ID not set
    int     id  = JOptionPane.PLAIN_MESSAGE;
    name = (name != null)? name.toUpperCase(): "null";

    if (name.startsWith("ERR"))   {id=JOptionPane.ERROR_MESSAGE;      err=false;}
    if (name.startsWith("INFO"))  {id=JOptionPane.INFORMATION_MESSAGE;err=false;}
    if (name.startsWith("WARN"))  {id=JOptionPane.WARNING_MESSAGE;    err=false;}
    if (name.startsWith("QUEST")) {id=JOptionPane.QUESTION_MESSAGE;   err=false;}
    if (name.startsWith("PLAIN")) {id=JOptionPane.PLAIN_MESSAGE;      err=false;}

    if (err) {
      M.warning("Unknown message type (" + name + ") using default.");
    }
    return id;
  }

  /** Gets the popup position information for a given /WPOS.
      @param wpos The value of /WPOS.
      @return The appropriate position.
   */
  private int[] getPopupPosition(String wpos) {
    int[]    position = MJPopup.POSITION_CENTER;
    String[] coords   = null;

    if (wpos.startsWith("(") && wpos.endsWith(")")) {
      coords = wpos.substring(1,wpos.length()-1).split(",");
    }

    if ((wpos == null) || wpos.equalsIgnoreCase("CENTER")) {
      position = MJPopup.POSITION_CENTER;
    } else if (wpos.equalsIgnoreCase("MOUSE")) {
      position = MJPopup.POSITION_MOUSE;
    } else if ((coords != null) && ((coords.length == 2) || (coords.length == 4))) {
      position = new int[coords.length];
      for (int i = 0; i < position.length; i++) {
        position[i] = Convert.s2l(coords[i]);
      }
    } else {
      M.warning("POPUP: Illegal window position /WPOS="+wpos+" expected one of: "
              + "'CENTER', 'MOUSE', '(x,y)', or '(x,y,w,h)'.");
    }
    return position;
  }
}
