//! Job worker: a generic worker that just processes incoming
//! jobs one by one

use std::sync::Arc;

use async_trait::async_trait;
use tokio::sync::{mpsc, Mutex};

use crate::background::worker::*;
use crate::background::*;

pub(crate) struct JobWorker {
	pub(crate) index: usize,
	pub(crate) job_chan: Arc<Mutex<mpsc::UnboundedReceiver<(Job, bool)>>>,
	pub(crate) next_job: Option<Job>,
}

#[async_trait]
impl Worker for JobWorker {
	fn name(&self) -> String {
		format!("Job worker #{}", self.index)
	}

	async fn work(&mut self, _must_exit: &mut watch::Receiver<bool>) -> Result<WorkerState, Error> {
		match self.next_job.take() {
			None => return Ok(WorkerState::Idle),
			Some(job) => {
				job.await?;
				Ok(WorkerState::Busy)
			}
		}
	}

	async fn wait_for_work(&mut self, must_exit: &watch::Receiver<bool>) -> WorkerState {
		loop {
			match self.job_chan.lock().await.recv().await {
				Some((job, cancellable)) => {
					if cancellable && *must_exit.borrow() {
						continue;
					}
					self.next_job = Some(job);
					return WorkerState::Busy;
				}
				None => return WorkerState::Done,
			}
		}
	}
}