var async     = require("async");
var buster    = require("buster");

var Amount    = require("ripple-lib").Amount;
var Remote    = require("ripple-lib").Remote;
var Server    = require("./server").Server;

var testutils = require("./testutils");
var config    = testutils.init_config();

// How long to wait for server to start.
var serverDelay = 1500;

buster.testRunner.timeout = 5000;

/*
buster.testCase("Fee Changes", {
  'setUp' : testutils.build_setup({no_server: true}),  //
  'tearDown' : testutils.build_teardown(),

  "varying the fee for Payment" :
    function (done) {

                this.remote.transaction()
        .payment('root', 'alice', "10000")
        .on('success', function (r) {
           done();
          }).submit();

          this.remote.transaction()
        .payment('root', 'alice', "20000")
        .on('success', function (r) {
           done();
          }).submit();

           }
    });
  */

buster.testCase("Sending", {
  'setUp'     : testutils.build_setup(),
  //'setUp'     : testutils.build_setup({verbose: true , no_server: true}),
  'tearDown'  : testutils.build_teardown(),

  "send XRP to non-existent account with insufficent fee" :
    function (done) {
      var self    = this;
      var ledgers = 20;
      var got_proposed;

      this.remote.transaction()
        .payment('root', 'alice', "1")
        .on('success', function (r) {
            // Transaction sent.

            // console.log("success: %s", JSON.stringify(r));
          })
        .on('pending', function() {
            // Moving ledgers along.
            // console.log("missing: %d", ledgers);

            ledgers    -= 1;
            if (ledgers) {
              self.remote.ledger_accept();
            }
            else {
              buster.assert(false, "Final never received.");
              done();
            }
          })
        .on('lost', function () {
            // Transaction did not make it in.
            // console.log("lost");

            buster.assert(true);
            done();
          })
        .on('proposed', function (m) {
            // Transaction got an error.
            // console.log("proposed: %s", JSON.stringify(m));

            buster.assert.equals(m.result, 'tecNO_DST_INSUF_XRP');

            got_proposed  = true;

            self.remote.ledger_accept();    // Move it along.
          })
        .on('final', function (m) {
            // console.log("final: %s", JSON.stringify(m, undefined, 2));

            buster.assert.equals(m.metadata.TransactionResult, 'tecNO_DST_INSUF_XRP');
            done();
          })
        .on('error', function(m) {
            // console.log("error: %s", m);

            buster.assert(false);
          })
        .submit();
    },

  // Also test transaction becomes lost after tecNO_DST.
  "credit_limit to non-existent account = tecNO_DST" :
    function (done) {
      this.remote.transaction()
        .ripple_line_set("root", "100/USD/alice")
        .on('proposed', function (m) {
            //console.log("proposed: %s", JSON.stringify(m));

            buster.assert.equals(m.result, 'tecNO_DST');

            done();
          })
        .submit();
    },

  "credit_limit" :
    function (done) {
      var self = this;

      async.waterfall([
          function (callback) {
            self.what = "Create accounts.";

            testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob", "mtgox"], callback);
          },
          function (callback) {
            self.what = "Check a non-existent credit limit.";

            self.remote.request_ripple_balance("alice", "mtgox", "USD", 'CURRENT')
              .on('ripple_state', function (m) {
                  callback(true);
                })
              .on('error', function(m) {
                  // console.log("error: %s", JSON.stringify(m));

                  buster.assert.equals('remoteError', m.error);
                  buster.assert.equals('entryNotFound', m.remote.error);
                  callback();
                })
              .request();
          },
          function (callback) {
            self.what = "Create a credit limit.";

            testutils.credit_limit(self.remote, "alice", "800/USD/mtgox", callback);
          },
          function (callback) {
            self.remote.request_ripple_balance("alice", "mtgox", "USD", 'CURRENT')
              .on('ripple_state', function (m) {
//                console.log("BALANCE: %s", JSON.stringify(m));
//                console.log("account_balance: %s", m.account_balance.to_text_full());
//                console.log("account_limit: %s", m.account_limit.to_text_full());
//                console.log("peer_balance: %s", m.peer_balance.to_text_full());
//                console.log("peer_limit: %s", m.peer_limit.to_text_full());
                  buster.assert(m.account_balance.equals("0/USD/alice"));
                  buster.assert(m.account_limit.equals("800/USD/mtgox"));
                  buster.assert(m.peer_balance.equals("0/USD/mtgox"));
                  buster.assert(m.peer_limit.equals("0/USD/alice"));

                  callback();
                })
              .request();
          },
          function (callback) {
            self.what = "Modify a credit limit.";

            testutils.credit_limit(self.remote, "alice", "700/USD/mtgox", callback);
          },
          function (callback) {
            self.remote.request_ripple_balance("alice", "mtgox", "USD", 'CURRENT')
              .on('ripple_state', function (m) {
                  buster.assert(m.account_balance.equals("0/USD/alice"));
                  buster.assert(m.account_limit.equals("700/USD/mtgox"));
                  buster.assert(m.peer_balance.equals("0/USD/mtgox"));
                  buster.assert(m.peer_limit.equals("0/USD/alice"));

                  callback();
                })
              .request();
          },
          // Set negative limit.
          function (callback) {
            self.remote.transaction()
              .ripple_line_set("alice", "-1/USD/mtgox")
              .on('proposed', function (m) {
                  buster.assert.equals('temBAD_LIMIT', m.result);

                  callback('temBAD_LIMIT' !== m.result);
                })
              .submit();
          },
//          function (callback) {
//            self.what = "Display ledger";
//
//            self.remote.request_ledger('current', true)
//              .on('success', function (m) {
//                  console.log("Ledger: %s", JSON.stringify(m, undefined, 2));
//
//                  callback();
//                })
//              .request();
//          },
          function (callback) {
            self.what = "Zero a credit limit.";

            testutils.credit_limit(self.remote, "alice", "0/USD/mtgox", callback);
          },
          function (callback) {
            self.what = "Make sure line is deleted.";

            self.remote.request_ripple_balance("alice", "mtgox", "USD", 'CURRENT')
              .on('ripple_state', function (m) {
                  // Used to keep lines.
                  // buster.assert(m.account_balance.equals("0/USD/alice"));
                  // buster.assert(m.account_limit.equals("0/USD/alice"));
                  // buster.assert(m.peer_balance.equals("0/USD/mtgox"));
                  // buster.assert(m.peer_limit.equals("0/USD/mtgox"));

                  buster.assert(false);
                })
              .on('error', function (m) {
                  // console.log("error: %s", JSON.stringify(m));
                  buster.assert.equals('remoteError', m.error);
                  buster.assert.equals('entryNotFound', m.remote.error);

                  callback();
                })
              .request();
          },
          // TODO Check in both owner books.
          function (callback) {
            self.what = "Set another limit.";

            testutils.credit_limit(self.remote, "alice", "600/USD/bob", callback);
          },
          function (callback) {
            self.what = "Set limit on other side.";

            testutils.credit_limit(self.remote, "bob", "500/USD/alice", callback);
          },
          function (callback) {
            self.what = "Check ripple_line's state from alice's pov.";

            self.remote.request_ripple_balance("alice", "bob", "USD", 'CURRENT')
              .on('ripple_state', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));

                  buster.assert(m.account_balance.equals("0/USD/alice"));
                  buster.assert(m.account_limit.equals("600/USD/bob"));
                  buster.assert(m.peer_balance.equals("0/USD/bob"));
                  buster.assert(m.peer_limit.equals("500/USD/alice"));

                  callback();
                })
              .request();
          },
          function (callback) {
            self.what = "Check ripple_line's state from bob's pov.";

            self.remote.request_ripple_balance("bob", "alice", "USD", 'CURRENT')
              .on('ripple_state', function (m) {
                  buster.assert(m.account_balance.equals("0/USD/bob"));
                  buster.assert(m.account_limit.equals("500/USD/alice"));
                  buster.assert(m.peer_balance.equals("0/USD/alice"));
                  buster.assert(m.peer_limit.equals("600/USD/bob"));

                  callback();
                })
              .request();
          },
        ], function (error) {
          buster.refute(error, self.what);
          done();
        });
    },
});

