use std::collections::{HashMap, hash_set::Iter};
use time::{Month, OffsetDateTime};
use crate::calendar::{Calendar, CalendarDay};
use crate::error::Error;
use crate::{Recurrences};
use crate::utility;
pub enum EventRecurrences<'a>{
Multiple(&'a HashMap<Month, Recurrences>),
Single(&'a Recurrences),
}
pub fn check_the_from_and_frequenced_periods(check_period: fn(OffsetDateTime, recurrences: &EventRecurrences) -> Result<Option<OffsetDateTime>, Error>, from_period: OffsetDateTime, frequenced_period: OffsetDateTime, event_recurrences: &EventRecurrences) -> Result<Option<OffsetDateTime>, Error> {
let next_recurrence_date_ = check_period(from_period, event_recurrences);
if next_recurrence_date_.is_ok() && next_recurrence_date_.as_ref().unwrap().is_none() {
check_period(frequenced_period, event_recurrences)
} else {
next_recurrence_date_
}
}
pub fn check_month(from_date: OffsetDateTime, event_recurrences: &EventRecurrences) -> Result<Option<OffsetDateTime>, Error> {
let recurrences = match event_recurrences {
EventRecurrences::Multiple(_) => Err(Error::UnexpectedValue)?,
EventRecurrences::Single(recurrences) => recurrences,
};
let mut next_recurrence_dates = Vec::new();
let to_date = utility::first_date_of_next_month(&from_date)?;
let mut calendar = Calendar::new();
calendar.create_days(from_date, to_date)?;
let next_recurrence_date_ = match_recurrences(recurrences, Recurrences::weekdays, &calendar, CalendarDay::weekdays)?;
if next_recurrence_date_.is_some() {
next_recurrence_dates.push(next_recurrence_date_.unwrap());
};
let next_recurrence_date_ = match_recurrences(recurrences, Recurrences::monthly_days_ordinals, &calendar, CalendarDay::monthly_days_ordinals)?;
if next_recurrence_date_.is_some() {
next_recurrence_dates.push(next_recurrence_date_.unwrap());
};
let next_recurrence_date_ = match_recurrences(recurrences, Recurrences::monthly_weekdays_ordinals, &calendar, CalendarDay::monthly_weekdays_ordinals)?;
if next_recurrence_date_.is_some() {
next_recurrence_dates.push(next_recurrence_date_.unwrap());
};
if next_recurrence_dates.is_empty() {
Ok(None)
} else {
Ok(next_recurrence_dates.iter().min().cloned())
}
}
pub fn check_week(from_date: OffsetDateTime, event_recurrences: &EventRecurrences) -> Result<Option<OffsetDateTime>, Error> {
let recurrences = match event_recurrences {
EventRecurrences::Multiple(_) => Err(Error::UnexpectedValue)?,
EventRecurrences::Single(recurrences) => recurrences,
};
let to_date = utility::first_date_of_next_week(&from_date)?;
let mut calendar = Calendar::new();
calendar.create_days(from_date, to_date)?;
match_recurrences(recurrences, Recurrences::weekdays, &calendar, CalendarDay::weekdays)
}
pub fn check_year(from_date: OffsetDateTime, event_recurrences: &EventRecurrences) -> Result<Option<OffsetDateTime>, Error> {
let recurrences_by_month = match event_recurrences {
EventRecurrences::Multiple(recurrences) => recurrences,
EventRecurrences::Single(_) => Err(Error::UnexpectedValue)?,
};
let mut month = from_date.month();
let mut first_pass = true;
while first_pass || month != Month::January {
let recurrences_ = recurrences_by_month.get(&month);
if recurrences_.is_some() {
let start_date = if month == from_date.month() {
from_date
} else {
from_date.replace_day(1)?.replace_month(month)?
};
let next_recurrence_date_ = check_month(start_date, &EventRecurrences::Single(&recurrences_.unwrap()));
if next_recurrence_date_.is_ok() && next_recurrence_date_.as_ref().unwrap().is_some() {
return next_recurrence_date_;
};
};
month = month.next();
first_pass = false;
};
Ok(None)
}
fn match_recurrences<U: std::cmp::PartialEq>(recurrences: &Recurrences, event_recurrences_kind: fn(& Recurrences) -> Iter<U>, calendar: &Calendar, calendar_recurrence_kind: fn(& CalendarDay) -> &[U]) -> Result<Option<OffsetDateTime>, Error> {
for day in calendar.days() {
for event_recurrence_kind in event_recurrences_kind(recurrences) {
if calendar_recurrence_kind(&day).contains(event_recurrence_kind) {
return Ok(Some(day.date()))
};
};
};
Ok(None)
}