//
// 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 is used internally to search ahead for some optimized Regex
 * objects. It searches within a String for occrences of a given String -- like
 * a more flexible version of String.indexOf.
 * 
 * @see com.stevesoft.pat.Skip2
 * @see com.stevesoft.pat.SkipBMH
 */
public class Skip
{
  static int mkmask(int c)
  {
    char x = (char) c;
    return ~(CaseMgr.toUpperCase(x) | CaseMgr.toLowerCase(x)
            | CaseMgr.toTitleCase(x));
  }

  String src;

  int c, mask;

  int offset;

  boolean ign, m1;

  /**
   * Examine a Regex to determine what String it will attempt to skip to when
   * searching for patterns. Return -1 if we aren't doing this.
   */
  public static String string(Regex r)
  {
    return r.skipper == null ? null : r.skipper.src;
  }

  /**
   * Determine the offset of the String within the pattern that we are skipping
   * to. Return -1 if we aren't doing this.
   */
  public static int offset(Regex r)
  {
    return r.skipper == null ? -1 : r.skipper.offset;
  }

  /**
   * Initialize, give it a String to search for, tell it whether or not to
   * ignoreCase, and what the offset is of the String within the String to be
   * searched.
   */
  public Skip(String s, boolean ign, int o)
  {
    src = s;
    c = s.charAt(0);
    if (ign)
    {
      mask = mkmask(c);
    }
    else
    {
      mask = 0;
    }
    offset = o;
    this.ign = ign;
    m1 = (s.length() == 1);
  }

  /** The same as find(s,0,s.length()) */
  public final int find(StringLike s)
  {
    return find(s, 0, s.length());
  }

  static final int min(int a, int b)
  {
    return a < b ? a : b;
  }

  /**
   * Searches a given region of text beginning at position start and ending at
   * position end for the skip object.
   */
  public int find(StringLike s, int start, int end)
  {
    if (start > end)
    {
      return -1;
    }
    start += offset;
    int vend = min(s.length() - 1, end + offset);
    if (mask != c)
    {
      for (int i = start; i <= vend; i++)
      {
        if (0 == (s.charAt(i) & mask))
        {
          // if(m1||s.regionMatches(ign,i,src,0,src.length()) )
          if (m1 || CaseMgr.regionMatches(s, ign, i, src, 0, src.length()))
          {
            return i - offset;
          }
        }
      }
    }
    else
    {
      for (int i = start; i <= vend; i++)
      {
        if (c == s.charAt(i))
        {
          // if(m1||s.regionMatches(ign,i,src,0,src.length()) )
          if (m1 || CaseMgr.regionMatches(s, ign, i, src, 0, src.length()))
          {
            return i - offset;
          }
        }
      }
    }
    return -1;
  }

  static Skip findSkip(Regex r)
  {
    return findSkip(r.thePattern, r.ignoreCase, !r.dontMatchInQuotes);
  }

  // look for things that can be skipped
  static Skip findSkip(Pattern p, boolean ignoreCase, boolean trnc)
  {
    StringBuffer sb = new StringBuffer();
    Skip subsk = null;
    int offset = 0;
    int skipc = -1, skipoff = 0;
    for (; p != null; p = p.next)
    {
      if (p instanceof oneChar)
      {
        skipc = ((oneChar) p).c;
        skipoff = offset;
      }
      if (p instanceof oneChar && p.next instanceof oneChar)
      {
        Pattern psav = p;
        sb.append(((oneChar) p).c);
        while (p.next instanceof oneChar)
        {
          sb.append(((oneChar) p.next).c);
          p = p.next;
        }
        String st = sb.toString();
        Skip sk = null;
        if (st.length() > 2)
        {
          sk = new SkipBMH(st, ignoreCase, offset);
        }
        else
        {
          sk = new Skip2(st, ignoreCase, offset);
        }
        if (trnc && st.length() > 2)
        { // chop out a whole string...
          psav.next = new Skipped(st.substring(1));
          psav.next.next = p.next;
          psav.next.parent = p.parent;
        }
        return sk;
      }
      else if (p instanceof Or && ((Or) p).v.size() == 1
              && !((Or) p).leftForm().equals("(?!")
              && null != (subsk = findSkip(
                      (Pattern) ((Or) p).v.elementAt(0), ignoreCase, trnc)))
      {
        subsk.offset += offset;
        return subsk;
      }
      else if (p.minChars().equals(p.maxChars()))
      {
        offset += p.minChars().intValue();
      }
      else
      {
        return skipc < 0 ? null
                : new Skip("" + (char) skipc, ignoreCase, skipoff);
      }
    }
    return null;
  }
}
