use std::cell::RefCell;
use std::rc::Rc;

use crate::utility::list::List;
use crate::kernel::thread::Thread;
use super::thread_manager::ThreadManager;

#[derive(PartialEq)]
pub struct Scheduler {
    ready_list: List<Rc<RefCell<Thread>>>,
    pub thread_manager: Option<Rc<RefCell<ThreadManager>>> 
}

impl Scheduler {

    /// Constructor
    /// 
    /// Initilize the list of ready thread
    pub fn new() -> Self {
        Self {
            ready_list: List::new(),
            thread_manager: Option::None
        }
    }

    /// Mark a thread as aready, but not necessarily running yet.
    /// 
    /// Put it in the ready list, for later scheduling onto the CPU.
    /// 
    /// ## Pamameter
    /// 
    /// **thread** is the thread to be put on the read list
    pub fn ready_to_run(&mut self, thread: Rc<RefCell<Thread>>) {
        self.ready_list.push(thread);
    }
    
    /// Return the next thread to be scheduled onto the CPU.
    /// If there are no ready threads, return Option::None
    /// 
    /// Thread is removed from the ready list.
    /// 
    /// **return** Thread thread to be scheduled
    pub fn find_next_to_run(&mut self) -> Option<Rc<RefCell<Thread>>> {
        self.ready_list.pop()
    }

    /// Dispatch the CPU to next_thread. Save the state of the old thread 
    /// and load the state of the new thread.
    /// 
    /// We assume the state of the previously running thread has already been changed from running to blocked or ready.
    /// 
    /// Global variable g_current_thread become next_thread
    /// 
    /// ## Parameter
    /// 
    /// **next_thread** thread to dispatch to the CPU
    pub fn switch_to(&mut self, next_thread: Rc<RefCell<Thread>>) {
        if let Some(tm) = &self.thread_manager {
            let rc = Rc::clone(&tm);
            if let Some(old_thread) = tm.borrow_mut().get_g_current_thread() {
                rc.borrow_mut().thread_save_processor_state(Rc::clone(&old_thread));
                // old_thread.save_simulator_state();

                if old_thread != &next_thread {
                    rc.borrow_mut().thread_restore_processor_state(Rc::clone(&next_thread));
                    // next_thread.restore_simulator_state();
                    rc.borrow_mut().set_g_current_thread(Option::Some(next_thread));
                }
            }
        } else {
            panic!("thread manager shouldn't be none");
        }
    }
}