/**
   * Returns the next recurrence of this rule.
   *
   * @param startTime The time this recurrence first began.
   * @param fromTime The time to base the next recurrence on.
   * @param currentCount The total number of times the recurrence has run.
   * @return long The next recurrence as a long.
   */
  public long next(long startTime, long fromTime, long currentCount) {
    // Set up the values
    if (startTime == 0) startTime = RecurrenceUtil.now();
    if (fromTime == 0) fromTime = startTime;

    // Test the end time of the recurrence.
    if (getEndTime() != 0 && getEndTime() <= RecurrenceUtil.now()) return 0;
    Debug.logVerbose("Rule NOT expired by end time.", module);

    // Test the recurrence limit.
    if (getCount() != -1 && currentCount >= getCount()) return 0;
    Debug.logVerbose("Rule NOT expired by max count.", module);

    boolean isSeeking = true;
    long nextRuntime = 0;
    long seekTime = fromTime;
    int loopProtection = 0;
    int maxLoop = (10 * 10 * 10 * 10 * 10);

    while (isSeeking && loopProtection < maxLoop) {
      Date nextRun = getNextFreq(startTime, seekTime);
      seekTime = nextRun.getTime();
      if (validByRule(nextRun)) {
        isSeeking = false;
        nextRuntime = nextRun.getTime();
      }
      loopProtection++;
    }
    return nextRuntime;
  }
  /**
   * Gets the current recurrence (current for the checkTime) of this rule and returns it if it is
   * valid. If the current recurrence is not valid, doesn't try to find a valid one, instead returns
   * 0.
   *
   * @param startTime The time this recurrence first began.
   * @param checkTime The time to base the current recurrence on.
   * @param currentCount The total number of times the recurrence has run.
   * @return long The current recurrence as long if valid. If next recurrence is not valid, returns
   *     0.
   */
  public long validCurrent(long startTime, long checkTime, long currentCount) {
    if (startTime == 0) {
      startTime = RecurrenceUtil.now();
    }
    if (checkTime == 0) {
      checkTime = startTime;
    }

    // Test the end time of the recurrence.
    if (getEndTime() != 0 && getEndTime() <= RecurrenceUtil.now()) {
      return 0;
    }

    // Test the recurrence limit.
    if (getCount() != -1 && currentCount >= getCount()) {
      return 0;
    }

    // Get the next frequency from checkTime
    Date nextRun = getNextFreq(startTime, checkTime);
    Calendar cal = Calendar.getInstance();
    Calendar checkTimeCal = Calendar.getInstance();
    cal.setTime(nextRun);
    checkTimeCal.setTime(new Date(checkTime));

    // Get previous frequency and update its values from checkTime
    switch (getFrequency()) {
      case YEARLY:
        cal.add(Calendar.YEAR, -getIntervalInt());
        if (cal.get(Calendar.YEAR) != checkTimeCal.get(Calendar.YEAR)) {
          return 0;
        }

      case MONTHLY:
        if (MONTHLY == getFrequency()) {
          cal.add(Calendar.MONTH, -getIntervalInt());
          if (cal.get(Calendar.MONTH) != checkTimeCal.get(Calendar.MONTH)) {
            return 0;
          }
        } else {
          cal.set(Calendar.MONTH, checkTimeCal.get(Calendar.MONTH));
        }

      case WEEKLY:
        if (WEEKLY == getFrequency()) {
          cal.add(Calendar.WEEK_OF_YEAR, -getIntervalInt());
          if (cal.get(Calendar.WEEK_OF_YEAR) != checkTimeCal.get(Calendar.WEEK_OF_YEAR)) {
            return 0;
          }
        } else {
          cal.set(Calendar.WEEK_OF_YEAR, checkTimeCal.get(Calendar.WEEK_OF_YEAR));
        }

      case DAILY:
        if (DAILY == getFrequency()) {
          cal.add(Calendar.DAY_OF_MONTH, -getIntervalInt());
          if (cal.get(Calendar.DAY_OF_MONTH) != checkTimeCal.get(Calendar.DAY_OF_MONTH)) {
            return 0;
          }
        } else {
          cal.set(Calendar.DAY_OF_MONTH, checkTimeCal.get(Calendar.DAY_OF_MONTH));
        }

      case HOURLY:
        if (HOURLY == getFrequency()) {
          cal.add(Calendar.HOUR_OF_DAY, -getIntervalInt());
          if (cal.get(Calendar.HOUR_OF_DAY) != checkTimeCal.get(Calendar.HOUR_OF_DAY)) {
            return 0;
          }
        } else {
          cal.set(Calendar.HOUR_OF_DAY, checkTimeCal.get(Calendar.HOUR_OF_DAY));
        }

      case MINUTELY:
        if (MINUTELY == getFrequency()) {
          cal.add(Calendar.MINUTE, -getIntervalInt());
          if (cal.get(Calendar.MINUTE) != checkTimeCal.get(Calendar.MINUTE)) {
            return 0;
          }
        } else {
          cal.set(Calendar.MINUTE, checkTimeCal.get(Calendar.MINUTE));
        }

      case SECONDLY:
        if (SECONDLY == getFrequency()) {
          cal.add(Calendar.SECOND, -getIntervalInt());
          if (cal.get(Calendar.SECOND) != checkTimeCal.get(Calendar.SECOND)) {
            return 0;
          }
        } else {
          cal.set(Calendar.SECOND, checkTimeCal.get(Calendar.SECOND));
        }
    }

    // Check for validity of the current frequency.
    if (validByRule(cal.getTime())) {
      return cal.getTime().getTime();
    }

    return 0;
  }