package dev.inmo.krontab.internal import com.soywiz.klock.DateTime import com.soywiz.klock.TimezoneOffset import dev.inmo.krontab.KronScheduler /** * @param daysOfWeek 0-6 * @param years any int * @param months 0-11 * @param daysOfMonth 0-30 * @param hours 0-23 * @param minutes 0-59 * @param seconds 0-59 */ internal data class CronDateTime( val daysOfWeek: Array? = null, val years: Array? = null, val months: Array? = null, val daysOfMonth: Array? = null, val hours: Array? = null, val minutes: Array? = null, val seconds: Array? = null, val milliseconds: Array? = millisecondsArrayDefault ) { init { check(daysOfWeek ?.all { it in dayOfWeekRange } ?: true) check(years?.all { it in yearRange } ?: true) check(months?.all { it in monthRange } ?: true) check(daysOfMonth ?.all { it in dayOfMonthRange } ?: true) check(hours?.all { it in hoursRange } ?: true) check(minutes?.all { it in minutesRange } ?: true) check(seconds?.all { it in secondsRange } ?: true) check(milliseconds?.all { it in millisecondsRange } ?: true) } internal val calculators = listOf( years ?.let { NearDateTimeCalculatorYears(it) }, daysOfWeek ?.let { NearDateTimeCalculatorWeekDays(it) }, milliseconds ?.let { NearDateTimeCalculatorMillis(it) }, seconds ?.let { NearDateTimeCalculatorSeconds(it) }, minutes ?.let { NearDateTimeCalculatorMinutes(it) }, hours ?.let { NearDateTimeCalculatorHours(it) }, daysOfMonth ?.let { NearDateTimeCalculatorDays(it) }, months ?.let { NearDateTimeCalculatorMonths(it) }, ) internal fun toNearDateTime(relativelyTo: DateTime = DateTime.now()): DateTime? { var current = relativelyTo whileLoop@while (true) { for (calculator in calculators) { val (calculated, requireRecalculation) = (calculator ?: continue).calculateNearTime(current) ?: return null current = calculated if (requireRecalculation) { continue@whileLoop } } return current } } } internal fun createCronDateTime( seconds: Array? = null, minutes: Array? = null, hours: Array? = null, dayOfMonth: Array? = null, month: Array? = null, years: Array? = null, weekDays: Array? = null, milliseconds: Array? = millisecondsArrayDefault ): CronDateTime { return CronDateTime(weekDays, years, month, dayOfMonth, hours, minutes, seconds, milliseconds) } /** * @return [KronScheduler] (in fact [CronDateTimeScheduler]) based on incoming data */ internal fun createKronScheduler( seconds: Array? = null, minutes: Array? = null, hours: Array? = null, dayOfMonth: Array? = null, month: Array? = null, years: Array? = null, weekDays: Array? = null, milliseconds: Array? = millisecondsArrayDefault ): KronScheduler = CronDateTimeScheduler( createCronDateTime( seconds, minutes, hours, dayOfMonth, month, years, weekDays, milliseconds ) ) /** * @return [KronScheduler] (in fact [CronDateTimeScheduler]) based on incoming data */ internal fun createKronSchedulerWithOffset( seconds: Array? = null, minutes: Array? = null, hours: Array? = null, dayOfMonth: Array? = null, month: Array? = null, years: Array? = null, weekDays: Array? = null, offset: TimezoneOffset, milliseconds: Array? = millisecondsArrayDefault ): KronScheduler = CronDateTimeSchedulerTz( createCronDateTime( seconds, minutes, hours, dayOfMonth, month, years, weekDays, milliseconds ), offset ) internal fun List.merge() = CronDateTime( flatMap { it.daysOfWeek ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() }, flatMap { it.years ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() }, flatMap { it.months ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() }, flatMap { it.daysOfMonth ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() }, flatMap { it.hours ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() }, flatMap { it.minutes ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() }, flatMap { it.seconds ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() }, flatMap { it.milliseconds ?.toList() ?: listOf(0) }.distinct().toTypedArray().takeIf { it.isNotEmpty() }, )