#![warn(missing_docs)]

//! The standard ableOS scheduler named
//!
//! # Notes
//! The scheduler is also responsible for choosing the priority of a process.
//! The scheduler is responsible for choosing which process to execute next.

use alloc::vec::Vec;

pub mod capabilities;
pub mod proc;

mod new_sched;
use proc::{Process, PID};

#[cfg(test)]
mod tests;

use crate::file::PathRep;

use capabilities::Capabilities;

lazy_static::lazy_static!(
        /// The standard implementation for ableOS
        pub static ref SCHEDULER: spin::Mutex<Scheduler> = spin::Mutex::new(Scheduler::new());

);

/// Scheduler priority model
#[derive(Clone, Copy, Debug)]
pub enum Priority {
   /// Exclusively Kernel space | 20 Timer Tick execution time
   High,
   /// Kernel / User space | 15 Timer Tick execution time
   Medium,
   /// low priority userspace code | 10 Timer Tick execution time
   Low,
}

#[derive(Clone, Debug)]
pub enum FileAccessTypes {
   All,
   Some(Vec<PathRep>),
   None,
}

///  This structure handles the process scheduling and execution
///
/// # Example
/// ```
/// use crate::scheduler::Priority;
/// use crate::scheduler::Scheduler;
///
/// let mut scheduler = Scheduler::new();
///
/// let pid = scheduler.new_process(Priority::High);
/// scheduler.run_process(pid);
/// ```
#[derive(Clone, Debug)]
pub struct Scheduler {
   /// The highest free process ID
   pub free_pid: PID,
   /// The execution time of the current process in ticks
   pub process_exec_time: u64,
   /// The list of processes
   pub list: Vec<Process>,
}

impl Scheduler {
   /// Create a new scheduler
   ///
   /// # Example
   /// ```
   /// use ableos::scheduler::Priority;
   /// let mut scheduler = Scheduler::new();
   /// ```
   pub fn new() -> Self {
      Self {
         free_pid: PID(0),
         process_exec_time: 0,
         list: Vec::new(),
      }
   }

   /// Change the current process to the next process in the list
   ///
   /// # Example
   ///
   /// ```
   /// let mut scheduler = scheduler();
   /// let mut process = scheduler.next_process();
   /// ```
   ///
   /// # Panics
   ///
   /// Panics if there are no processes in the list
   pub fn next_process(&mut self) {
      self.process_exec_time = 0;
      let previous_task = self.list[0].clone();
      self.list.remove(0);
      self.list.push(previous_task);
   }

   /// Creates a new process
   ///
   /// # Arguments
   ///
   /// * `priority` - The priority of the process
   ///
   /// # Example
   ///
   /// ```
   /// let mut scheduler = scheduler();
   /// let mut process = scheduler.new_process(Priority::Medium);
   /// ```
   pub fn new_process(&mut self, priority: Priority) -> Process {
      let process = Process {
         id: self.free_pid.clone(),
         capabilities: Capabilities::empty(),
         priority,
      };
      // self.free_pid.0 += 1;
      // self.list.push(process);
      process
   }
   /// Add a created process to the scheduler
   ///
   /// # Arguments
   ///
   /// * `process` - The process to add
   ///
   /// # Example
   ///
   /// ```
   /// let mut scheduler = Scheduler::new();
   /// let mut process = scheduler.new_process(Priority::Medium);
   /// scheduler.add_process(process);
   /// ```
   pub fn add_process(&mut self, process: Process) {
      self.list.push(process);

      self.free_pid.0 += 1;
   }
   /// Terminate the process with the matching PID
   ///
   /// # Arguments
   ///
   /// * `pid` - The PID of the process to terminate
   ///
   /// # Example
   ///
   /// ```
   /// let mut scheduler = scheduler();
   /// let mut process = scheduler.new_process(Priority::Medium);
   /// scheduler.terminate_process(process.id);
   /// ```
   pub fn term_process(&mut self, pid: PID) {
      let mut process_index = 0;
      for x in &self.list {
         if x.id == pid {
            self.list.remove(process_index);
            break;
         }
         process_index += 1
      }
   }
   /// Bump the current process' execution time
   ///
   /// # Example
   ///
   /// ```
   /// let mut scheduler = scheduler();
   /// let mut process = scheduler.new_process(Priority::Medium);
   /// scheduler.bump_exec();
   /// ```
   pub fn bump_exec(&mut self) {
      self.process_exec_time += 1;

      use Priority::*;
      if self.list.len() > 0 {
         match (self.process_exec_time, self.list[0].priority) {
            (20, High) => {
               self.next_process();
            }
            (15, Medium) => {
               self.next_process();
            }
            (10, Low) => {
               self.next_process();
            }

            (_, _) => {}
         }
      }
   }
}
