use {
    super::exception::*,
    super::job_policy::*,
    super::process::Process,
    crate::object::*,
    crate::task::Task,
    alloc::sync::{Arc, Weak},
    alloc::vec::Vec,
    spin::Mutex,
};
#[allow(dead_code)]
pub struct Job {
    base: KObjectBase,
    _counter: CountHelper,
    parent: Option<Arc<Job>>,
    parent_policy: JobPolicy,
    exceptionate: Arc<Exceptionate>,
    debug_exceptionate: Arc<Exceptionate>,
    inner: Mutex<JobInner>,
}
impl_kobject!(Job
    fn get_child(&self, id: KoID) -> ZxResult<Arc<dyn KernelObject>> {
        let inner = self.inner.lock();
        if let Some(job) = inner.children.iter().filter_map(|o|o.upgrade()).find(|o| o.id() == id) {
            return Ok(job);
        }
        if let Some(proc) = inner.processes.iter().find(|o| o.id() == id) {
            return Ok(proc.clone());
        }
        Err(ZxError::NOT_FOUND)
    }
    fn related_koid(&self) -> KoID {
        self.parent.as_ref().map(|p| p.id()).unwrap_or(0)
    }
);
define_count_helper!(Job);
#[derive(Default)]
struct JobInner {
    policy: JobPolicy,
    children: Vec<Weak<Job>>,
    processes: Vec<Arc<Process>>,
    
    killed: bool,
    timer_policy: TimerSlack,
    self_ref: Weak<Job>,
}
impl Job {
    
    pub fn root() -> Arc<Self> {
        let job = Arc::new(Job {
            base: KObjectBase::new(),
            _counter: CountHelper::new(),
            parent: None,
            parent_policy: JobPolicy::default(),
            exceptionate: Exceptionate::new(ExceptionChannelType::Job),
            debug_exceptionate: Exceptionate::new(ExceptionChannelType::JobDebugger),
            inner: Mutex::new(JobInner::default()),
        });
        job.inner.lock().self_ref = Arc::downgrade(&job);
        job
    }
    
    pub fn create_child(self: &Arc<Self>) -> ZxResult<Arc<Self>> {
        let mut inner = self.inner.lock();
        if inner.killed {
            return Err(ZxError::BAD_STATE);
        }
        let child = Arc::new(Job {
            base: KObjectBase::new(),
            _counter: CountHelper::new(),
            parent: Some(self.clone()),
            parent_policy: inner.policy.merge(&self.parent_policy),
            exceptionate: Exceptionate::new(ExceptionChannelType::Job),
            debug_exceptionate: Exceptionate::new(ExceptionChannelType::JobDebugger),
            inner: Mutex::new(JobInner::default()),
        });
        let child_weak = Arc::downgrade(&child);
        child.inner.lock().self_ref = child_weak.clone();
        inner.children.push(child_weak);
        Ok(child)
    }
    fn remove_child(&self, to_remove: &Weak<Job>) {
        let mut inner = self.inner.lock();
        inner.children.retain(|child| !to_remove.ptr_eq(child));
        if inner.killed && inner.processes.is_empty() && inner.children.is_empty() {
            drop(inner);
            self.terminate()
        }
    }
    
    pub fn policy(&self) -> JobPolicy {
        self.inner.lock().policy.merge(&self.parent_policy)
    }
    
    pub fn parent(&self) -> Option<Arc<Self>> {
        self.parent.clone()
    }
    
    
    
    
    
    
    
    pub fn set_policy_basic(
        &self,
        options: SetPolicyOptions,
        policies: &[BasicPolicy],
    ) -> ZxResult {
        let mut inner = self.inner.lock();
        if !inner.is_empty() {
            return Err(ZxError::BAD_STATE);
        }
        for policy in policies {
            if self.parent_policy.get_action(policy.condition).is_some() {
                match options {
                    SetPolicyOptions::Absolute => return Err(ZxError::ALREADY_EXISTS),
                    SetPolicyOptions::Relative => {}
                }
            } else {
                inner.policy.apply(*policy);
            }
        }
        Ok(())
    }
    
    pub fn set_policy_timer_slack(&self, policy: TimerSlackPolicy) -> ZxResult {
        let mut inner = self.inner.lock();
        if !inner.is_empty() {
            return Err(ZxError::BAD_STATE);
        }
        check_timer_policy(&policy)?;
        inner.timer_policy = inner.timer_policy.generate_new(policy);
        Ok(())
    }
    
    pub(super) fn add_process(&self, process: Arc<Process>) -> ZxResult {
        let mut inner = self.inner.lock();
        if inner.killed {
            return Err(ZxError::BAD_STATE);
        }
        inner.processes.push(process);
        Ok(())
    }
    
    pub(super) fn remove_process(&self, id: KoID) {
        let mut inner = self.inner.lock();
        inner.processes.retain(|proc| proc.id() != id);
        if inner.killed && inner.processes.is_empty() && inner.children.is_empty() {
            drop(inner);
            self.terminate()
        }
    }
    
