package nxm.sys.intr;

import java.util.Vector;

import nxm.sys.inc.LegacyMethod;
import nxm.sys.lib.Args;
import nxm.sys.lib.Convert;
import nxm.sys.lib.FileUtil;
import nxm.sys.lib.Intrinsic;
import nxm.sys.lib.Midas;
import nxm.sys.lib.Parser;
import nxm.sys.lib.StringUtil;

/**
  Utility for editing strings from the macro language (or static).

  @author Jeff Schoen / Jay Hallam
*/
public class Sedit extends Intrinsic {
  // Note: As much of Sedit is general library type functionality, much of Sedit
  // functionality was moved to methods in library Classes. (since 3.9.2)

  /** input string */
  private String istr;
  /** output string */
  private String ostr;
  private Object obj=null;

  /** How many deprecation warnings will be issues per session for the 3.1.2 SEDIT changes */
  private static int numSeditWarns = 3; // remove when SEDIT changes go as default

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

  public int process () {
    numSeditWarns = MA.getL("/NUMWARNS",numSeditWarns);  // NOT DOCUMENTED: change how many 3.1.2 deprecation warnings per session

    istr = MA.getCS("ISTR");
    ostr = MA.getU("OSTR");
    boolean dbg = MA.getState("/DEBUG");
    int tblflags = (MA.getState("/FORCE")? nxm.sys.lib.Table.FORCE : 0); // since 2.9.3

    obj = Convert.processSeditCommandLine(istr, M, MA, 3, dbg); // Call the moved version of processSeditCommandLine since 3.9.2

    if (ostr.length()>0) {
      MR.put (ostr, obj, tblflags);
    } else {
      M.println ("Output string ='"+obj+"'");  // since 3.7.1 using M.println instead of M.terminal.writeln to allow logging - NXM-1813
    }
    return FINISH;
  }

  /** Process the command line arguments to this command.
      This is its own method to be called by inline SEDIT
   *  @param inputString  Input string
   *  @param M            Midas context
   *  @param MA           Args
   *  @param nextArgIndex Number of arguments
   *  @param dbg          True if debug mode
   *  @return the resulting value
   */
  @LegacyMethod("use method in library Class: nxm.sys.lib.Convert.processSeditCommandLine(String, Midas, Args, int, boolean)")
  public static Object processCommandLine (String inputString, Midas M, Args MA,
                                          int nextArgIndex, boolean dbg) {
    return Convert.processSeditCommandLine(inputString, M, MA, nextArgIndex, dbg);
  }

  /** Convert nulls and the word 'null' to an empty string to keep the
      sedit interface consistent.  Ensures "" is returned to the user
      instead of the word 'null'. <br>
      This is the same as <code>StringUtil.convertNullsToEmpty(obj)</code>.
      @param obj The object to convert to an empty string iff it is a null.
      @return The results of calling <code>StringUtil.convertNullsToEmpty(obj)</code>.
      @see StringUtil#convertNullsToEmpty(Object)
   */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.convertNullsToEmpty(Object)")
  public static Object convertNullsToEmpty (Object obj) {
    return StringUtil.convertNullsToEmpty(obj);
  }

  /** Get the value from a string containing TAG1=VAL1  TAG2=VAL2 pairs.
      A delimiter other than "=" may be used to separate the tag and value. <br>
      This is the same as <code>StringUtil.getKeyValue(inString,tag,delim)</code>.
      @param inString The input "TAG1=VAL TAG2=VAL2 ... " string
      @param tag      The name of the tag
      @param delim    The delimiter, if null or "" the equal sign (=) is used
      @return Everything from the matching tag plus delimeter to the first
              whitespace character or <code>null</code> if tag is not found.
      @see StringUtil#getKeyValue(String,String,String)
   */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.StringUtil#getKeyValue(String, String, String)")
  public static String getKeyValue (String inString, String tag, String delim) {
    return StringUtil.getKeyValue(inString,tag,delim);
  }

  /** Parses given element in string by "natural" comma or space delimiter.  <br>
      See {@link #parse(String,int,String,boolean)} for details.
      @param string   The string to parse.
      @param element  The index of the element to get.
      @param clean    Indicates that the string should be cleaned while parsing.
      @return The element within the parsed string at the specified index or
              <code>""</code> if <code>index</code> is invalid.
  */
  @LegacyMethod("use method in library Class: xm.sys.lib.StringUtil.parse(String, int, String, boolean)")
  public static String parse (String string, int element, boolean clean) {
    return StringUtil.parse(string,element,null,clean);
  }

