Documentation
use std::{
  collections::VecDeque,
  sync::{Arc, Mutex},
  time::Duration,
};

use crate::error::{EmapiError, Result};

pub trait EmapiConnection {
  fn write(&mut self, data: &[u8]) -> Result<()>;
  fn read(&mut self, timeout: Duration) -> Result<Vec<u8>>;
}

#[derive(Debug, Clone)]
pub struct ScriptedEmapiConnection {
  state: Arc<Mutex<ScriptedEmapiConnectionState>>,
}

#[derive(Debug, Default)]
struct ScriptedEmapiConnectionState {
  responses: VecDeque<Option<Vec<u8>>>,
  writes: Vec<Vec<u8>>,
  reads: usize,
}

impl ScriptedEmapiConnection {
  pub fn new(responses: Vec<Option<Vec<u8>>>) -> Self {
    Self {
      state: Arc::new(Mutex::new(ScriptedEmapiConnectionState {
        responses: responses.into(),
        writes: Vec::new(),
        reads: 0,
      })),
    }
  }

  pub fn writes(&self) -> Vec<Vec<u8>> {
    self.state.lock().unwrap().writes.clone()
  }

  pub fn reads(&self) -> usize {
    self.state.lock().unwrap().reads
  }
}

impl EmapiConnection for ScriptedEmapiConnection {
  fn write(&mut self, data: &[u8]) -> Result<()> {
    self.state.lock().unwrap().writes.push(data.to_vec());
    Ok(())
  }

  fn read(&mut self, timeout: Duration) -> Result<Vec<u8>> {
    let mut state = self.state.lock().unwrap();
    state.reads += 1;
    match state.responses.pop_front() {
      Some(Some(response)) => Ok(response),
      Some(None) => Err(EmapiError::Timeout {
        message: format!("timeout waiting for response after {timeout:?}"),
      }),
      None => Err(EmapiError::Timeout {
        message: format!("no response after {timeout:?}"),
      }),
    }
  }
}