//
// 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;

import java.util.*;

/**
 * A special case of Multi, implemented when minChars().equals(maxChars()), and
 * some other conditions spelled out in RegOpt.safe4fm "Safe for FastMulti." It
 * avoids stack growth problems as well as being slightly faster.
 */
class FastMulti extends PatternSub
{
  patInt fewestMatches, mostMatches;

  public patInt minChars()
  {
    return sub.countMinChars().mul(fewestMatches);
  }

  public patInt maxChars()
  {
    return sub.countMaxChars().mul(mostMatches);
  }

  public boolean matchFewest = false;

  FastMulti(patInt a, patInt b, Pattern p) throws RegSyntax
  {
    if (p == null)
    {
      RegSyntaxError.endItAll("Null length pattern "
              + "followed by *, +, or other Multi.");
    }
    fewestMatches = a;
    mostMatches = b;
    sub = p;
    step = p.countMinChars().intValue();
    sub.setParent(null);
  }

  public String toString()
  {
    return sub.toString() + "{" + fewestMatches + "," + mostMatches + "}"
            + (matchFewest ? "?" : "") + "(?# <= fast multi)"
            + nextString();
  }

  int step = -1;

  public int matchInternal(int pos, Pthings pt)
  {
    int m = -1;
    int i = pos;
    int endstr = pt.src.length() - step;
    patInt matches = new patInt(0);
    if (matchFewest)
    {
      if (fewestMatches.lessEq(matches))
      {
        int ii = nextMatch(i, pt);
        if (ii >= 0)
        {
          return ii;
        }
      }
      while (i >= 0 && i <= endstr)
      {
        i = sub.matchInternal(i, pt);
        if (i >= 0)
        {
          matches.inc();
          if (fewestMatches.lessEq(matches))
          {
            int ii = nextMatch(i, pt);
            if (ii >= 0)
            {
              return ii;
            }
          }
          if (matches.equals(mostMatches))
          {
            return -1;
          }
        }
      }
      return -1;
    }
    int nMatches = 0;
    while (fewestMatches.intValue() > nMatches)
    {
      i = sub.matchInternal(i, pt);
      if (i >= 0)
      {
        nMatches++;
      }
      else
      {
        return -1;
      }
    }
    m = i;
    if (mostMatches.finite())
    {
      while (nMatches < mostMatches.intValue())
      {
        i = sub.matchInternal(i, pt);
        if (i >= 0)
        {
          m = i;
          nMatches++;
        }
        else
        {
          break;
        }
      }
    }
    else
    {
      while (true)
      {
        i = sub.matchInternal(i, pt);
        if (i >= 0)
        {
          m = i;
          nMatches++;
        }
        else
        {
          break;
        }
      }
    }
    while (m >= pos)
    {
      int r = nextMatch(m, pt);
      if (r >= 0)
      {
        return r;
      }
      m -= step;
      nMatches--;
      if (nMatches < fewestMatches.intValue())
      {
        return -1;
      }
    }
    return -1;
  }

  public Pattern clone1(Hashtable h)
  {
    try
    {
      FastMulti fm = new FastMulti(fewestMatches, mostMatches, sub.clone(h));
      fm.matchFewest = matchFewest;
      return fm;
    } catch (RegSyntax rs)
    {
      return null;
    }
  }
}