  /** Parses given element in string by given delimiter.  <br>
      This is the same as <code>StringUtil.getParsedElement(string,element,delim,clean)</code>.
      @param string   The string to parse.
      @param element  The index of the element to get.
      @param delim    The delimiter
      @param clean    Indicates that the string should be cleaned while parsing.
      @return The element within the parsed string at the specified index or
              <code>""</code> if <code>index</code> is invalid.
      @see StringUtil#getParsedElement(String,int,char,boolean)
      @see StringUtil#getParsedElement(String,int,boolean)
  */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.parse(String, int, String, boolean)")
  public static String parse (String string, int element, String delim, boolean clean) {
    return StringUtil.parse(string, element, delim, clean); // All logic moved to StringUtil since 3.9.2
  }

  /** Parses string into ALL elements by "natural" comma or space delimiter.  <br>
      This is the same as <code>StringUtil.parseString(String,int,boolean)</code>.
      @param string   The string to parse.
      @param elements The maximum number of elements to get when parsing.
      @param clean    Indicates that the string should be cleaned while parsing.
      @return An array of the first <i><code>elements</code></i> elements in the string.
      @see StringUtil#parseString(String,boolean)
      @see StringUtil#parseString(String,int,boolean)
   */
  @LegacyMethod("use method in library Class:o nxm.sys.lib.StringUtil.parseAll(String, int, String, boolean)")
  public static String[] parseAll (String string, int elements, boolean clean) {
    return StringUtil.parseString(string,elements,clean);
  }

  /** Parses string into ALL elements by given delimiter or "natural" comma or space delimiter. <br>
      @param string   The string to parse.
      @param elements The maximum number of elements to get when parsing.
      @param delim    The delimiter character as a String
      @param clean    Indicates that the string should be cleaned while parsing.
      @return An array of the first <i><code>elements</code></i> elements in the string.
      @see StringUtil#parseString(String,boolean)
      @see StringUtil#parseString(String,int,boolean)
  */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.parseAll(String, int, String, boolean)")
  public static String[] parseAll (String string, int elements, String delim, boolean clean) {
    return StringUtil.parseAll(string, elements, delim, clean);
  }

  /** Parses string into an Args object as if parsing a command line. <br>
      This is the same as <code>Args.parseCommand(M,string)</code>.
      @param string The string to parse the arguments from.
      @param M      The midas context to use.
      @return The parsed arguments.
      @see Args#parseCommand(Midas,String)
  */
  @LegacyMethod("use method in library Class: nxm.sys.lib.Args.parseCommand(Midas, String)")
  public static Args parseArgs (String string, Midas M) {
    return Args.parseCommand(M,string);
  }

  /** Parses string to find index of word (0 to #words-1 ). <br>
      This is the same as <code>StringUtil.getParsedElementIndex(string,item,clean)</code>.
      @param string   The string to parse.
      @param item     The element to find the index of.
      @param clean    Indicates that the string should be cleaned while parsing.
      @return The index of the specified element within the parsed string or
              <code>-1</code> if such does not exist.
      @see StringUtil#getParsedElementIndex(String,String,boolean)
    */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.getParsedElementIndex(String, String, boolean)")
  public static int parsedIndex (String string, String item, boolean clean) {
    return StringUtil.getParsedElementIndex(string,item,clean);
  }

  /** Parses string to find bit mask of String args. <br>
      @param string   The string listing the items in index order.
      @param items    The string listing the enabled items.
      @return The bitmask of the enables elements.
    */
  @LegacyMethod("use method in library Class: nxm.sys.lib.Parser.mask(String, String, int)")
  public static int mask (String string, String items) {
    return Parser.mask(string,items,0);
  }

  /** Parses string to evaluate all carets in a String arg. <br>
      @param string   The string with optional caret'ed elements.
      @param args     The valid args context for the caret'ed string.
      @return The string with carets substituted.
    */
  @LegacyMethod("use method in library Class: nxm.sys.lib.Args.evaluateCarets(String)")
  public static String carets (String string, Args args) {
    return args.evaluateCarets(string);
  }

  /** Returns a CLEANed (all caps, and comma delimiter) version of string. <br>
      This is the same as <code>StringUtil.cleanString(string)</code>.
      @param string The string to clean.
      @return A cleaned version of the string.
      @see StringUtil#cleanString(String)
   */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.cleanString(String)")
  public static String clean (String string) {
    return StringUtil.cleanString(string);
  }