// XXX In the future add ledger_accept after partial retry is implemented in the server.
buster.testCase("Sending future", {
  'setUp'     : testutils.build_setup(),
  // 'setUp'     : testutils.build_setup({ verbose : true }),
  'tearDown'  : testutils.build_teardown(),

  "direct ripple" :
    function (done) {
      var self = this;

      // self.remote.set_trace();

      async.waterfall([
          function (callback) {
            self.what = "Create accounts.";

            testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob"], callback);
          },
          function (callback) {
            self.what = "Set alice's limit.";

            testutils.credit_limit(self.remote, "alice", "600/USD/bob", callback);
          },
          function (callback) {
            self.what = "Set bob's limit.";

            testutils.credit_limit(self.remote, "bob", "700/USD/alice", callback);
          },
          function (callback) {
            self.what = "Set alice send bob partial with alice as issuer.";

            self.remote.transaction()
              .payment('alice', 'bob', "24/USD/alice")
              .once('proposed', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));
                  callback(m.result !== 'tesSUCCESS');
                })
              .once('final', function (m) {
                  buster.assert(m.result !== 'tesSUCCESS');
                })
              .submit();
          },
          function (callback) {
            self.what = "Verify balance.";

            self.remote.request_ripple_balance("alice", "bob", "USD", 'CURRENT')
              .once('ripple_state', function (m) {
                  buster.assert(m.account_balance.equals("-24/USD/alice"));
                  buster.assert(m.peer_balance.equals("24/USD/bob"));

                  callback();
                })
              .request();
          },
          function (callback) {
            self.what = "Set alice send bob more with bob as issuer.";

            self.remote.transaction()
              .payment('alice', 'bob', "33/USD/bob")
              .once('proposed', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));
                  callback(m.result !== 'tesSUCCESS');
                })
              .once('final', function (m) {
                  buster.assert(m.result !== 'tesSUCCESS');
                })
              .submit();
          },
          function (callback) {
            self.what = "Verify balance from bob's pov.";

            self.remote.request_ripple_balance("bob", "alice", "USD", 'CURRENT')
              .once('ripple_state', function (m) {
                  buster.assert(m.account_balance.equals("57/USD/bob"));
                  buster.assert(m.peer_balance.equals("-57/USD/alice"));

                  callback();
                })
              .request();
          },
          function (callback) {
            self.what = "Bob send back more than sent.";

            self.remote.transaction()
              .payment('bob', 'alice', "90/USD/bob")
              .once('proposed', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));
                  callback(m.result !== 'tesSUCCESS');
                })
              .once('final', function (m) {
                  buster.assert(m.result !== 'tesSUCCESS');
                })
              .submit();
          },
          function (callback) {
            self.what = "Verify balance from alice's pov: 1";

            self.remote.request_ripple_balance("alice", "bob", "USD", 'CURRENT')
              .once('ripple_state', function (m) {
                  buster.assert(m.account_balance.equals("33/USD/alice"));

                  callback();
                })
              .request();
          },
          function (callback) {
            self.what = "Alice send to limit.";

            self.remote.transaction()
              .payment('alice', 'bob', "733/USD/bob")
              .once('proposed', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));
                  callback(m.result !== 'tesSUCCESS');
                })
              .once('final', function (m) {
                  buster.assert(m.result !== 'tesSUCCESS');
                })
              .submit();
          },
          function (callback) {
            self.what = "Verify balance from alice's pov: 2";

            self.remote.request_ripple_balance("alice", "bob", "USD", 'CURRENT')
              .once('ripple_state', function (m) {
                  buster.assert(m.account_balance.equals("-700/USD/alice"));

                  callback();
                })
              .request();
          },
          function (callback) {
            self.what = "Bob send to limit.";

            self.remote.transaction()
              .payment('bob', 'alice', "1300/USD/bob")
              .once('proposed', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));
                  callback(m.result !== 'tesSUCCESS');
                })
              .once('final', function (m) {
                  buster.assert(m.result !== 'tesSUCCESS');
                })
              .submit();
          },
          function (callback) {
            self.what = "Verify balance from alice's pov: 3";

            self.remote.request_ripple_balance("alice", "bob", "USD", 'CURRENT')
              .once('ripple_state', function (m) {
                  buster.assert(m.account_balance.equals("600/USD/alice"));

                  callback();
                })
              .request();
          },
          function (callback) {
            // If this gets applied out of order, it could stop the big payment.
            self.what = "Bob send past limit.";

            self.remote.transaction()
              .payment('bob', 'alice', "1/USD/bob")
              .once('proposed', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));
                  callback(m.result !== 'tecPATH_DRY');
                })
              .submit();
          },
          function (callback) {
            self.what = "Verify balance from alice's pov: 4";

            self.remote.request_ripple_balance("alice", "bob", "USD", 'CURRENT')
              .once('ripple_state', function (m) {
                  buster.assert(m.account_balance.equals("600/USD/alice"));

                  callback();
                })
              .request();
          },
