use super::{process::Process, system::ObjectType, thread_manager::SIMULATORSTACKSIZE};
use crate::{simulator::machine::{NUM_INT_REGS, NUM_FP_REGS, STACK_REG}};

const STACK_FENCEPOST: u32 = 0xdeadbeef;

#[derive(PartialEq)]
pub struct ThreadContext {
    pub int_registers: [i64; NUM_INT_REGS],
    pub float_registers: [f32; NUM_FP_REGS],
    pc: i64,
}

#[derive(PartialEq)]
pub struct Thread {
    name: String,
    pub process: Option<Process>,
    // simulation_context: UContextT,
    pub thread_context: ThreadContext,
    pub stack_pointer: i32,
    object_type: ObjectType
}

impl Thread {

    /// Thread constructor
    pub fn new(name: &str) -> Self {
        Self {
            name: String::from(name),
            process: None,
            // simulation_context: UContextT::new(),
            thread_context: ThreadContext { 
                int_registers: [0; NUM_INT_REGS], 
                float_registers: [0f32; NUM_FP_REGS], 
                pc: 0 
            },
            stack_pointer: 0,
            object_type: ObjectType::ThreadType,
        }
    }

    pub fn init_thread_context(&mut self, initial_pc_reg: i64, initial_sp: i64, arg: i64) {
        self.thread_context.pc = initial_pc_reg;
        self.thread_context.int_registers[10] = arg;
        self.thread_context.int_registers[STACK_REG] = initial_sp;
    }

    pub fn init_simulator_context(&self, base_stack_addr: [i8; SIMULATORSTACKSIZE]) {
        // let res = self.simulation_context.get_context();
        // if res != 0 {
        //     panic!("getcontext returns non-zero value {}", res);
        // }
        // self.simulation_context.buf.uc_stack.ss_sp = base_stack_addr;
        // self.simulation_context.buf.uc_stack.ss_size = base_stack_addr.len();
        // self.simulation_context.buf.uc_stack.ss_flags = 0;
        // self.simulation_context.buf.uc_link = UContextT::new().buf;
        // self.simulation_context.make_context(start_thread_execution, 0);

        // self.simulation_context.stackBottom = base_stack_addr.to_vec();
        // self.simulation_context.stackBottom[0] = STACK_FENCEPOST;
    }
    
    /// Check if a thread has overflowed its stack
    /// 
    /// This assertion doesn't catch all stack overflow conditions and your program may still crash because of an overflow.
    /// 
    pub fn check_overflow(&self) {
        // if self.simulator_context.stackBottom != STACK_FENCEPOST {
        //     panic!("thread {} has overflowed", self.get_name())
        // }
    }

    pub fn sleep(&self) {
        unreachable!("Has been moved to thread manager");
    }

    pub fn save_simulator_state(&self) {
        todo!();
    }

    pub fn restore_simulator_state(&self) {
        todo!();
    }

    pub fn get_name(&self) -> String {
        self.name.clone()
    }

}

impl Drop for Thread {

    fn drop(&mut self) {
        self.object_type = ObjectType::InvalidType;
        todo!();
    }

}

fn start_thread_execution() {
    
}

#[cfg(test)]
mod test {

    use super::Thread;

    fn get_new_thread() -> Thread {
        Thread::new("test_thread")
    }

}