use crate::utility::list::List;
use crate::kernel::thread::Thread;
use crate::simulator::interrupt::InterruptStatus::InterruptOff;
use crate::simulator::machine::Machine;
use std::rc::Rc;


use super::scheduler::Scheduler;

pub struct Semaphore{

    counter:i32,
    waiting_queue:List<Rc<Thread>>

}

impl Semaphore{

    pub fn p(&mut self, current_thread: Rc<Thread>, machine: &mut Machine){
        let old_status = machine.interrupt.set_status(InterruptOff);
        self.counter -= 1;
        if self.counter < 0 {
            self.waiting_queue.push(Rc::clone(&current_thread));
            current_thread.sleep();
        }
        machine.interrupt.set_status(old_status);
    }

    pub fn v(&mut self, machine: &mut Machine, scheduler: &mut Scheduler){
        let old_status = machine.interrupt.set_status(InterruptOff);
        self.counter -= 1;
        if self.waiting_queue.peek() != None {
            scheduler.ready_to_run(self.waiting_queue.pop().unwrap());
        }
        machine.interrupt.set_status(old_status);
    }
}

pub struct Lock{

    owner: Rc<Thread>,
    waiting_queue:List<Rc<Thread>>,
    free: bool

}

impl Lock {
    pub fn acquire(&mut self, machine: &mut Machine, current_thread: Rc<Thread>) {
        let old_status = machine.interrupt.set_status(InterruptOff);

        if self.free {
            self.free = false;
            self.owner = current_thread;
        } else {
            self.waiting_queue.push(Rc::clone(&current_thread));
            current_thread.sleep();
        }

        machine.interrupt.set_status(old_status);
    }

    pub fn release(&mut self, machine: &mut Machine, scheduler: &mut Scheduler, current_thread: Rc<Thread>) {
        let old_status = machine.interrupt.set_status(InterruptOff);

        if self.is_held_by_current_thread(current_thread) {
            if self.waiting_queue.peek() != None {
                self.owner = self.waiting_queue.pop().unwrap();
                scheduler.ready_to_run(Rc::clone(&self.owner));
            } else {
                self.free = true;
            }
        }

        machine.interrupt.set_status(old_status);
    }

    pub fn is_held_by_current_thread(&mut self, current_thread: Rc<Thread>) -> bool {
        Rc::ptr_eq(&self.owner, &current_thread)
    }
}

pub struct Condition{

    waiting_queue:List<Rc<Thread>>
    
}

impl Condition {
    
    pub fn wait(&mut self, machine: &mut Machine, current_thread: Rc<Thread>) {
        let old_status = machine.interrupt.set_status(InterruptOff);

        self.waiting_queue.push(Rc::clone(&current_thread));
        current_thread.sleep();

        machine.interrupt.set_status(old_status);
    }

    pub fn signal(&mut self, machine: &mut Machine, scheduler: &mut Scheduler) {
        let old_status = machine.interrupt.set_status(InterruptOff);

        if self.waiting_queue.peek() != None {
            scheduler.ready_to_run(self.waiting_queue.pop().unwrap());
        }

        machine.interrupt.set_status(old_status);

    }

    pub fn broadcast(&mut self, machine: &mut Machine, scheduler: &mut Scheduler) {
        let old_status = machine.interrupt.set_status(InterruptOff);

        while self.waiting_queue.peek() != None {
            scheduler.ready_to_run(self.waiting_queue.pop().unwrap());
        }
        machine.interrupt.set_status(old_status);

    }

}