use super::*;

#[derive(Clone)]
pub(crate) struct Track(Arc<RwLock<Inner>>);

struct Inner {
  buffer: Vec<f32>,
  decoder: Decoder<BufReader<File>>,
  done: bool,
}

impl Track {
  pub(crate) fn new(path: &Utf8Path) -> Result<Self> {
    let file = File::open(path).context(error::FilesystemIo { path })?;
    let reader = BufReader::new(file);
    let source = Decoder::new(reader).context(error::DecoderOpen { path })?;
    Ok(Self(Arc::new(RwLock::new(Inner {
      buffer: Vec::new(),
      decoder: source,
      done: false,
    }))))
  }

  fn read(&self) -> RwLockReadGuard<Inner> {
    self.0.read().unwrap()
  }

  fn write(&mut self) -> RwLockWriteGuard<Inner> {
    self.0.write().unwrap()
  }
}

impl Source for Track {
  fn channels(&self) -> u16 {
    self.read().decoder.channels()
  }

  fn current_span_len(&self) -> Option<usize> {
    self.read().decoder.current_span_len()
  }

  fn sample_rate(&self) -> u32 {
    self.read().decoder.sample_rate()
  }

  fn total_duration(&self) -> Option<std::time::Duration> {
    self.read().decoder.total_duration()
  }
}

impl Stream for Track {
  fn append(&self, sink: &Sink) {
    sink.append(self.clone());
  }

  fn channels(&self) -> u16 {
    self.read().decoder.channels()
  }

  fn drain_samples(&mut self, samples: &mut Vec<f32>) {
    samples.append(&mut self.write().buffer);
  }

  fn is_done(&self) -> bool {
    let inner = self.read();
    inner.done && inner.buffer.is_empty()
  }

  fn sample_rate(&self) -> u32 {
    self.read().decoder.sample_rate()
  }
}

impl Iterator for Track {
  type Item = f32;

  fn next(&mut self) -> Option<f32> {
    let mut inner = self.write();

    let Some(sample) = inner.decoder.next() else {
      inner.done = true;
      return None;
    };

    let sample = sample.to_sample();

    inner.buffer.push(sample);

    Some(sample)
  }
}