//        function (callback) {
//          // Make sure all is good after canonical ordering.
//          self.what = "Close the ledger and check balance.";
//
//          self.remote
//            .once('ledger_closed', function (message) {
//                // console.log("LEDGER_CLOSED: A: %d: %s", ledger_closed_index, ledger_closed);
//                callback();
//              })
//            .ledger_accept();
//        },
//        function (callback) {
//          self.what = "Verify balance from alice's pov: 5";
//
//          self.remote.request_ripple_balance("alice", "bob", "USD", 'CURRENT')
//            .once('ripple_state', function (m) {
//                console.log("account_balance: %s", m.account_balance.to_text_full());
//                console.log("account_limit: %s", m.account_limit.to_text_full());
//                console.log("peer_balance: %s", m.peer_balance.to_text_full());
//                console.log("peer_limit: %s", m.peer_limit.to_text_full());
//
//                buster.assert(m.account_balance.equals("600/USD/alice"));
//
//                callback();
//              })
//            .request();
//        },
        ], function (error) {
          buster.refute(error, self.what);
          done();
        });
    },

    // Ripple without credit path.
    // Ripple with one-way credit path.
});

buster.testCase("Gateway", {
  'setUp'     : testutils.build_setup(),
  // 'setUp'     : testutils.build_setup({ verbose: true }),
  'tearDown'  : testutils.build_teardown(),

  "customer to customer with and without transfer fee" :
    function (done) {
      var self = this;

      // self.remote.set_trace();

      async.waterfall([
          function (callback) {
            self.what = "Create accounts.";

            testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob", "mtgox"], callback);
          },
          function (callback) {
            self.what = "Set credit limits.";

            testutils.credit_limits(self.remote,
              {
                "alice" : "100/AUD/mtgox",
                "bob"   : "100/AUD/mtgox",
              },
              callback);
          },
          function (callback) {
            self.what = "Distribute funds.";

            testutils.payments(self.remote,
              {
                "mtgox" : [ "1/AUD/alice" ],
              },
              callback);
          },
          function (callback) {
            self.what = "Verify balances.";

            testutils.verify_balances(self.remote,
              {
                "alice"   : "1/AUD/mtgox",
                "mtgox"   : "-1/AUD/alice",
              },
              callback);
          },
          function (callback) {
            self.what = "Alice sends Bob 1 AUD";

            self.remote.transaction()
              .payment("alice", "bob", "1/AUD/mtgox")
              .on('proposed', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));

                  callback(m.result !== 'tesSUCCESS');
                })
              .submit();
          },
          function (callback) {
            self.what = "Verify balances 2.";

            testutils.verify_balances(self.remote,
              {
                "alice"   : "0/AUD/mtgox",
                "bob"     : "1/AUD/mtgox",
                "mtgox"   : "-1/AUD/bob",
              },
              callback);
          },
          function (callback) {
            self.what = "Set transfer rate.";

            self.remote.transaction()
              .account_set("mtgox")
              .transfer_rate(1e9*1.1)
              .once('proposed', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));
                  callback(m.result !== 'tesSUCCESS');
                })
              .submit();
          },
          function (callback) {
            self.what = "Bob sends Alice 0.5 AUD";

            self.remote.transaction()
              .payment("bob", "alice", "0.5/AUD/mtgox")
              .send_max("0.55/AUD/mtgox") // !!! Very important.
              .on('proposed', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));

                  callback(m.result !== 'tesSUCCESS');
                })
              .submit();
          },
          function (callback) {
            self.what = "Verify balances 3.";

            testutils.verify_balances(self.remote,
              {
                "alice"   : "0.5/AUD/mtgox",
                "bob"     : "0.45/AUD/mtgox",
                "mtgox"   : [ "-0.5/AUD/alice","-0.45/AUD/bob" ],
              },
              callback);
          },
        ], function (error) {
          buster.refute(error, self.what);
          done();
        });
    },

  "customer to customer, transfer fee, default path with and without specific issuer for Amount and SendMax" :
    function (done) {
      var self = this;

      // self.remote.set_trace();

      async.waterfall([
          function (callback) {
            self.what = "Create accounts.";

            testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob", "mtgox"], callback);
          },
          function (callback) {
            self.what = "Set transfer rate.";

            self.remote.transaction()
              .account_set("mtgox")
              .transfer_rate(1e9*1.1)
              .once('proposed', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));
                  callback(m.result !== 'tesSUCCESS');
                })
              .submit();
          },
          function (callback) {
            self.what = "Set credit limits.";

            testutils.credit_limits(self.remote,
              {
                "alice" : "100/AUD/mtgox",
                "bob"   : "100/AUD/mtgox",
              },
              callback);
          },
          function (callback) {
            self.what = "Distribute funds.";

            testutils.payments(self.remote,
              {
                "mtgox" : [ "4.4/AUD/alice" ],
              },
              callback);
          },
          function (callback) {
            self.what = "Verify balances.";

            testutils.verify_balances(self.remote,
              {
                "alice"   : "4.4/AUD/mtgox",
              },
              callback);
          },
          function (callback) {
            self.what = "Alice sends 1.1/AUD/mtgox Bob 1/AUD/mtgox";

            self.remote.transaction()
              .payment("alice", "bob", "1/AUD/mtgox")
              .send_max("1.1/AUD/mtgox")
              .on('proposed', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));

                  callback(m.result !== 'tesSUCCESS');
                })
              .submit();
          },
          function (callback) {
            self.what = "Verify balances 2.";

            testutils.verify_balances(self.remote,
              {
                "alice"   : "3.3/AUD/mtgox",
                "bob"     : "1/AUD/mtgox",
              },
              callback);
          },
          function (callback) {
            self.what = "Alice sends 1.1/AUD/mtgox Bob 1/AUD/bob";

            self.remote.transaction()
              .payment("alice", "bob", "1/AUD/bob")
              .send_max("1.1/AUD/mtgox")
              .on('proposed', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));

                  callback(m.result !== 'tesSUCCESS');
                })
              .submit();
          },
          function (callback) {
            self.what = "Verify balances 3.";

            testutils.verify_balances(self.remote,
              {
                "alice"   : "2.2/AUD/mtgox",
                "bob"     : "2/AUD/mtgox",
              },
              callback);
          },
          function (callback) {
            self.what = "Alice sends 1.1/AUD/alice Bob 1/AUD/mtgox";

            self.remote.transaction()
              .payment("alice", "bob", "1/AUD/mtgox")
              .send_max("1.1/AUD/alice")
              .on('proposed', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));

                  callback(m.result !== 'tesSUCCESS');
                })
              .submit();
          },
          function (callback) {
            self.what = "Verify balances 4.";

            testutils.verify_balances(self.remote,
              {
                "alice"   : "1.1/AUD/mtgox",
                "bob"     : "3/AUD/mtgox",
              },
              callback);
          },
          function (callback) {
            // Must fail, doesn't know to use the mtgox
            self.what = "Alice sends 1.1/AUD/alice Bob 1/AUD/bob";

            self.remote.transaction()
              .payment("alice", "bob", "1/AUD/bob")
              .send_max("1.1/AUD/alice")
              .on('proposed', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));

                  callback(m.result !== 'terNO_LINE');
                })
              .submit();
          },
          function (callback) {
            self.what = "Verify balances 5.";

            testutils.verify_balances(self.remote,
              {
                "alice"   : "1.1/AUD/mtgox",
                "bob"     : "3/AUD/mtgox",
              },
              callback);
          },
        ], function (error) {
          buster.refute(error, self.what);
          done();
        });
    },

  "subscribe test: customer to customer with and without transfer fee" :
    function (done) {
      var self = this;

      // self.remote.set_trace();

      async.waterfall([
          function (callback) {
            self.what = "Create accounts.";

            testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob", "mtgox"], callback);
          },
          function (callback) { testutils.ledger_close(self.remote, callback); },
          function (callback) {
            self.what = "Set credit limits.";

            testutils.credit_limits(self.remote,
              {
                "alice" : "100/AUD/mtgox",
                "bob"   : "100/AUD/mtgox",
              },
              callback);
          },
          function (callback) { testutils.ledger_close(self.remote, callback); },
          function (callback) {
            self.what = "Distribute funds.";

            testutils.payments(self.remote,
              {
                "mtgox" : [ "1/AUD/alice" ],
              },
              callback);
          },
          function (callback) { testutils.ledger_close(self.remote, callback); },
          function (callback) {
            self.what = "Verify balances.";

            testutils.verify_balances(self.remote,
              {
                "alice"   : "1/AUD/mtgox",
                "mtgox"   : "-1/AUD/alice",
              },
              callback);
          },
          function (callback) {
            self.what = "Alice sends Bob 1 AUD";

            self.remote.transaction()
              .payment("alice", "bob", "1/AUD/mtgox")
              .on('proposed', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));

                  callback(m.result !== 'tesSUCCESS');
                })
              .submit();
          },
          function (callback) { testutils.ledger_close(self.remote, callback); },
          function (callback) {
            self.what = "Verify balances 2.";

            testutils.verify_balances(self.remote,
              {
                "alice"   : "0/AUD/mtgox",
                "bob"     : "1/AUD/mtgox",
                "mtgox"   : "-1/AUD/bob",
              },
              callback);
          },
          function (callback) {
            self.what = "Set transfer rate.";

            self.remote.transaction()
              .account_set("mtgox")
              .transfer_rate(1e9*1.1)
              .once('proposed', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));
                  callback(m.result !== 'tesSUCCESS');
                })
              .submit();
          },
          function (callback) { testutils.ledger_close(self.remote, callback); },
          function (callback) {
            self.what = "Bob sends Alice 0.5 AUD";

            self.remote.transaction()
              .payment("bob", "alice", "0.5/AUD/mtgox")
              .send_max("0.55/AUD/mtgox") // !!! Very important.
              .on('proposed', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));

                  callback(m.result !== 'tesSUCCESS');
                })
              .submit();
          },
          function (callback) {
            self.what = "Verify balances 3.";

            testutils.verify_balances(self.remote,
              {
                "alice"   : "0.5/AUD/mtgox",
                "bob"     : "0.45/AUD/mtgox",
                "mtgox"   : [ "-0.5/AUD/alice","-0.45/AUD/bob" ],
              },
              callback);
          },
          function (callback) {
            self.what = "Subscribe and accept.";

            self.count  = 0;
            self.found  = 0;

            self.remote.on('ledger_closed', function (m) {
                // console.log("#### LEDGER_CLOSE: %s", JSON.stringify(m));
              });

            self.remote
              .on('transaction', function (m) {
                  // console.log("ACCOUNT: %s", JSON.stringify(m));
                  self.found  = 1;
                })
              .on('ledger_closed', function (m) {
                  // console.log("LEDGER_CLOSE: %d: %s", self.count, JSON.stringify(m));
              
                  if (self.count) {
                    callback(!self.found);
                  }
                  else {
                    self.count  = 1;

                    self.remote.ledger_accept();
                  }
                })
              .request_subscribe().accounts("mtgox")
              .request();

            self.remote.ledger_accept();
          },
        ], function (error) {
          buster.refute(error, self.what);
          done();
        });
    },

  "subscribe test: customer to customer with and without transfer fee: transaction retry logic" :
    function (done) {
      var self = this;

      // self.remote.set_trace();

      async.waterfall([
          function (callback) {
            self.what = "Create accounts.";

            testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob", "mtgox"], callback);
          },
          function (callback) {
            self.what = "Set credit limits.";

            testutils.credit_limits(self.remote,
              {
                "alice" : "100/AUD/mtgox",
                "bob"   : "100/AUD/mtgox",
              },
              callback);
          },
          function (callback) {
            self.what = "Distribute funds.";

            testutils.payments(self.remote,
              {
                "mtgox" : [ "1/AUD/alice" ],
              },
              callback);
          },
          function (callback) {
            self.what = "Verify balances.";

            testutils.verify_balances(self.remote,
              {
                "alice"   : "1/AUD/mtgox",
                "mtgox"   : "-1/AUD/alice",
              },
              callback);
          },
          function (callback) {
            self.what = "Alice sends Bob 1 AUD";

            self.remote.transaction()
              .payment("alice", "bob", "1/AUD/mtgox")
              .on('proposed', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));

                  callback(m.result !== 'tesSUCCESS');
                })
              .submit();
          },
          function (callback) {
            self.what = "Verify balances 2.";

            testutils.verify_balances(self.remote,
              {
                "alice"   : "0/AUD/mtgox",
                "bob"     : "1/AUD/mtgox",
                "mtgox"   : "-1/AUD/bob",
              },
              callback);
          },
