use std::{
collections::{BTreeMap, BTreeSet, HashMap, HashSet},
sync::{Arc, OnceLock},
thread::sleep,
time::Duration,
};
use activities::activities;
use eframe::{egui::mutex::RwLock, glow::MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS};
use rand::{seq::SliceRandom, thread_rng};
use serde::{Deserialize, Serialize};
use super::{
activities::{self, partnered_act, selected_act, Activity},
classes::{get_classes, validates_c_lims, validates_t_lims, Class},
generate::{
continued, create_total_hours, generating, multi_thread, recalc_sched, total_hours,
},
get_school,
limitations::limitations_ui::{
classes_sched, create_classes_sched, create_teachers_sched, get_class_limitations,
get_teacher_limitations, schedules, teachers_limitations, teachers_scheds, update_tt,
ClassLimitation, TeacherLimitation,
},
school_hour,
teachers::{get_teachers, Teacher},
};
pub fn act_total_teachers() -> &'static Arc<RwLock<HashMap<i32, Vec<i32>>>> {
static STOP: OnceLock<Arc<RwLock<HashMap<i32, Vec<i32>>>>> = OnceLock::new();
STOP.get_or_init(|| Arc::new(RwLock::new(create_act_total_teachers())))
}
fn create_act_total_teachers() -> HashMap<i32, Vec<i32>> {
let acts = activities().read().clone();
let mut map = HashMap::new();
for act in &acts {
let mut v = act.1.teachers.clone();
for a in &act.1.partners {
if let Some(a) = acts.get(&a) {
v.append(&mut a.teachers.clone())
};
}
map.insert(act.1.id, v);
}
map
}
pub fn act_total_classes() -> &'static Arc<RwLock<HashMap<i32, Vec<i32>>>> {
static STOP: OnceLock<Arc<RwLock<HashMap<i32, Vec<i32>>>>> = OnceLock::new();
STOP.get_or_init(|| Arc::new(RwLock::new(create_act_total_classes())))
}
pub fn available_days() -> &'static Arc<RwLock<HashMap<i32, Vec<(usize, Vec<usize>)>>>> {
static STOP: OnceLock<Arc<RwLock<HashMap<i32, Vec<(usize, Vec<usize>)>>>>> = OnceLock::new();
STOP.get_or_init(|| Arc::new(RwLock::new(create_available_days())))
}
pub fn conflict_acts() -> &'static Arc<RwLock<HashSet<i32>>> {
static STOP: OnceLock<Arc<RwLock<HashSet<i32>>>> = OnceLock::new();
STOP.get_or_init(|| Arc::new(RwLock::new(HashSet::new())))
}
pub fn create_available_days() -> HashMap<i32, Vec<(usize, Vec<usize>)>> {
let acts = activities().read();
let mut map = HashMap::new();
let hour = get_school().read().hour;
for a in &*acts {
let mut days = vec![];
for i in 0..7 {
let mut hours = vec![];
for h in (0..hour).rev() {
if a.1.teachers_available(h, i) && a.1.classes_available(h, i) {
hours.push(h);
}
}
if !hours.is_empty() {
days.push((i, hours));
}
}
map.insert(*a.0, days);
}
map
}
fn create_act_total_classes() -> HashMap<i32, Vec<i32>> {
let acts = activities().read().clone();
let mut map = HashMap::new();
for act in &acts {
let mut v = act.1.classes.clone();
for a in &act.1.partners {
if let Some(a) = acts.get(&a) {
v.append(&mut a.classes.clone())
};
}
map.insert(act.1.id, v);
}
map
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(crate = "serde")]
pub struct Schedule {
pub day_id: usize,
pub hour: usize,
pub activity: i32,
pub locked: bool,
}
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
#[serde(crate = "serde")]
pub struct Params {
pub hour: usize,
pub depth: usize,
pub depth2: usize,
}
#[derive(Clone, Serialize, Deserialize)]
#[serde(crate = "serde")]
pub struct TimetableData {
pub tat: HashMap<i32, TeacherLimitation>,
pub cat: HashMap<i32, ClassLimitation>,
pub clean_tat: HashMap<i32, TeacherLimitation>,
pub clean_cat: HashMap<i32, ClassLimitation>,
pub acts: Vec<Activity>,
pub teachers_acts: HashMap<i32, Vec<Activity>>,
pub neighbour_acts: HashMap<i32, HashSet<i32>>,
pub classes: BTreeMap<i32, Class>,
pub teachers: BTreeMap<i32, Teacher>,
pub timetables: HashMap<i32, Vec<Schedule>>,
pub school_hour: usize,
pub not_placed_acts: HashSet<i32>,
}
impl TimetableData {
pub fn generate(&mut self, params: &mut Params) -> bool {
let mut act = self.not_placed_acts.iter();
let Some(act) = act.next() else {
return true;
};
let acts = activities().read();
let Some(act) = acts.get(&act) else {
return false;
};
*selected_act().write() = act.id;
let available = self.find_timeslot(act, params);
match available {
Some(slots) => {
self.put_act(slots.0, slots.1, act, false);
return true;
}
None => {
let mut s_c = self.clone();
conflict_acts().write().clear();
if s_c.recursive_put(act, 0, &act, params) {
*self = s_c;
return true;
}
self.not_placed_acts = HashSet::from_iter(
self.acts
.clone()
.into_iter()
.filter(|a| !a.teachers.is_empty() && self.timetables.get(&a.id).is_none())
.map(|a| a.id),
);
let conflict_acts = self.find_conflict_activity(act, &act, params);
if conflict_acts.is_empty() {
return false;
}
for a in &conflict_acts[0] {
self.delete_activity(a);
}
if let Some(slots) = self.find_timeslot(act, params) {
self.put_act(slots.0, slots.1, act, false);
}
return true;
}
}
}
pub fn replace_act(&mut self, day: usize, hour: usize, act_id: i32, act_hour: usize) {
let tt = &mut self.timetables;
let scheds = &*schedules().write().clone();
let ng = self.neighbour_acts.get(&act_id);
if let Some(ng) = ng {
let acts = scheds
.iter()
.filter(|s| s.day_id == day && ng.iter().any(|a| a == &s.activity))
.collect::<Vec<&Schedule>>();
println!("{acts:?}");
for h in hour..hour + act_hour {
if let Some(s) = acts.iter().find(|a| a.hour == h) {
tt.remove(&s.activity);
};
}
update_tt();
}
}
pub fn find_timeslot(&self, act: &Activity, params: &Params) -> Option<(usize, usize)> {
let days = available_days().read().clone();
let days = days.get(&act.id).unwrap();
if self.tat.len() == 0 {
validates_c_lims();
validates_t_lims();
return None;
}
for day in days {
for hour in &day.1 {
if self.teachers_available(act, *hour, day.0)
&& self.classes_available(act, *hour, day.0)
&& (
act.no_limit || self.same_day_available(act, *hour, day.0, params)
)
{
return Some((day.0, *hour));
}
}
}
None
}
fn same_day_available(&self, act: &Activity, hour: usize, day: usize, params: &Params) -> bool {
if let Some(teacher_acts) = self.teachers_acts.get(&act.id) {
let mut same_day_acts = vec![];
for a in teacher_acts {
if !a.no_limit {
if let Some(s) = self.timetables.get(&a.id) {
same_day_acts.append(&mut s.clone());
}
}
}
let same_day_acts: Vec<&Schedule> =
same_day_acts.iter().filter(|t| t.day_id == day).collect();
if same_day_acts.len() == 0 {
return true;
} else if (act.hour + same_day_acts.len()) > params.hour {
return false;
} else {
let hours = same_day_acts
.iter()
.cloned()
.find(|t| hour > 0 && t.hour == (hour - 1) || t.hour == (hour + act.hour));
if hours.is_some() {
return true;
}
return false;
}
}
true
}
fn classes_available(&self, act: &Activity, hour: usize, day: usize) -> bool {
let mut classes_availables = vec![];
let act_classes = act_total_classes().read();
let Some(classes) = act_classes.get(&act.id) else {
return false;
};
for class in classes {
let class = self.cat.get(class);
if let Some(c) = class {
classes_availables.push(c.limitations[day].clone());
}
}
(hour..hour + act.hour as usize).all(|h| classes_availables.iter().all(|ca| ca[h]))
}
fn teachers_available(&self, act: &Activity, hour: usize, day: usize) -> bool {
let mut teachers_availables = vec![];
let act_teachers = act_total_teachers().read();
let Some(teachers) = act_teachers.get(&act.id) else {
return false;
};
for teacher in teachers {
let teacher = self.tat.get(teacher);
if let Some(t) = teacher {
teachers_availables.push(t.limitations[day].clone());
}
}
(hour..hour + act.hour as usize).all(|h| teachers_availables.iter().all(|ta| ta[h]))
}
pub fn put_act(&mut self, day: usize, hour: usize, act: &Activity, lock: bool) {
for timetable in hour..hour + act.hour as usize {
let tt = Schedule {
day_id: day,
hour: timetable,
activity: act.id,
locked: lock,
};
let act_teachers = act_total_teachers().read();
if let Some(teachers) = act_teachers.get(&act.id) {
for teacher in teachers {
if let Some(tat) = self.tat.get_mut(teacher) {
if let Some(tat_index) = tat.limitations.get_mut(tt.day_id) {
tat_index[tt.hour as usize] = false;
}
}
}
}
let act_classes = act_total_classes().read();
if let Some(classes) = act_classes.get(&act.id) {
for class in classes {
if let Some(cat) = self.cat.get_mut(class) {
if let Some(cat_index) = cat.limitations.get_mut(tt.day_id) {
cat_index[tt.hour] = false;
}
}
}
}
if let Some(timetables) = self.timetables.get_mut(&act.id) {
timetables.push(tt);
} else {
self.timetables.insert(act.id, vec![tt.clone()]);
}
}
self.not_placed_acts.remove(&act.id);
}
pub fn delete_activity(&mut self, act: &Activity) {
let tt = self.timetables.get(&act.id).unwrap();
for t in tt {
let act_teachers = act_total_teachers().read();
if let Some(teachers) = act_teachers.get(&act.id) {
for teacher in teachers {
if let Some(tat) = self.tat.get_mut(teacher) {
if let Some(tat_index) = tat.limitations.get_mut(t.day_id) {
tat_index[t.hour as usize] = true;
}
}
}
}
let act_classes = act_total_classes().read();
if let Some(classes) = act_classes.get(&act.id) {
for class in classes {
if let Some(cat) = self.cat.get_mut(class) {
if let Some(cat_index) = cat.limitations.get_mut(t.day_id) {
cat_index[t.hour as usize] = true;
}
}
}
}
}
self.not_placed_acts.insert(act.id);
self.timetables.remove(&act.id);
}
fn find_conflict_activity(
&self,
act: &Activity,
ignores: &Activity,
params: &Params,
) -> Vec<Vec<Activity>> {
let mut total_act: Vec<Vec<Activity>> = Vec::new();
let acts = self.neighbour_acts.get(&act.id).unwrap();
let days = &*available_days().read();
let days = days.get(&act.id).unwrap();
for day in days {
for h in &day.1 {
let mut less_conflict: HashSet<Activity> = HashSet::new();
for i in *h..*h + act.hour as usize {
for a in acts {
if let Some(s) = self.timetables.get(&a) {
let sa = s.iter().find(|t| {
t.day_id == day.0 && t.hour as usize == i && !t.locked
});
if let Some(s) = sa {
if let Some(a) = activities().read().get(&s.activity) {
less_conflict.insert(a.clone());
};
};
}
}
}
if !less_conflict.is_empty() {
total_act.push(Vec::from_iter(less_conflict));
}
}
}
total_act.shuffle(&mut thread_rng());
if total_act.len() >= params.depth {
return total_act[..params.depth].to_vec();
}
total_act
}
pub(crate) fn recursive_put(
&mut self,
act: &Activity,
depth: usize,
ignores: &Activity,
params: &Params,
) -> bool {
let mut conflict_acts = self.find_conflict_activity(act, ignores, params);
let mut okey2 = false;
let tat2 = self.tat.clone();
let not_placed_acts = self.not_placed_acts.clone();
let cat2 = self.cat.clone();
let timetables2 = self.timetables.clone();
for c_act in &mut conflict_acts {
for a in &*c_act {
self.delete_activity(a);
}
c_act.sort_by(|a, b| a.hour.cmp(&b.hour));
c_act.push(act.clone());
let mut okey = true;
for a in c_act.iter().rev() {
let available = self.find_timeslot(a, params);
match available {
Some(slots) => {
self.put_act(slots.0, slots.1, a, false);
}
None => {
if depth < params.depth2 {
let rec_result = self.recursive_put(a, depth + 1, ignores, params);
if !rec_result {
okey = false;
break;
}
} else {
okey = false;
break;
}
}
}
}
if okey {
okey2 = true;
break;
} else {
self.tat = tat2.to_owned();
self.cat = cat2.to_owned();
self.timetables = timetables2.to_owned();
self.not_placed_acts = not_placed_acts.to_owned();
}
}
okey2
}
fn validate_tt(&mut self) {
self.clean_cat = get_class_limitations();
self.clean_tat = get_teacher_limitations();
self.tat = self.clean_tat.clone();
self.cat = self.clean_cat.clone();
let tt_c = self.timetables.clone();
self.timetables.clear();
schedules().write().clear();
teachers_scheds().write().clear();
classes_sched().write().clear();
self.not_placed_acts = self.acts.iter().map(|a| a.id.clone()).collect();
for tt in &tt_c {
let act = self
.acts
.clone()
.into_iter()
.find(|a| a.id == *tt.0)
.unwrap();
if self.teachers_available(&act, tt.1[0].hour as usize, tt.1[0].day_id)
&& self.classes_available(&act, tt.1[0].hour as usize, tt.1[0].day_id)
&& (act.no_limit
|| self.same_day_available(
&act,
tt.1[0].hour as usize,
tt.1[0].day_id,
&Params {
hour: 2,
depth: 0,
depth2: 0,
},
))
{
self.put_act(tt.1[0].day_id, tt.1[0].hour as usize, &act, tt.1[0].locked);
} else {
}
}
let mut sch = vec![];
self.timetables
.values()
.for_each(|tt| sch.append(&mut tt.clone()));
*schedules().write() = sch.clone();
*teachers_scheds().write() = create_teachers_sched();
*classes_sched().write() = create_classes_sched();
*data().write() = self.clone();
}
}
pub fn create_data() -> TimetableData {
let clean_tat = get_teacher_limitations();
let tat = get_teacher_limitations();
let clean_cat = get_class_limitations();
let cat = get_class_limitations();
let mut acts = (*activities().read())
.clone()
.values()
.filter(|a| partnered_act().read().get(&a.id).is_none())
.map(|a| a.clone())
.collect::<Vec<Activity>>();
acts.shuffle(&mut thread_rng());
acts.sort_by(|a, b| a.hour.cmp(&b.hour));
let not_placed_acts = acts.iter().map(|a| a.id).collect();
let hour = &*school_hour().read();
let h = hour.parse::<usize>().unwrap_or(8);
let timetables = HashMap::new();
let dt = TimetableData {
tat,
cat,
clean_cat,
clean_tat,
acts,
teachers_acts: create_teachers_acts(),
neighbour_acts: create_ng(),
classes: get_classes().read().clone(),
teachers: (*get_teachers().read()).clone(),
timetables, school_hour: h,
not_placed_acts,
};
dt
}
fn create_teachers_acts() -> HashMap<i32, Vec<Activity>> {
let activities = (*activities().read()).clone();
let acts: Vec<Activity> = activities.clone().values().map(|a| a.clone()).collect();
let mut ts_acts = HashMap::new();
for act in &activities {
let acts: Vec<Activity> = acts
.iter()
.cloned()
.filter(
|a| {
act.1
.teachers
.iter()
.all(|t| a.teachers.iter().any(|t2| t2 == t))
&& act
.1
.classes
.iter()
.all(|c| a.classes.iter().any(|c2| c2 == c))
}, )
.collect();
ts_acts.insert(act.1.id, acts);
}
ts_acts
}
pub fn data() -> &'static Arc<RwLock<TimetableData>> {
static DATA: OnceLock<Arc<RwLock<TimetableData>>> = OnceLock::new();
DATA.get_or_init(|| Arc::new(RwLock::new(create_data())))
}
pub fn generate() {
let mut params = Params {
hour: 2,
depth: 6,
depth2: 6,
};
std::thread::spawn(move || {
let mut t_data = data().read().clone();
t_data.validate_tt();
t_data.timetables = recalc_sched();
*total_hours().write() = create_total_hours();
let mut sch = vec![];
t_data
.timetables
.values()
.for_each(|tt| sch.append(&mut tt.clone()));
*schedules().write() = sch.clone();
*teachers_scheds().write() = create_teachers_sched();
*classes_sched().write() = create_classes_sched();
loop {
if !*generating().read() {
break;
}
if t_data.not_placed_acts.is_empty() {
let mut sch = vec![];
t_data
.timetables
.values()
.for_each(|tt| sch.append(&mut tt.clone()));
*schedules().write() = sch.clone();
*teachers_scheds().write() = create_teachers_sched();
*classes_sched().write() = create_classes_sched();
*generating().write() = false;
*continued().write() = false;
break;
}
if t_data.generate(&mut params) {
if t_data.timetables.len() >= data().read().timetables.len() {
*data().write() = t_data.clone();
} else {
t_data = data().read().clone();
}
let mut sch = vec![];
t_data
.timetables
.values()
.for_each(|tt| sch.append(&mut tt.clone()));
*schedules().write() = sch.clone();
*teachers_scheds().write() = create_teachers_sched();
*classes_sched().write() = create_classes_sched();
} else {
*generating().write() = false;
*continued().write() = false;
break;
}
}
});
}
fn create_ng() -> HashMap<i32, HashSet<i32>> {
let acts = &*activities()
.read()
.values()
.map(|a| a.clone())
.collect::<Vec<Activity>>();
let mut neighbours: HashMap<i32, HashSet<i32>> = HashMap::new();
for a in &*acts {
let ns = &*acts
.into_iter()
.filter(|a2| {
a2.id != a.id
&& (a2
.classes
.iter()
.any(|c| a.classes.iter().any(|c2| c2 == c))
|| a2
.teachers
.iter()
.any(|t| a.teachers.iter().any(|t2| t2 == t)))
})
.collect::<Vec<&Activity>>();
let neigh_map: HashSet<i32> = HashSet::from_iter(ns.iter().map(|n| n.id));
neighbours.insert(a.id, neigh_map);
}
neighbours
}