    pub fn get_info(&self) -> JobInfo {
        JobInfo::default()
    }
    
    pub fn check_root_job(&self) -> ZxResult {
        if self.parent.is_some() {
            Err(ZxError::ACCESS_DENIED)
        } else {
            Ok(())
        }
    }
    
    pub fn process_ids(&self) -> Vec<KoID> {
        self.inner.lock().processes.iter().map(|p| p.id()).collect()
    }
    
    pub fn children_ids(&self) -> Vec<KoID> {
        self.inner
            .lock()
            .children
            .iter()
            .filter_map(|j| j.upgrade())
            .map(|j| j.id())
            .collect()
    }
    
    pub fn is_empty(&self) -> bool {
        self.inner.lock().is_empty()
    }
    
    fn terminate(&self) {
        self.exceptionate.shutdown();
        self.debug_exceptionate.shutdown();
        self.base.signal_set(Signal::JOB_TERMINATED);
        if let Some(parent) = self.parent.as_ref() {
            parent.remove_child(&self.inner.lock().self_ref)
        }
    }
}
impl Task for Job {
    
    
    fn kill(&self) {
        let (children, processes) = {
            let mut inner = self.inner.lock();
            if inner.killed {
                return;
            }
            inner.killed = true;
            (inner.children.clone(), inner.processes.clone())
        };
        if children.is_empty() && processes.is_empty() {
            self.terminate();
            return;
        }
        for child in children {
            if let Some(child) = child.upgrade() {
                child.kill();
            }
        }
        for proc in processes {
            proc.kill();
        }
    }
    fn suspend(&self) {
        panic!("job do not support suspend");
    }
    fn resume(&self) {
        panic!("job do not support resume");
    }
    fn exceptionate(&self) -> Arc<Exceptionate> {
        self.exceptionate.clone()
    }
    fn debug_exceptionate(&self) -> Arc<Exceptionate> {
        self.debug_exceptionate.clone()
    }
}
impl JobInner {
    fn is_empty(&self) -> bool {
        self.processes.is_empty() && self.children.is_empty()
    }
}
impl Drop for Job {
    fn drop(&mut self) {
        self.terminate();
    }
}
#[repr(C)]
#[derive(Default)]
pub struct JobInfo {
    return_code: i64,
    exited: bool,
    kill_on_oom: bool,
    debugger_attached: bool,
    padding: [u8; 5],
}
#[cfg(test)]
mod tests {
    use super::*;
    use crate::task::{CurrentThread, Status, Thread, ThreadState, TASK_RETCODE_SYSCALL_KILL};
    #[test]
    fn create() {
        let root_job = Job::root();
        let job = Job::create_child(&root_job).expect("failed to create job");
        let child = root_job
            .get_child(job.id())
            .unwrap()
            .downcast_arc()
            .unwrap();
        assert!(Arc::ptr_eq(&child, &job));
        assert_eq!(job.related_koid(), root_job.id());
        assert_eq!(root_job.related_koid(), 0);
        root_job.kill();
        assert_eq!(root_job.create_child().err(), Some(ZxError::BAD_STATE));
    }
    #[test]
    fn set_policy() {
        let root_job = Job::root();
        
        assert_eq!(
            root_job.policy().get_action(PolicyCondition::BadHandle),
            None
        );
        
        let policy = &[BasicPolicy {
            condition: PolicyCondition::BadHandle,
            action: PolicyAction::Deny,
        }];
        root_job
            .set_policy_basic(SetPolicyOptions::Relative, policy)
            .expect("failed to set policy");
        assert_eq!(
            root_job.policy().get_action(PolicyCondition::BadHandle),
            Some(PolicyAction::Deny)
        );
        
        let policy = &[BasicPolicy {
            condition: PolicyCondition::BadHandle,
            action: PolicyAction::Allow,
        }];
        root_job
            .set_policy_basic(SetPolicyOptions::Relative, policy)
            .expect("failed to set policy");
        assert_eq!(
            root_job.policy().get_action(PolicyCondition::BadHandle),
            Some(PolicyAction::Allow)
        );
        
        let job = Job::create_child(&root_job).expect("failed to create job");
        
        assert_eq!(
            job.policy().get_action(PolicyCondition::BadHandle),
            Some(PolicyAction::Allow)
        );
        
        assert_eq!(
            root_job.set_policy_basic(SetPolicyOptions::Relative, &[]),
            Err(ZxError::BAD_STATE)
        );
        
        let policy = &[BasicPolicy {
            condition: PolicyCondition::WrongObject,
            action: PolicyAction::Allow,
        }];
        job.set_policy_basic(SetPolicyOptions::Relative, policy)
            .expect("failed to set policy");
        assert_eq!(
            job.policy().get_action(PolicyCondition::WrongObject),
            Some(PolicyAction::Allow)
        );
        
        let policy = &[BasicPolicy {
            condition: PolicyCondition::BadHandle,
            action: PolicyAction::Deny,
        }];
        job.set_policy_basic(SetPolicyOptions::Relative, policy)
            .expect("failed to set policy");
        assert_eq!(
            job.policy().get_action(PolicyCondition::BadHandle),
            Some(PolicyAction::Allow)
        );
        
        assert_eq!(
            job.set_policy_basic(SetPolicyOptions::Absolute, policy),
            Err(ZxError::ALREADY_EXISTS)
        );
    }
    #[test]
    fn parent_child() {
        let root_job = Job::root();
        let job = Job::create_child(&root_job).expect("failed to create job");
        let proc = Process::create(&root_job, "proc").expect("failed to create process");
        assert_eq!(root_job.get_child(job.id()).unwrap().id(), job.id());
        assert_eq!(root_job.get_child(proc.id()).unwrap().id(), proc.id());
        assert_eq!(
            root_job.get_child(root_job.id()).err(),
            Some(ZxError::NOT_FOUND)
        );
        assert!(Arc::ptr_eq(&job.parent().unwrap(), &root_job));
        let job1 = root_job.create_child().expect("failed to create job");
        let proc1 = Process::create(&root_job, "proc1").expect("failed to create process");
        assert_eq!(root_job.children_ids(), vec![job.id(), job1.id()]);
        assert_eq!(root_job.process_ids(), vec![proc.id(), proc1.id()]);
        root_job.kill();
        assert_eq!(root_job.create_child().err(), Some(ZxError::BAD_STATE));
    }
    #[test]
    fn check() {
        let root_job = Job::root();
        assert!(root_job.is_empty());
        let job = root_job.create_child().expect("failed to create job");
        assert_eq!(root_job.check_root_job(), Ok(()));
        assert_eq!(job.check_root_job(), Err(ZxError::ACCESS_DENIED));
        assert!(!root_job.is_empty());
        assert!(job.is_empty());
        let _proc = Process::create(&job, "proc").expect("failed to create process");
        assert!(!job.is_empty());
    }
    #[test]
    fn kill() {
        let root_job = Job::root();
        let job = Job::create_child(&root_job).expect("failed to create job");
        let proc = Process::create(&root_job, "proc").expect("failed to create process");
        let thread = Thread::create(&proc, "thread").expect("failed to create thread");
        let current_thread = CurrentThread(thread.clone());
        root_job.kill();
        assert!(root_job.inner.lock().killed);
        assert!(job.inner.lock().killed);
        assert_eq!(proc.status(), Status::Exited(TASK_RETCODE_SYSCALL_KILL));
        assert_eq!(thread.state(), ThreadState::Dying);
        
        assert!(!root_job.signal().contains(Signal::JOB_TERMINATED));
        assert!(job.signal().contains(Signal::JOB_TERMINATED)); 
        assert!(!proc.signal().contains(Signal::PROCESS_TERMINATED));
        assert!(!thread.signal().contains(Signal::THREAD_TERMINATED));
        std::mem::drop(current_thread);
        assert!(root_job.inner.lock().killed);
        assert!(job.inner.lock().killed);
        assert_eq!(proc.status(), Status::Exited(TASK_RETCODE_SYSCALL_KILL));
        assert_eq!(thread.state(), ThreadState::Dead);
        
        assert!(root_job.signal().contains(Signal::JOB_TERMINATED));
        assert!(job.signal().contains(Signal::JOB_TERMINATED));
        assert!(proc.signal().contains(Signal::PROCESS_TERMINATED));
        assert!(thread.signal().contains(Signal::THREAD_TERMINATED));
        
        let root_job = Job::root();
        root_job.kill();
        assert!(root_job.inner.lock().killed);
        assert!(root_job.signal().contains(Signal::JOB_TERMINATED));
        
        let root_job = Job::root();
        let job = Job::create_child(&root_job).expect("failed to create job");
        let proc = Process::create(&root_job, "proc").expect("failed to create process");
        root_job.kill();
        assert!(root_job.inner.lock().killed);
        assert!(job.inner.lock().killed);
        assert_eq!(proc.status(), Status::Exited(TASK_RETCODE_SYSCALL_KILL));
        assert!(root_job.signal().contains(Signal::JOB_TERMINATED));
        assert!(job.signal().contains(Signal::JOB_TERMINATED));
        assert!(proc.signal().contains(Signal::PROCESS_TERMINATED));
    }
    #[test]
    fn critical_process() {
        let root_job = Job::root();
        let job = root_job.create_child().unwrap();
        let job1 = root_job.create_child().unwrap();
        let proc = Process::create(&job, "proc").expect("failed to create process");
        assert_eq!(
            proc.set_critical_at_job(&job1, true).err(),
            Some(ZxError::INVALID_ARGS)
        );
        proc.set_critical_at_job(&root_job, true).unwrap();
        assert_eq!(
            proc.set_critical_at_job(&job, true).err(),
            Some(ZxError::ALREADY_BOUND)
        );
        proc.exit(666);
        assert!(root_job.inner.lock().killed);
        assert!(root_job.signal().contains(Signal::JOB_TERMINATED));
    }
}