//
// This software is now distributed according to
// the Lesser Gnu Public License.  Please see
// http://www.gnu.org/copyleft/lesser.txt for
// the details.
//    -- Happy Computing!
//
package com.stevesoft.pat;

/**
 * This class provides a method for parsing the "s/.../.../" constructs of
 * Regex.perlCode.
 * 
 * @see Regex#perlCode
 */
class parsePerl
{
  final static char close(char c)
  {
    // This switch statement does not behave
    // properly when compiled with jdk1.1.5
    // and the -O flag.
    /*
     * switch(c) { case '[': return ']'; case '(': return ')'; case '{': return
     * '}'; } return c;
     */
    if (c == '<')
    {
      return '>';
    }
    if (c == '[')
    {
      return ']';
    }
    if (c == '(')
    {
      return ')';
    }
    if (c == '{')
    {
      return '}';
    }
    return c;
  }

  final public static String codify(String s, boolean keepbs)
  {
    return codify(s, 0, s.length(), keepbs);
  }

  final public static String codify(String s, int i0, int iN, boolean keepbs)
  {
    StringBuffer sb = new StringBuffer();
    boolean ucmode = false, lcmode = false, litmode = false;
    boolean uc1 = false, lc1 = false;
    boolean modified = false;
    for (int i = i0; i < iN; i++)
    {
      char c = s.charAt(i);
      boolean mf = true, app = true;
      if (c == '\\')
      {
        app = false;
        i++;
        if (i < s.length())
        {
          char c2 = s.charAt(i);
          switch (c2)
          {
          case 'Q':
            litmode = true;
            break;
          case 'U':
            ucmode = true;
            break;
          case 'L':
            lcmode = true;
            break;
          case 'u':
            uc1 = true;
            break;
          case 'l':
            lc1 = true;
            break;
          case 'E':
            uc1 = lc1 = ucmode = lcmode = litmode = false;
            break;
          default:
            if (keepbs)
            {
              sb.append('\\');
            }
            c = c2;
            if (keepbs)
            {
              mf = false;
            }
            app = true;
            break;
          }
          modified |= mf;
        }
      }
      if (app)
      {
        if (lc1)
        {
          c = lc(c);
          lc1 = false;
        }
        else if (uc1)
        {
          c = uc(c);
          uc1 = false;
        }
        else if (ucmode)
        {
          c = uc(c);
        }
        else if (lcmode)
        {
          c = lc(c);
        }
        if (litmode && needbs(c))
        {
          sb.append('\\');
        }
        sb.append(c);
      }
    }
    return modified ? sb.toString() : s;
  }

  final static char uc(char c)
  {
    return CaseMgr.toUpperCase(c);
  }

  final static char lc(char c)
  {
    return CaseMgr.toLowerCase(c);
  }

  final static boolean needbs(char c)
  {
    if (c >= 'a' && c <= 'z')
    {
      return false;
    }
    if (c >= 'A' && c <= 'Z')
    {
      return false;
    }
    if (c >= '0' && c <= '9')
    {
      return false;
    }
    if (c == '_')
    {
      return false;
    }
    return true;
  }

  final static Regex parse(String s)
  {
    boolean igncase = false, optim = false, gFlag = false;
    boolean sFlag = false, mFlag = false, xFlag = false;

    StringBuffer s1 = new StringBuffer();
    StringBuffer s2 = new StringBuffer();
    int i = 0, count = 0;
    char mode, delim = '/', cdelim = '/';
    if (s.length() >= 3 && s.charAt(0) == 's')
    {
      mode = 's';
      delim = s.charAt(1);
      cdelim = close(delim);
      i = 2;
    }
    else if (s.length() >= 2 && s.charAt(0) == 'm')
    {
      mode = 'm';
      delim = s.charAt(1);
      cdelim = close(delim);
      i = 2;
    }
    else if (s.length() >= 1 && s.charAt(0) == '/')
    {
      mode = 'm';
      i = 1;
    }
    else
    {
      try
      {
        RegSyntaxError.endItAll("Regex.perlCode should be of the "
                + "form s/// or m// or //");
      } catch (RegSyntax rs)
      {
      }
      return null;
    }
    for (; i < s.length(); i++)
    {
      if (s.charAt(i) == '\\')
      {
        s1.append('\\');
        i++;
      }
      else if (s.charAt(i) == cdelim && count == 0)
      {
        i++;
        break;
      }
      else if (s.charAt(i) == delim && cdelim != delim)
      {
        count++;
      }
      else if (s.charAt(i) == cdelim && cdelim != delim)
      {
        count--;
      }
      s1.append(s.charAt(i));
    }
    if (mode == 's' && cdelim != delim)
    {
      while (i < s.length() && Prop.isWhite(s.charAt(i)))
      {
        i++;
      }
      if (i >= s.length())
      {
        try
        {
          RegSyntaxError.endItAll("" + mode + delim + " needs " + cdelim);
        } catch (RegSyntax rs)
        {
        }
        return null;
      }
      cdelim = close(delim = s.charAt(i));
      i++;
    }
    count = 0;
    if (mode == 's')
    {
      for (; i < s.length(); i++)
      {
        if (s.charAt(i) == '\\')
        {
          s2.append('\\');
          i++;
        }
        else if (s.charAt(i) == cdelim && count == 0)
        {
          i++;
          break;
        }
        else if (s.charAt(i) == delim && cdelim != delim)
        {
          count++;
        }
        else if (s.charAt(i) == cdelim && cdelim != delim)
        {
          count--;
        }
        s2.append(s.charAt(i));
      }
    }
    for (; i < s.length(); i++)
    {
      char c = s.charAt(i);
      switch (c)
      {
      case 'x':
        xFlag = true;
        break;
      case 'i':
        igncase = true;
        break;
      case 'o':
        optim = true;
        break;
      case 's':
        sFlag = true;
        break;
      case 'm':
        mFlag = true;
        break;
      case 'g':
        gFlag = true;
        break;
      default:

        // syntax error!
        try
        {
          RegSyntaxError.endItAll("Illegal flag to pattern: " + c);
        } catch (RegSyntax rs)
        {
        }
        return null;
      }
    }
    Regex r = new Regex();
    try
    {
      String pat = s1.toString(), reprul = s2.toString();
      if (xFlag)
      {
        pat = strip(pat);
        reprul = strip(reprul);
      }
      r.compile(pat);
      r.ignoreCase |= igncase;
      r.gFlag |= gFlag;
      r.sFlag |= sFlag;
      r.mFlag |= mFlag;
      if (optim)
      {
        r.optimize();
      }
      if (delim == '\'')
      {
        r.setReplaceRule(new StringRule(reprul));
      }
      else
      {
        r.setReplaceRule(ReplaceRule.perlCode(reprul));
      }
    } catch (RegSyntax rs)
    {
      r = null;
    }
    return r;
  }

  static String strip(String s)
  {
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < s.length(); i++)
    {
      char c = s.charAt(i);
      if (Prop.isWhite(c))
      {
        ;
      }
      else if (c == '#')
      {
        i++;
        while (i < s.length())
        {
          if (s.charAt(i) == '\n')
          {
            break;
          }
          i++;
        }
      }
      else if (c == '\\')
      {
        sb.append(c);
        sb.append(s.charAt(++i));
      }
      else
      {
        sb.append(c);
      }
    }
    return sb.toString();
  }
}