  /** This will substitute occurrances of a substring (<code>p1</code>) in
      a string (<code>string</code>) with a new string <code>p2</code>. <br>
      If <code>all</code> is <code>true</code>, this is the same as
      <code>StringUtil.replaceAll(string,p1,p2)</code><br>
      If <code>all</code> is <code>false</code>, this is the same as
      <code>StringUtil.replaceFirst(string,p1,p2)</code>
      @param string The to make replacements on.
      @param p1     The plain-text pattern identifying what is to be replaced.
      @param p2     The plain-text replacement for anything that matches the pattern.
      @param all    If this is <code>true</code> ALL occurrances of <code>p1</code> are
                    replaced with <code>p2</code>. If this is <code>false</code> only the
                    FIRST occurrance of <code>p1</code> are replaced with <code>p2</code>.
      @return A new version of <code>string</code> where occurrances of
              <code><i>p1</i></code> have been replaced with <code><i>p2</i></code>.
      @see StringUtil#replaceAll(String,String,String)
      @see StringUtil#replaceFirst(String,String,String)
   */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.subs(String, String, String, boolean)")
  public static String subs (String string, String p1, String p2, boolean all) {
    return StringUtil.subs(string, p1, p2, all);
  }

  /** Calculates the length of string through the Nth naturally delimited entry.
      Zero-based index.
      @param string Input string
      @param index  Entry number (0..n)
      @return The length.
   */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.length(String, int)")
  public static int length (String string, int index) {
    return StringUtil.length(string, index);
  }

  /** Calculates the length of string through the Nth naturally delimited entry.
      One-based index.
      @param string Input string
      @param index  Entry number (0..n)
      @return The length.
   */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.length(String, int) in 3.9.2")
  public static int lengthElement (String string, int index) {
    return StringUtil.lengthElement(string, index);
  }

  /** Extracts the "inclusive" substring from <code>inString</code> between <code>imin</code> and
      <code>imax</code>.  Indices are zero-based.  Negative numbers represent an index
      relative to the last character in the input string.
      @param inString Input string
      @param imin     Start index (0 to len-1) of substring (INCLUSIVE)
      @param imax     End index (0 to len-1) of substring (INCLUSIVE)
      @return The substring, if in range, or "" if imin&gt;imax or any part of the min or max is beyond the range of the input String
      @since NeXtMidas 3.1.2
   */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.getStringInRange(String, int, int)")
  public static String getStringInRange (String inString, int imin, int imax) {
    return StringUtil.getStringInRange(inString, imin, imax);

  }

  /** Extracts the "inclusive" substring from <code>string</code> between <code>p1</code> and
      <code>p2</code>.  Indices are zero-based.  Negative numbers represent relative indices.
      @param string Input string
      @param p1     Start index (0 to len-1) of substring (INCLUSIVE)
      @param p2     End index (0 to len-1) of substring (INCLUSIVE)
      @return The substring.
   */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.getStringInRange(String, int, int)")
  public static String range (String string, int p1, int p2) {
    return StringUtil.range(string, p1, p2);
  }

  /** Returns a string array which is the result of splitting the string using
      the specified delimiter. The return value will be an array of strings.
      If either the value or the delimiter is null, then the method will
      simply return null. <br>
      This is the same as <code>StringUtil.split(val,delim)</code>.
      @param val The string to split
      @param delim The string to split on
      @return A new array of strings which is the result of the split, or
              <code>null</code> if either parameter is <code>null</code>.
   */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.split(String,String)")
  public static String[] split (String val, String delim) {
    return StringUtil.split(val,delim);
  }

  /** Returns a string which is the result of joining the values using the
      specified delimiter. The return value will be a single string which is
      basically vals[0] + delim + vals[1] + delim + ... + vals[n - 1]. <br>
      This is the same as <code>StringUtils.join(vals,delim)</code>.
      @param vals The list of values that are to be joined.
      @param delim The string that will go between each value.
      @return The result of performing the join on the string, or
              <code>null</code> if <code>list</code> is <code>null</code> and
              <code>""</code> if <code>list</code> has a length of zero.
   */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.join(Object[],String)")
  public static String join (Object[] vals, String delim) {
    return StringUtil.join(vals, delim);
  }


  /** Returns a string which is the result of joining the values using the
      specified delimiter. The return value will be a single string which is
      basically vals[0] + delim + vals[1] + delim + ... + vals[n - 1]. <br>
      This is the same as <code>StringUtil.join(list.toArray(),delim)</code>.
      @param vals The list of values that are to be joined.
      @param delim The string that will go between each value.
      @return The result of performing the join on the string, or
              <code>null</code> if <code>list</code> is <code>null</code> and
              <code>""</code> if <code>list</code> has a length of zero.
   */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.join(Object[],String)")
  public static String join (Vector<?> vals, String delim) {
    return StringUtil.join(vals.toArray(),delim);
  }

