use std::future::Future;

use futures::channel::oneshot;

pub enum Soon<T: Send + 'static> {
  Waiting(oneshot::Receiver<T>),
  Value(T),
}

impl<T: Send + 'static> Soon<T> {
  pub fn new(future: impl Future<Output = T> + Send + 'static) -> Self {
    let (sender, receiver) = oneshot::channel();
    async_std::task::spawn(async move {
      let _ = sender.send(future.await);
    });

    Self::Waiting(receiver)
  }

  pub fn is_done(&mut self) -> bool {
    match self {
      Soon::Waiting(receiver) => {
        if let Some(value) = receiver.try_recv().unwrap() {
          *self = Soon::Value(value);

          true
        } else {
          false
        }
      }
      Soon::Value(_) => true,
    }
  }

  pub fn try_borrow(&mut self) -> Option<&T> {
    match self {
      Soon::Waiting(receiver) => {
        if let Some(value) = receiver.try_recv().unwrap() {
          *self = Soon::Value(value);

          self.try_borrow()
        } else {
          None
        }
      }
      Soon::Value(ref value) => Some(value),
    }
  }

  pub fn try_borrow_mut(&mut self) -> Option<&mut T> {
    match self {
      Soon::Waiting(receiver) => {
        if let Some(value) = receiver.try_recv().unwrap() {
          *self = Soon::Value(value);

          self.try_borrow_mut()
        } else {
          None
        }
      }
      Soon::Value(ref mut value) => Some(value),
    }
  }
}

impl<T: Clone + Send + 'static> Soon<T> {
  pub fn try_clone(&mut self) -> Option<T> {
    self.try_borrow().cloned()
  }
}
