import { CalendarDate } from '../../../../../../helpers/calendar';
import { VegaInternalTranslation } from '../../../../../../helpers/translation/internal-translation-controller';
import { CalendarEventBaseRepeatPattern } from './base-repeat-pattern';
/** The `CalendarEventYearlyRepeatPattern` class defines a yearly repeat pattern for calendar events, checking for matches and generating future dates based on specified criteria. */
export class CalendarEventYearlyRepeatPattern extends CalendarEventBaseRepeatPattern {
  /**
   * The constructor initializes a new instance with specified start and end dates, interval, count, and until date.
   *
   * @param {CalendarDate} startDate - The `startDate` parameter is of type `CalendarDate` and
   * represents the starting date of a calendar event or interval.
   * @param {number} interval - The `interval` parameter represents the frequency at which the event
   * occurs. For example, if the interval is set to 2, the event will occur every 2 units of time (days,
   * weeks, months, etc.).
   * @param {Nullable<number>} count - The `count` parameter specifies the number of occurrences of the event. If `count`
   * is provided, the event will repeat `count` times within the specified interval. If `count` is not
   * provided, the event will repeat indefinitely based on the other parameters like `endDate` or
   * `until`.
   * @param {Nullable<CalendarDate>} until - The `until` parameter in the constructor is of type `Nullable<CalendarDate>`. This
   * means that it can either accept a `CalendarDate` object or be `null`. It is used to specify a date
   * until which the recurring event should repeat. If `until` is provided, the event
   */
  constructor(startDate, interval, count, until) {
    super(startDate, interval, count, until);
    this.matchDay = this.repeatStartDate.date;
    this.matchMonth = this.repeatStartDate.month;
    this.initCache();
  }
  /**
   * Generate repeat display string.
   *
   * @returns {string} repeat display string
   */
  generateRepeatString() {
    const monthName = VegaInternalTranslation.t(this.repeatStartDate.getMonthName());
    const repeatString = this.interval > 1
      ? VegaInternalTranslation.t('Repeat every {0} years on {1} {2}', this.interval, monthName, this.matchDay)
      : VegaInternalTranslation.t('Repeat yearly on {0} {1}', monthName, this.matchDay);
    return repeatString + this.generateRepeatEndsString();
  }
  /**
   * The function generates repeat dates and caches them until reaching the specified end date.
   *
   * @param {CalendarDate} endDate - The `endDate` parameter in the
   * `generateRepeatDateCacheUntilEndDate` function is a CalendarDate object that represents the date
   * until which the repeat date cache should be generated. The function checks if the last date in the
   * cache is before the `endDate`, and if so, it generates additional repeat dates
   */
  generateRepeatDateCacheUntilEndDate(endDate) {
    const lastMatchDate = this.cache[this.cache.length - 1];
    if (lastMatchDate.toDate() < endDate.toDate() &&
      (!this.count || (typeof this.count === 'number' && this.cache.length < this.count))) {
      let nextMatchDate = this.getNextRepeatStartDate(lastMatchDate);
      while (nextMatchDate && nextMatchDate.toDate() < endDate.toDate()) {
        const newNextMatchDate = this.getNextRepeatStartDate(nextMatchDate);
        //The below if block is used to break the infinite loop when the `getNextRepeatStartDate` method return unexpected result. such as when the date is greater than a certain value, it will be returned back 1900.
        if (newNextMatchDate && newNextMatchDate.toDate() <= nextMatchDate.toDate()) {
          this.cache.pop();
          break;
        }
        nextMatchDate = newNextMatchDate;
        if (typeof this.count === 'number' && this.cache.length >= this.count) {
          break;
        }
      }
    }
  }
  /**
   * The function getNextRepeatStartDate calculates the next repeat start date based on a given date and certain conditions.
   *
   * @param {CalendarDate} date - The `date` parameter represents the current date for which you want to
   * calculate the next repeat start date. It is of type `CalendarDate`, which seems to be a custom
   * class or structure that holds year, month, and day information.
   * @returns {Nullable<CalendarDate>} The function `getNextRepeatStartDate` returns either a `CalendarDate` object or `null`
   * based on certain conditions. If the next date exceeds the `until` date or the count limit has been
   * reached, it returns `null`. Otherwise, it adds the next date to the cache and returns it as a
   * `CalendarDate`.
   */
  getNextRepeatStartDate(date) {
    const nextDate = this.getNextStartDate(date.year + this.interval);
    const nextCalendarDate = CalendarDate.from(nextDate);
    if ((this.until && nextDate > this.until.toDate()) ||
      (typeof this.count === 'number' && this.cache.length >= this.count)) {
      return null;
    }
    else {
      this.cache.push(nextCalendarDate);
      return nextCalendarDate;
    }
  }
  /**
   * get the new date with fixed month and fixed date, if the date.getDate() not equal the fixed date or fixed month, find the next year
   *
   * @param {number} year year
   * @returns {Date} the new date
   */
  getNextStartDate(year) {
    const nextDate = new Date(year, this.matchMonth - 1, this.matchDay);
    if (nextDate.getDate() === this.matchDay && nextDate.getMonth() + 1 === this.matchMonth) {
      return nextDate;
    }
    else {
      return this.getNextStartDate(year + 1);
    }
  }
  /**
   * The function `initCache` initializes a cache array with the `startDate` value.
   */
  initCache() {
    this.cache = [this.repeatStartDate];
  }
}