  /** Removes all the leading and trailing whitespace from a string. <br>
      This is the same as <code>string.trim()</code>.
      @param string The string to trim.
      @return The trimmed string.
   */
  @LegacyMethod("use Java method: nxm.sys.lib.StringUtil.trim()")
  public static String strim (String string) {
    return string.trim();
  }


  /** Trims off string before p1 and after p2. <br>
      This is the same as <code>StringUtil.trim(string,p1,p2)</code>.
      @param string The string to trim.
      @param p1     The beginning of the portion of the string to keep.
      @param p2     The ending of the portion of the string to keep.
      @return The string with the ends trimmed off.
   */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.trim(String,String,String)")
  public static String trim (String string, String p1, String p2) {
    return StringUtil.trim(string,p1,p2);
  }

  /** Format a number.
    @param dval Number to be formation
    @param pattern Format string pattern
    @return Formatted number as a string
  */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.nform(double, String)")
  public static String nform (double dval, String pattern) {
    return StringUtil.nform(dval, pattern); // All logic moved to StringUtil since 3.9.1
  }

  /** Wrapper for padding LEFT Right or BOTH (center)
    @param string Input string
    @param newLen Width of output string
    @param dir Direction to pad PADLeft or LEFT, PADRight or RIGHT, PADBoth or BOTH
    @param padStr First character of this string is used to pad
    @return Padded string or original string if longer than desired input length
    @see nxm.sys.lib.StringUtil#padLeft
    @see nxm.sys.lib.StringUtil#padRight
    @see nxm.sys.lib.StringUtil#padBoth
  */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.pad(String, int, String, String)")
  public static String pad (String string, int newLen, String dir, String padStr) {
    return StringUtil.pad(string, newLen, dir, padStr); // All logic moved to StringUtil since 3.9.1
  }

  /** returns the path of a filename
      @param string Full file name
      @return Head or path of file name
   */
  @LegacyMethod("use method in library Class: nxm.sys.lib.FileUtil.head(String)")
  public static String head (String string) {
    return FileUtil.head(string); // All logic moved to FileUtil since 3.9.1
  }

  /** returns the filename and extension of a full file name
      @param string Full file name
      @return File name root and extension
   */
  @LegacyMethod("use method in library Class: nxm.sys.lib.FileUtil.tail(String)")
  public static String tail (String string) {
    return FileUtil.tail(string); // All logic moved to FileUtil since 3.9.1
  }

  /** returns the filename without extension of a full file name
      @param string Full file name
      @return File name without path or extension
   */
  @LegacyMethod("use method in library Class: nxm.sys.lib.FileUtil.root(String)")
  public static String root (String string) {
    return FileUtil.root(string); // All logic moved to FileUtil since 3.9.1
  }

  /** returns the filename extension
      @param string Full file name
      @return File name extension
   */
  @LegacyMethod("use method in library Class: nxm.sys.lib.FileUtil.ext(String)")
  public static String ext (String string) {
    return FileUtil.ext(string); // All logic moved to FileUtil since 3.9.1
  }

  /** Returns upper-case version of same string. <br>
      This is the same as <code>string.toUpperCase()</code>
      @param string The string to convert to upper-case.
      @return The upper-case string.
      @see String#toUpperCase()
   */
  @LegacyMethod("use Java method: java.lang.String.toUpperCase()")
  public static String upcase (String string) {
    return string.toUpperCase();
  }

  /** Returns lower-case version of same string. <br>
      This is the same as <code>string.toLowerCase()</code>
      @param string The string to convert to lower-case.
      @return The lower-case string.
      @see String#toLowerCase()
   */
  @LegacyMethod("use Java method: java.lang.String.toLowerCase()")
  public static String locase (String string) {
    return string.toLowerCase();
  }

  /** Returns number of elements in a comma delimited string. <br>
      This is the same as <code>nelements(string,',')</code>.
      @param string - Input string
      @return The number of elements (tokens) in the string.
      @see #nelements(String,String)
  */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.nelements(String)")
  public static int nelements (String string) {
    return StringUtil.nelements(string);
  }

  /** Returns number of elements in a delimited string. <br>
      @param string  Input string
      @param delim - delimiter
      @return number of elements
  */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.nelements(String, char)")
  public static int nelements (String string, char delim) {
    return StringUtil.nelements(string, delim);
  }

  /** Returns number of elements in a delimited string. <br>
      Examples:
      <pre>
        nelements(" ", ",")               --&gt; 0
        nelements(",", ",")               --&gt; 2
        nelements("a and b and c", "and") --&gt; 3
      </pre>
      @param string - Input string
      @param delim - delimiter
      @return number of elements
  */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.nelements(String, String)")
  public static int nelements (String string, String delim) {
    return StringUtil.nelements(string, delim);
  }

