zinzen 0.3.0

Algorithm for auto-scheduling time-constrained tasks on a timeline
use crate::models::activity::{ActivityStatus, ActivityType};
use crate::models::interval::Interval;
use crate::models::task::TaskCompletedToday;
use crate::models::{activity::Activity, budget::TimeBudgetType, calendar::Calendar, goal::Goal};
use crate::technical::error::SchedulerError;
use std::collections::BTreeMap;

pub fn add_budget_min_week_activities(calendar: &Calendar, activities: &mut Vec<Activity>) {
    crate::log_debug!("Adding budget min week activities...");
    let mut get_to_week_min_budget_activities = vec![];
    for budget in &calendar.budgets {
        let mut is_min_week_reached = true;
        for time_budget in &budget.time_budgets {
            if time_budget.time_budget_type != TimeBudgetType::Week {
                continue;
            }
            if time_budget.scheduled < time_budget.min_scheduled {
                is_min_week_reached = false;
            }
        }
        if !is_min_week_reached {
            for time_budget in &budget.time_budgets {
                if time_budget.time_budget_type == TimeBudgetType::Week
                    && time_budget.scheduled < time_budget.min_scheduled
                {
                    get_to_week_min_budget_activities.extend(
                        Activity::get_activities_to_get_min_week_budget(
                            budget,
                            calendar,
                            time_budget,
                        ),
                    );
                }
            }
        }
    }
    crate::log_dbg!(&get_to_week_min_budget_activities);
    activities.extend(get_to_week_min_budget_activities);
}

pub fn add_budget_top_up_week_activities(calendar: &Calendar, activities: &mut Vec<Activity>) {
    crate::log_debug!("Adding budget top up week activities...");
    let mut top_up_activities = vec![];
    for budget in &calendar.budgets {
        let mut max_per_week = 0;
        for time_budget in &budget.time_budgets {
            if time_budget.time_budget_type == TimeBudgetType::Week
                && time_budget.scheduled != time_budget.max_scheduled
            {
                max_per_week = time_budget.max_scheduled;
            }
        }
        for time_budget in &budget.time_budgets {
            if time_budget.time_budget_type == TimeBudgetType::Period
                && time_budget.min_scheduled < time_budget.max_scheduled
                && time_budget.scheduled < time_budget.max_scheduled
            {
                top_up_activities.extend(Activity::get_activities_to_top_up_week_budget(
                    budget,
                    calendar,
                    time_budget,
                    max_per_week,
                ));
            }
        }
    }
    crate::log_dbg!(&top_up_activities);
    activities.extend(top_up_activities);
}

pub(crate) fn add_simple_activities(
    calendar: &mut Calendar,
    goals: &BTreeMap<String, Goal>,
    activities: &mut Vec<Activity>,
) -> Result<(), SchedulerError> {
    crate::log_debug!("Adding simple activities...");
    let mut simple_activities = vec![];
    for goal in goals.values() {
        if goal.min_duration.is_none() {
            continue;
        };
        simple_activities.extend(Activity::get_simple_activities(goal, calendar));
    }
    activities.extend(simple_activities);
    Ok(())
}

pub(crate) fn add_budget_min_day_activities(
    calendar: &mut Calendar,
    activities: &mut Vec<Activity>,
) {
    crate::log_debug!("Adding budget min day/period activities...");
    let mut min_day_activities = vec![];
    for budget in &calendar.budgets {
        for time_budget in &budget.time_budgets {
            if time_budget.time_budget_type == TimeBudgetType::Week {
                continue;
            }
            if time_budget.scheduled < time_budget.min_scheduled {
                min_day_activities.extend(Activity::get_activities_to_get_to_min_day_budget(
                    budget,
                    calendar,
                    time_budget,
                ));
            }
        }
    }
    crate::log_dbg!(&min_day_activities);
    activities.extend(min_day_activities);
}

pub(crate) fn add_tasks_completed_today(
    calendar: &Calendar,
    goals: &BTreeMap<String, Goal>,
    tasks_completed_today: &[TaskCompletedToday],
    activities: &mut Vec<Activity>,
) {
    crate::log_debug!("Adding tasks completed today...");
    for task in tasks_completed_today {
        let Some(goal) = goals.get(&task.goalid) else {
            continue;
        };
        let start_index = calendar.get_index_of(task.start);
        let end_index = calendar.get_index_of(task.deadline);
        let duration = end_index.saturating_sub(start_index);
        if duration == 0 {
            continue;
        }
        activities.push(Activity {
            goal_id: goal.id.clone(),
            activity_type: ActivityType::SimpleGoal,
            title: goal.title.clone(),
            min_block_size: duration,
            max_block_size: duration,
            total_duration: duration,
            duration_left: duration,
            status: ActivityStatus::Unprocessed,
            start: task.start,
            deadline: Some(task.deadline),
            compatible_intervals: vec![Interval {
                start: start_index,
                end: end_index,
            }],
            incompatible_intervals: vec![],
            flex: Some(1),
        });
    }
}