/*
 * Licensed to The OpenNMS Group, Inc (TOG) under one or more
 * contributor license agreements.  See the LICENSE.md file
 * distributed with this work for additional information
 * regarding copyright ownership.
 *
 * TOG licenses this file to You under the GNU Affero General
 * Public License Version 3 (the "License") or (at your option)
 * any later version.  You may not use this file except in
 * compliance with the License.  You may obtain a copy of the
 * License at:
 *
 *      https://www.gnu.org/licenses/agpl-3.0.txt
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied.  See the License for the specific
 * language governing permissions and limitations under the
 * License.
 */
import Types from '../Types';
import Time from '../Time';
import Range from '../Range';
import CronDefinition from '../CronDefintion';
import WeekdayOptions from '../WeekdayOptions';
import ScheduleOptions from '../ScheduleOptions';
import Intervals from '../Intervals';

/**
 * The daily parser will parse any cron expression,
 * which was generated by the Schedule Editor in "Daily" or "Days per Week" Mode.
 */
export default class DailyParser {

    canParse(input) {
        const cron = CronDefinition.createFrom(input);
        const weekdayOptions = WeekdayOptions.createFrom(cron.dayOfWeek);
        const canParse = cron.year === undefined
            && cron.seconds === '0'
            && (cron.isConcreteMinutes() || (cron.isMinutesInterval() && cron.minutes.indexOf('0') === 0))
            && (cron.isConcreteHours() || cron.isHoursInterval() || cron.isHoursRange())
            && (cron.month === '*')
            && ((cron.dayOfMonth === '*' && cron.dayOfWeek === '?')
                    || (cron.dayOfMonth === '?' && weekdayOptions.getSelectedWeekdays().length > 0));
        if (canParse) {
            if (cron.isMinutesInterval()) {
                const minuteIntervals = Intervals.Minutes;
                return minuteIntervals.indexOf(cron.interval) >= 0;
            }
            if (cron.isHoursInterval()) {
                const hourIntervals = Intervals.Hours;
                return hourIntervals.indexOf(cron.interval) >= 0;
            }
            return true;
        }
        return false;
    }

    parse(input) {
        const cron = CronDefinition.createFrom(input);
        const options = new ScheduleOptions({ type: Types.DAILY });
        options.interval = cron.interval;

        // Parse at, from and to
        if (options.interval === '0') {
            options.at = new Time({ hours: cron.hours, minutes: cron.minutes });
        } else {
            const range = new Range(cron.hours);

            // add one hour offset in case of minutes interval and hours are range
            if ((options.interval % 60 !== 0) && cron.isHoursRange()) {
                // When generating the cron, the to hour was decreased by one,
                // we now revert it by increasing it by 1
                range.end = parseInt(range.end, 10) + 1;
            }
            options.from = new Time({hours: range.start, minutes: 0});
            options.to = new Time({hours: range.end, minutes: 0});
        }

        // If days of week are set, switch type and apply daysOfWeek accordingly
        const weekdayOptions = WeekdayOptions.createFrom(cron.dayOfWeek);
        if (weekdayOptions.getSelectedWeekdays().length > 0) {
            options.type = Types.DAYS_PER_WEEK;
            options.daysOfWeek = weekdayOptions;
        }
        return options;
    }
}