  /** Case sensitive search of string for substring. <br>
      This is the same as <code>string.indexOf(p1)</code>.
      @param string The string.
      @param p1     The substring to search for.
      @return The index of the FIRST occurrance of substring within the string (zero-based)
              or <code>-1</code> if the substring is not found within the string.
      @see #bsearch(String,String)
      @see String#indexOf(String)
   */
  @LegacyMethod("use Java method: java.lang.String.indexOf(String)")
  public static int search (String string, String p1) {
    return string.indexOf(p1);
  }

  /** Case sensitive search backwards of string for substring. <br>
      This is the same as <code>string.lastIndexOf(p1)</code>.
      @param string The string.
      @param p1     The substring to search for.
      @return The index of the LAST occurrance of substring within the string (zero-based)
              or <code>-1</code> if the substring is not found within the string.
      @see #search(String,String)
      @see String#lastIndexOf(String)
   */
  @LegacyMethod("use Java method: java.lang.String.lastIndexOf(String)")
  public static int bsearch (String string, String p1) {
    return string.lastIndexOf(p1);
  }

  /** Find the index of the nth instance of a substring in an input string.
      @param inString  Input string
      @param subString Sub string to find in input string
      @param n occurrence of subString to find index of
      @return The index of the nth occurrence of substring or <code>-1</code> if nth occurrence not found.
   */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.getOccurrenceOf(String, String, int)")
  public static int getOccurrenceOf (String inString, String subString, int n) {
    return StringUtil.getOccurrenceOf(inString, subString, n);
  }

  /** Find the nth instance of a substring in an input string.
      @param inString  Input string
      @param subString Sub string to find in input string
      @return The index of the substring or <code>-1</code> if not found.
   */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.selectElement(String, String)")
  public static int selectElement (String inString, String subString) {
    return StringUtil.selectElement(inString, subString);
  }

  /** Find the nth instance of a substring in an input string.
      @param inString  Input string
      @param subString Sub string to find in input string
      @return The index of the substring or <code>-1</code> if not found.
   */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.select(String, String)")
  public static int select (String inString, String subString) {
    return StringUtil.select(inString, subString);
  }

  /** Get a word from a string delimited by given character.
      @param inString    Input string
      @param elemNum     Element number (1..n)
      @param delimString The delimiter DEFAULT=comma
      @return The word.
  */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.getWordByElement(String, int, String)2")
  public static String getWordByElement (String inString, int elemNum, String delimString) {
    return StringUtil.getWordByElement(inString, elemNum, delimString);
  }

  /** Get the "inclusive" substring between imin and imax. A negative index means that many
      characters from the end of the String.<br>
      This is the same as <code>StringUtil.between(inString,imin,imax)</code>.
      @param inString  Input string
      @param imin      Index (0-n) of start of string to extract (inclusive)
      @param imax      Index (0-n) of end of string to extract (inclusive)
      @return The appropriate substring.
      @see StringUtil#between(String,int,int)
      @since NeXtMidas 3.1.2
   */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.between(String, int, int)")
  public static String getSubStringBetween (String inString, int imin, int imax) {
    return StringUtil.between(inString, imin, imax);
  }

  /** Get the substring between imin and imax of string. A negative index may be
      used to indicate a relative index. <br>
      This is the same as <code>StringUtil.substring(inString,imin,imax)</code>.
      @param inString  Inoput string
      @param imin      Index (0-n) of start of string to extract
      @param imax      Index (0-n) of end of string to extract
      @return The appropriate substring.
      @see StringUtil#substring(String,int,int)
   */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.substring(String, int, int)")
  public static String getStringBetween (String inString, int imin, int imax) {
    return StringUtil.substring(inString,imin,imax);
  }

  /** Get a word from a string containing a substring. <br>
      For example, getWordByString("This is a string","ri") returns "string"
      @param inString  Input string
      @param subString The substring for which to search
      @return Word containing sub string
  */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.getWordByString(String, String)")
  public static String getWordByString (String inString, String subString) {
    return StringUtil.getWordByString(inString, subString);
  }

  /** Strip the quotes from a string. <br>
      This is the same as <code>StringUtil.stripQuotes(string)</code>.
      @param string The string to remove the quotes from.
      @return The string after removing the quotes.
      @see StringUtil#stripQuotes(String)
   */
  @LegacyMethod("use method in library Class: nxm.sys.lib.StringUtil.stripQuotes(String)")
  public static String strip (String string) {
    return StringUtil.stripQuotes(string);
  }
}
