// xfail-pretty

// An example of the bank protocol from eholk's blog post.
//
// http://theincredibleholk.wordpress.com/2012/07/06/rusty-pipes/

use pipes::try_recv;

type username = ~str;
type password = ~str;
type money = float;
type amount = float;

proto! bank (
    login:send {
        login(username, password) -> login_response
    }

    login_response:recv {
        ok -> connected,
        invalid -> login
    }

    connected:send {
        deposit(money) -> connected,
        withdrawal(amount) -> withdrawal_response
    }

    withdrawal_response:recv {
        money(money) -> connected,
        insufficient_funds -> connected
    }
)

macro_rules! move_it (
    { $x:expr } => { unsafe { let y <- *ptr::addr_of(&($x)); y } }
)

fn switch<T: Send, U>(+endp: pipes::RecvPacket<T>,
                      f: fn(+v: Option<T>) -> U) -> U {
    f(pipes::try_recv(endp))
}

fn move_it<T>(-x: T) -> T { x }

macro_rules! follow (
    {
        $($message:path$(($($x: ident),+))||* -> $next:ident $e:expr)+
    } => (
        |m| match move m {
          $(Some($message($($($x,)+)* next)) => {
            let $next = move_it!(next);
            $e })+
          _ => { fail }
        }
    );
)

fn client_follow(+bank: bank::client::login) {
    use bank::*;

    let bank = client::login(bank, ~"theincredibleholk", ~"1234");
    let bank = switch(bank, follow! (
        ok -> connected { connected }
        invalid -> _next { fail ~"bank closed the connected" }
    ));

    let bank = client::deposit(bank, 100.00);
    let bank = client::withdrawal(bank, 50.00);
    switch(bank, follow! (
        money(m) -> _next {
            io::println(~"Yay! I got money!");
        }
        insufficient_funds -> _next {
            fail ~"someone stole my money"
        }
    ));
}

fn bank_client(+bank: bank::client::login) {
    use bank::*;

    let bank = client::login(bank, ~"theincredibleholk", ~"1234");
    let bank = match try_recv(bank) {
      Some(ok(connected)) => {
        move_it!(connected)
      }
      Some(invalid(_)) => { fail ~"login unsuccessful" }
      None => { fail ~"bank closed the connection" }
    };

    let bank = client::deposit(bank, 100.00);
    let bank = client::withdrawal(bank, 50.00);
    match try_recv(bank) {
      Some(money(m, _)) => {
        io::println(~"Yay! I got money!");
      }
      Some(insufficient_funds(_)) => {
        fail ~"someone stole my money"
      }
      None => {
        fail ~"bank closed the connection"
      }
    }
}

fn main() {
}
