// NB: transitionary, de-mode-ing.
// tjc: allowing deprecated modes due to function issue.
// can re-forbid them after snapshot
#[forbid(deprecated_pattern)];

/*!
 * A type representing values that may be computed concurrently and
 * operations for working with them.
 *
 * # Example
 *
 * ~~~
 * let delayed_fib = future::spawn {|| fib(5000) };
 * make_a_sandwich();
 * io::println(fmt!("fib(5000) = %?", delayed_fib.get()))
 * ~~~
 */

use either::Either;
use pipes::recv;
use cast::copy_lifetime;

#[doc = "The future type"]
pub struct Future<A> {
    /*priv*/ mut state: FutureState<A>,

    // FIXME(#2829) -- futures should not be copyable, because they close
    // over fn~'s that have pipes and so forth within!
    drop {}
}

priv enum FutureState<A> {
    Pending(fn~() -> A),
    Evaluating,
    Forced(~A)
}

/// Methods on the `future` type
impl<A:Copy> Future<A> {
    fn get() -> A {
        //! Get the value of the future

        get(&self)
    }
}

impl<A> Future<A> {
    fn get_ref(&self) -> &self/A {
        get_ref(self)
    }

    fn with<B>(blk: fn((&A)) -> B) -> B {
        //! Work with the value without copying it

        with(&self, blk)
    }
}

pub fn from_value<A>(val: A) -> Future<A> {
    /*!
     * Create a future from a value
     *
     * The value is immediately available and calling `get` later will
     * not block.
     */

    Future {state: Forced(~(move val))}
}

pub fn from_port<A:Send>(port: future_pipe::client::waiting<A>) ->
        Future<A> {
    /*!
     * Create a future from a port
     *
     * The first time that the value is requested the task will block
     * waiting for the result to be received on the port.
     */

    let port = ~mut Some(move port);
    do from_fn |move port| {
        let mut port_ = None;
        port_ <-> *port;
        let port = option::unwrap(move port_);
        match recv(move port) {
            future_pipe::completed(move data) => move data
        }
    }
}

pub fn from_fn<A>(f: ~fn() -> A) -> Future<A> {
    /*!
     * Create a future from a function.
     *
     * The first time that the value is requested it will be retreived by
     * calling the function.  Note that this function is a local
     * function. It is not spawned into another task.
     */

    Future {state: Pending(move f)}
}

pub fn spawn<A:Send>(blk: fn~() -> A) -> Future<A> {
    /*!
     * Create a future from a unique closure.
     *
     * The closure will be run in a new task and its result used as the
     * value of the future.
     */

    from_port(pipes::spawn_service_recv(future_pipe::init, |move blk, ch| {
        future_pipe::server::completed(move ch, blk());
    }))
}

pub fn get_ref<A>(future: &r/Future<A>) -> &r/A {
    /*!
     * Executes the future's closure and then returns a borrowed
     * pointer to the result.  The borrowed pointer lasts as long as
     * the future.
     */

    // The unsafety here is to hide the aliases from borrowck, which
    // would otherwise be concerned that someone might reassign
    // `future.state` and cause the value of the future to be freed.
    // But *we* know that once `future.state` is `Forced()` it will
    // never become "unforced"---so we can safely return a pointer
    // into the interior of the Forced() variant which will last as
    // long as the future itself.

    match future.state {
      Forced(ref v) => { // v here has type &A, but with a shorter lifetime.
        return unsafe{ copy_lifetime(future, &**v) }; // ...extend it.
      }
      Evaluating => {
        fail ~"Recursive forcing of future!";
      }
      Pending(_) => {}
    }

    let mut state = Evaluating;
    state <-> future.state;
    match move state {
      Forced(_) | Evaluating => {
        fail ~"Logic error.";
      }
      Pending(move f) => {
        future.state = Forced(~f());
        return get_ref(future);
      }
    }
}

pub fn get<A:Copy>(future: &Future<A>) -> A {
    //! Get the value of the future

    *get_ref(future)
}

pub fn with<A,B>(future: &Future<A>, blk: fn((&A)) -> B) -> B {
    //! Work with the value without copying it

    blk(get_ref(future))
}

proto! future_pipe (
    waiting:recv<T:Send> {
        completed(T) -> !
    }
)

#[allow(non_implicitly_copyable_typarams)]
pub mod test {
    #[test]
    pub fn test_from_value() {
        let f = from_value(~"snail");
        assert get(&f) == ~"snail";
    }

    #[test]
    pub fn test_from_port() {
        let (po, ch) = future_pipe::init();
        future_pipe::server::completed(ch, ~"whale");
        let f = from_port(po);
        assert get(&f) == ~"whale";
    }

    #[test]
    pub fn test_from_fn() {
        let f = from_fn(|| ~"brail");
        assert get(&f) == ~"brail";
    }

    #[test]
    pub fn test_interface_get() {
        let f = from_value(~"fail");
        assert f.get() == ~"fail";
    }

    #[test]
    pub fn test_with() {
        let f = from_value(~"nail");
        assert with(&f, |v| copy *v) == ~"nail";
    }

    #[test]
    pub fn test_get_ref_method() {
        let f = from_value(22);
        assert *f.get_ref() == 22;
    }

    #[test]
    pub fn test_get_ref_fn() {
        let f = from_value(22);
        assert *get_ref(&f) == 22;
    }

    #[test]
    pub fn test_interface_with() {
        let f = from_value(~"kale");
        assert f.with(|v| copy *v) == ~"kale";
    }

    #[test]
    pub fn test_spawn() {
        let f = spawn(|| ~"bale");
        assert get(&f) == ~"bale";
    }

    #[test]
    #[should_fail]
    #[ignore(cfg(target_os = "win32"))]
    pub fn test_futurefail() {
        let f = spawn(|| fail);
        let _x: ~str = get(&f);
    }

    #[test]
    pub fn test_sendable_future() {
        let expected = ~"schlorf";
        let f = do spawn |copy expected| { copy expected };
        do task::spawn {
            let actual = get(&f);
            assert actual == expected;
        }
    }
}