//          function (callback) {
//            self.what = "Set transfer rate.";
//
//            self.remote.transaction()
//              .account_set("mtgox")
//              .transfer_rate(1e9*1.1)
//              .once('proposed', function (m) {
//                  // console.log("proposed: %s", JSON.stringify(m));
//                  callback(m.result !== 'tesSUCCESS');
//                })
//              .submit();
//          },
          function (callback) {
            self.what = "Bob sends Alice 0.5 AUD";

            self.remote.transaction()
              .payment("bob", "alice", "0.5/AUD/mtgox")
//              .send_max("0.55/AUD/mtgox") // !!! Very important.
              .on('proposed', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));

                  callback(m.result !== 'tesSUCCESS');
                })
              .submit();
          },
          function (callback) {
            self.what = "Verify balances 3.";

            testutils.verify_balances(self.remote,
              {
                "alice"   : "0.5/AUD/mtgox",
                "bob"     : "0.5/AUD/mtgox",
                "mtgox"   : [ "-0.5/AUD/alice","-0.5/AUD/bob" ],
              },
              callback);
          },
          function (callback) {
            self.what = "Subscribe and accept.";

            self.count  = 0;
            self.found  = 0;

            self.remote.on('ledger_closed', function (m) {
                // console.log("#### LEDGER_CLOSE: %s", JSON.stringify(m));
              });

            self.remote
              .on('transaction', function (m) {
                  // console.log("ACCOUNT: %s", JSON.stringify(m));
                  self.found  = 1;
                })
              .on('ledger_closed', function (m) {
                  // console.log("LEDGER_CLOSE: %d: %s", self.count, JSON.stringify(m));
              
                  if (self.count) {
                    callback(!self.found);
                  }
                  else {
                    self.count  = 1;

                    self.remote.ledger_accept();
                  }
                })
              .request_subscribe().accounts("mtgox")
              .request();

            self.remote.ledger_accept();
          },
          function (callback) {
            self.what = "Verify balances 4.";

            testutils.verify_balances(self.remote,
              {
                "alice"   : "0.5/AUD/mtgox",
                "bob"     : "0.5/AUD/mtgox",
                "mtgox"   : [ "-0.5/AUD/alice","-0.5/AUD/bob" ],
              },
              callback);
          },
        ], function (error) {
          buster.refute(error, self.what);
          done();
        });
    },
});

buster.testCase("Indirect ripple", {
  'setUp'     : testutils.build_setup(),
  // 'setUp'     : testutils.build_setup({ verbose: true }),
  'tearDown'  : testutils.build_teardown(),

  "indirect ripple" :
    function (done) {
      var self = this;

      // self.remote.set_trace();

      async.waterfall([
          function (callback) {
            self.what = "Create accounts.";

            testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob", "mtgox"], callback);
          },
          function (callback) {
            self.what = "Set credit limits.";

            testutils.credit_limits(self.remote,
              {
                "alice" : "600/USD/mtgox",
                "bob"   : "700/USD/mtgox",
              },
              callback);
          },
          function (callback) {
            self.what = "Distribute funds.";

            testutils.payments(self.remote,
              {
                "mtgox" : [ "70/USD/alice", "50/USD/bob" ],
              },
              callback);
          },
          function (callback) {
            self.what = "Verify alice balance with mtgox.";

            testutils.verify_balance(self.remote, "alice", "70/USD/mtgox", callback);
          },
          function (callback) {
            self.what = "Verify bob balance with mtgox.";

            testutils.verify_balance(self.remote, "bob", "50/USD/mtgox", callback);
          },
          function (callback) {
            self.what = "Alice sends more than has to issuer: 100 out of 70";

            self.remote.transaction()
              .payment("alice", "mtgox", "100/USD/mtgox")
              .on('proposed', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));

                  callback(m.result !== 'tecPATH_PARTIAL');
                })
              .submit();
          },
          function (callback) {
            self.what = "Alice sends more than has to bob: 100 out of 70";

            self.remote.transaction()
              .payment("alice", "bob", "100/USD/mtgox")
              .on('proposed', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));

                  callback(m.result !== 'tecPATH_PARTIAL');
                })
              .submit();
          },
        ], function (error) {
          buster.refute(error, self.what);
          done();
        });
    },

  "indirect ripple with path" :
    function (done) {
      var self = this;

      async.waterfall([
          function (callback) {
            self.what = "Create accounts.";

            testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob", "mtgox"], callback);
          },
          function (callback) {
            self.what = "Set credit limits.";

            testutils.credit_limits(self.remote,
              {
                "alice" : "600/USD/mtgox",
                "bob"   : "700/USD/mtgox",
              },
              callback);
          },
          function (callback) {
            self.what = "Distribute funds.";

            testutils.payments(self.remote,
              {
                "mtgox" : [ "70/USD/alice", "50/USD/bob" ],
              },
              callback);
          },
          function (callback) {
            self.what = "Alice sends via a path";

            self.remote.transaction()
              .payment("alice", "bob", "5/USD/mtgox")
              .path_add( [ { account: "mtgox" } ])
              .on('proposed', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));

                  callback(m.result !== 'tesSUCCESS');
                })
              .submit();
          },
          function (callback) {
            self.what = "Verify alice balance with mtgox.";

            testutils.verify_balance(self.remote, "alice", "65/USD/mtgox", callback);
          },
          function (callback) {
            self.what = "Verify bob balance with mtgox.";

            testutils.verify_balance(self.remote, "bob", "55/USD/mtgox", callback);
          },
        ], function (error) {
          buster.refute(error, self.what);
          done();
        });
    },

  "indirect ripple with multi path" :
    function (done) {
      var self = this;

      async.waterfall([
          function (callback) {
            self.what = "Create accounts.";

            testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob", "carol", "amazon", "mtgox"], callback);
          },
          function (callback) {
            self.what = "Set credit limits.";

            testutils.credit_limits(self.remote,
              {
                "amazon"  : "2000/USD/mtgox",
                "bob"   : [ "600/USD/alice", "1000/USD/mtgox" ],
                "carol" : [ "700/USD/alice", "1000/USD/mtgox" ],
              },
              callback);
          },
          function (callback) {
            self.what = "Distribute funds.";

            testutils.payments(self.remote,
              {
                "mtgox" : [ "100/USD/bob", "100/USD/carol" ],
              },
              callback);
          },
          function (callback) {
            self.what = "Alice pays amazon via multiple paths";

            self.remote.transaction()
              .payment("alice", "amazon", "150/USD/mtgox")
              .path_add( [ { account: "bob" } ])
              .path_add( [ { account: "carol" } ])
              .on('proposed', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));

                  callback(m.result !== 'tesSUCCESS');
                })
              .submit();
          },
          function (callback) {
            self.what = "Verify balances.";

            testutils.verify_balances(self.remote,
              {
                "alice"   : [ "-100/USD/bob", "-50/USD/carol" ],
                "amazon"  : "150/USD/mtgox",
                "bob"     : "0/USD/mtgox",
                "carol"   : "50/USD/mtgox",
              },
              callback);
          },
        ], function (error) {
          buster.refute(error, self.what);
          done();
        });
    },

  "indirect ripple with path and transfer fee" :
    function (done) {
      var self = this;

      async.waterfall([
          function (callback) {
            self.what = "Create accounts.";

            testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob", "carol", "amazon", "mtgox"], callback);
          },
          function (callback) {
            self.what = "Set mtgox transfer rate.";

            testutils.transfer_rate(self.remote, "mtgox", 1.1e9, callback);
          },
          function (callback) {
            self.what = "Set credit limits.";

            testutils.credit_limits(self.remote,
              {
                "amazon"  : "2000/USD/mtgox",
                "bob"   : [ "600/USD/alice", "1000/USD/mtgox" ],
                "carol" : [ "700/USD/alice", "1000/USD/mtgox" ],
              },
              callback);
          },
          function (callback) {
            self.what = "Distribute funds.";

            testutils.payments(self.remote,
              {
                "mtgox" : [ "100/USD/bob", "100/USD/carol" ],
              },
              callback);
          },
          function (callback) {
            self.what = "Alice pays amazon via multiple paths";

            self.remote.transaction()
              .payment("alice", "amazon", "150/USD/mtgox")
              .send_max("200/USD/alice")
              .path_add( [ { account: "bob" } ])
              .path_add( [ { account: "carol" } ])
              .on('proposed', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));

                  callback(m.result !== 'tesSUCCESS');
                })
              .submit();
          },
//          function (callback) {
//            self.what = "Display ledger";
//
//            self.remote.request_ledger('current', true)
//              .on('success', function (m) {
//                  console.log("Ledger: %s", JSON.stringify(m, undefined, 2));
//
//                  callback();
//                })
//              .request();
//          },
          function (callback) {
            self.what = "Verify balances.";

            // 65.00000000000001 is correct.
            // This is result of limited precision.
            testutils.verify_balances(self.remote,
              {
                "alice"   : [ "-100/USD/bob", "-65.00000000000001/USD/carol" ],
                "amazon"  : "150/USD/mtgox",
                "bob"     : "0/USD/mtgox",
                "carol"   : "35/USD/mtgox",
              },
              callback);
          },
        ], function (error) {
          buster.refute(error, self.what);
          done();
        });
    },
    // Direct ripple without no liqudity.
    // Test with XRP at start and end.
});

// vim:sw=2:sts=2:ts=8:et
