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

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

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


buster.testRunner.timeout = 5000;

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

  "no direct path, no intermediary -> no alternatives" :
    function (done) {
      var self = this;

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

            testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob"], callback);
          },
          function (callback) {
            self.what = "Find path from alice to bob";

            self.remote.request_ripple_path_find("alice", "bob", "5/USD/bob",
              [ { 'currency' : "USD" } ])
              .on('success', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));

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

  "direct path, no intermediary" :
    function (done) {
      var self = this;

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

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

            testutils.credit_limits(self.remote,
              {
                "bob"   : "700/USD/alice",
              },
              callback);
          },
//          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 = "Display available lines from alice";
//
//            self.remote.request_account_lines("alice", undefined, 'CURRENT')
//              .on('success', function (m) {
//                  console.log("LINES: %s", JSON.stringify(m, undefined, 2));
//
//                  callback();
//                })
//              .request();
//          },
          function (callback) {
            self.what = "Find path from alice to bob";

            self.remote.request_ripple_path_find("alice", "bob", "5/USD/bob",
              [ { 'currency' : "USD" } ])
              .on('success', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));

                  // 1 alternative.
                  buster.assert.equals(1, m.alternatives.length)
                  // Path is empty.
                  buster.assert.equals(0, m.alternatives[0].paths_canonical.length)

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

  "payment auto path find (using build_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" ],
              },
              callback);
          },
          function (callback) {
            self.what = "Payment with auto path";

            self.remote.transaction()
              .payment('alice', 'bob', "24/USD/bob")
              .build_path(true)
              .once('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"   : "46/USD/mtgox",
                "mtgox"   : [ "-46/USD/alice", "-24/USD/bob" ],
                "bob"     : "24/USD/mtgox",
              },
              callback);
          },
        ], function (error) {
          buster.refute(error, self.what);
          done();
        });
    },

  "path find" :
    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 = "Find path from alice to mtgox";

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

                  // 1 alternative.
                  buster.assert.equals(1, m.alternatives.length)
                  // Path is empty.
                  buster.assert.equals(0, m.alternatives[0].paths_canonical.length)

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

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

  "alternative paths - consume both" :
    function (done) {
      var self = this;

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

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

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

            testutils.payments(self.remote,
              {
                "bitstamp" : "70/USD/alice",
                "mtgox" : "70/USD/alice",
              },
              callback);
          },
          function (callback) {
            self.what = "Payment with auto path";

            self.remote.transaction()
              .payment('alice', 'bob', "140/USD/bob")
              .build_path(true)
              .once('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"     : [ "0/USD/mtgox", "0/USD/bitstamp" ],
                "bob"       : [ "70/USD/mtgox", "70/USD/bitstamp" ],
                "bitstamp"  : [ "0/USD/alice", "-70/USD/bob" ],
                "mtgox"     : [ "0/USD/alice", "-70/USD/bob" ],
              },
              callback);
          },
        ], function (error) {
          buster.refute(error, self.what);
          done();
        });
    },

  "alternative paths - consume best transfer" :
    function (done) {
      var self = this;

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

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

            self.remote.transaction()
              .account_set("bitstamp")
              .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" : [ "600/USD/mtgox", "800/USD/bitstamp" ],
                "bob"   : [ "700/USD/mtgox", "900/USD/bitstamp" ]
              },
              callback);
          },
          function (callback) {
            self.what = "Distribute funds.";

            testutils.payments(self.remote,
              {
                "bitstamp" : "70/USD/alice",
                "mtgox" : "70/USD/alice",
              },
              callback);
          },
          function (callback) {
            self.what = "Payment with auto path";

            self.remote.transaction()
              .payment('alice', 'bob', "70/USD/bob")
              .build_path(true)
              .once('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"     : [ "0/USD/mtgox", "70/USD/bitstamp" ],
                "bob"       : [ "70/USD/mtgox", "0/USD/bitstamp" ],
                "bitstamp"  : [ "-70/USD/alice", "0/USD/bob" ],
                "mtgox"     : [ "0/USD/alice", "-70/USD/bob" ],
              },
              callback);
          },
        ], function (error) {
          buster.refute(error, self.what);
          done();
        });
    },

  "alternative paths - consume best transfer first" :
    function (done) {
      var self = this;

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

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

            self.remote.transaction()
              .account_set("bitstamp")
              .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" : [ "600/USD/mtgox", "800/USD/bitstamp" ],
                "bob"   : [ "700/USD/mtgox", "900/USD/bitstamp" ]
              },
              callback);
          },
          function (callback) {
            self.what = "Distribute funds.";

            testutils.payments(self.remote,
              {
                "bitstamp" : "70/USD/alice",
                "mtgox" : "70/USD/alice",
              },
              callback);
          },
          function (callback) {
            self.what = "Payment with auto path";

            self.remote.transaction()
              .payment('alice', 'bob', "77/USD/bob")
              .build_path(true)
              .send_max("100/USD/alice")
              .once('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"     : [ "0/USD/mtgox", "62.3/USD/bitstamp" ],
                "bob"       : [ "70/USD/mtgox", "7/USD/bitstamp" ],
                "bitstamp"  : [ "-62.3/USD/alice", "-7/USD/bob" ],
                "mtgox"     : [ "0/USD/alice", "-70/USD/bob" ],
              },
              callback);
          },
        ], function (error) {
          buster.refute(error, self.what);
          done();
        });
    },

    // Test alternative paths with qualities.
});

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

  "// alternative paths - limit returned paths to best quality" :
    // alice +- bitstamp         -+ bob
    //       |- carol(fee)       -|     // To be excluded.
    //       |- dan(issue)       -|
    //       |- mtgox            -|
    function (done) {
      var self = this;

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

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

            self.remote.transaction()
              .account_set("carol")
              .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" : [ "800/USD/bitstamp", "800/USD/carol", "800/USD/dan", "800/USD/mtgox", ],
                "bob"   : [ "800/USD/bitstamp", "800/USD/carol", "800/USD/dan", "800/USD/mtgox", ],
                "dan"   : [ "800/USD/alice", "800/USD/bob" ],
              },
              callback);
          },
          function (callback) {
            self.what = "Distribute funds.";

            testutils.payments(self.remote,
              {
                "bitstamp" : "100/USD/alice",
                "carol" : "100/USD/alice",
                "mtgox" : "100/USD/alice",
              },
              callback);
          },
// XXX What should this check?
          function (callback) {
            self.what = "Find path from alice to bob";

            self.remote.request_ripple_path_find("alice", "bob", "5/USD/bob",
              [ { 'currency' : "USD" } ])
              .on('success', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));

                  // 1 alternative.
//                  buster.assert.equals(1, m.alternatives.length)
//                  // Path is empty.
//                  buster.assert.equals(0, m.alternatives[0].paths_canonical.length)

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

  "alternative paths - consume best transfer" :
    function (done) {
      var self = this;

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

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

            self.remote.transaction()
              .account_set("bitstamp")
              .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" : [ "600/USD/mtgox", "800/USD/bitstamp" ],
                "bob"   : [ "700/USD/mtgox", "900/USD/bitstamp" ]
              },
              callback);
          },
          function (callback) {
            self.what = "Distribute funds.";

            testutils.payments(self.remote,
              {
                "bitstamp" : "70/USD/alice",
                "mtgox" : "70/USD/alice",
              },
              callback);
          },
          function (callback) {
            self.what = "Payment with auto path";

            self.remote.transaction()
              .payment('alice', 'bob', "70/USD/bob")
              .build_path(true)
              .once('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"     : [ "0/USD/mtgox", "70/USD/bitstamp" ],
                "bob"       : [ "70/USD/mtgox", "0/USD/bitstamp" ],
                "bitstamp"  : [ "-70/USD/alice", "0/USD/bob" ],
                "mtgox"     : [ "0/USD/alice", "-70/USD/bob" ],
              },
              callback);
          },
        ], function (error) {
          buster.refute(error, self.what);
          done();
        });
    },

  "alternative paths - consume best transfer first" :
    function (done) {
      var self = this;

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

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

            self.remote.transaction()
              .account_set("bitstamp")
              .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" : [ "600/USD/mtgox", "800/USD/bitstamp" ],
                "bob"   : [ "700/USD/mtgox", "900/USD/bitstamp" ]
              },
              callback);
          },
          function (callback) {
            self.what = "Distribute funds.";

            testutils.payments(self.remote,
              {
                "bitstamp" : "70/USD/alice",
                "mtgox" : "70/USD/alice",
              },
              callback);
          },
          function (callback) {
            self.what = "Payment with auto path";

            self.remote.transaction()
              .payment('alice', 'bob', "77/USD/bob")
              .build_path(true)
              .send_max("100/USD/alice")
              .once('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"     : [ "0/USD/mtgox", "62.3/USD/bitstamp" ],
                "bob"       : [ "70/USD/mtgox", "7/USD/bitstamp" ],
                "bitstamp"  : [ "-62.3/USD/alice", "-7/USD/bob" ],
                "mtgox"     : [ "0/USD/alice", "-70/USD/bob" ],
              },
              callback);
          },
        ], function (error) {
          buster.refute(error, self.what);
          done();
        });
    },

    // Test alternative paths with qualities.
});

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

  "Path negative: Issue #5" :
    function (done) {
      var self = this;

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

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

            testutils.credit_limits(self.remote,
              {
                //  2. acct 4 trusted all the other accts for 100 usd
                "dan"   : [ "100/USD/alice", "100/USD/bob", "100/USD/carol" ],
                //  3. acct 2 acted as a nexus for acct 1 and 3, was trusted by 1 and 3 for 100 usd
                "alice" : [ "100/USD/bob" ],
                "carol" : [ "100/USD/bob" ],
              },
              callback);
          },
          function (callback) {
            // 4. acct 2 sent acct 3 a 75 iou
            self.what = "Bob sends Carol 75.";

            self.remote.transaction()
              .payment("bob", "carol", "75/USD/bob")
              .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,
              {
                "bob"   : [ "-75/USD/carol" ],
                "carol"   : "75/USD/bob",
              },
              callback);
          },
//          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 = "Find path from alice to bob";

            // 5. acct 1 sent a 25 usd iou to acct 2
            self.remote.request_ripple_path_find("alice", "bob", "25/USD/bob",
              [ { 'currency' : "USD" } ])
              .on('success', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));

                  // 0 alternatives.
                  buster.assert.equals(0, m.alternatives.length)

                  callback();
                })
              .request();
          },
          function (callback) {
            self.what = "alice fails to send to bob.";

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

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

  "Path negative: ripple-client issue #23: smaller" :
    //
    // alice -- limit 40 --> bob
    // alice --> carol --> dan --> bob
    // Balance of 100 USD Bob - Balance of 37 USD -> Rod
    //
    function (done) {
      var self = this;

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

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

            testutils.credit_limits(self.remote,
              {
                "bob"   : [ "40/USD/alice", "20/USD/dan" ],
                "carol" : [ "20/USD/alice" ],
                "dan"   : [ "20/USD/carol" ],
              },
              callback);
          },
          function (callback) {
            self.what = "Payment.";

            self.remote.transaction()
              .payment('alice', 'bob', "55/USD/bob")
              .build_path(true)
              .once('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,
              {
                "bob"   : [ "40/USD/alice", "15/USD/dan" ],
              },
              callback);
          },
//          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 (error) {
          buster.refute(error, self.what);
          done();
        });
    },

  "Path negative: ripple-client issue #23: larger" :
    //
    // alice -120 USD-> amazon -25 USD-> bob
    // alice -25 USD-> carol -75 USD -> dan -100 USD-> bob
    //
    function (done) {
      var self = this;

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

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

            testutils.credit_limits(self.remote,
              {
                "amazon"  : [ "120/USD/alice" ],
                "bob"     : [ "25/USD/amazon", "100/USD/dan" ],
                "carol"   : [ "25/USD/alice" ],
                "dan"     : [ "75/USD/carol" ],
              },
              callback);
          },
          function (callback) {
            self.what = "Payment.";

            self.remote.transaction()
              .payment('alice', 'bob', "50/USD/bob")
              .build_path(true)
              .once('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"   : [ "-25/USD/amazon", "-25/USD/carol" ],
                "bob"     : [ "25/USD/amazon", "25/USD/dan" ],
                "carol"   : [ "25/USD/alice", "-25/USD/dan" ],
                "dan"     : [ "25/USD/carol", "-25/USD/bob" ],
              },
              callback);
          },
//          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 (error) {
          buster.refute(error, self.what);
          done();
        });
    }
});

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

  "Via gateway" :
    // carol holds mtgoxAUD, sells mtgoxAUD for XRP
    // bob will hold mtgoxAUD
    // alice pays bob mtgoxAUD using XRP
    function (done) {
      var self = this;

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

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

            self.remote.transaction()
              .account_set("mtgox")
              .transfer_rate(1005000000)
              .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,
              {
                "bob" : [ "100/AUD/mtgox" ],
                "carol" : [ "100/AUD/mtgox" ],
              },
              callback);
          },
          function (callback) {
            self.what = "Distribute funds.";

            testutils.payments(self.remote,
              {
                "mtgox" : "50/AUD/carol",
              },
              callback);
          },
          function (callback) {
            self.what = "Carol create offer.";

            self.remote.transaction()
              .offer_create("carol", "50.0", "50/AUD/mtgox")
              .on('proposed', function (m) {
                  // console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
                  callback(m.result !== 'tesSUCCESS');

                  seq_carol = m.tx_json.Sequence;
                })
              .submit();
          },
          function (callback) {
            self.what = "Alice sends bob 10/AUD/mtgox using XRP.";

            // XXX Also try sending 10/AUX/bob
            self.remote.transaction()
              .payment("alice", "bob", "10/AUD/mtgox")
              .build_path(true)
              .send_max("100.0")
              .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,
              {
                "bob"   : "10/AUD/mtgox",
                "carol"   : "39.95/AUD/mtgox",
              },
              callback);
          },
//          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 = "Find path from alice to bob";
//
//            // 5. acct 1 sent a 25 usd iou to acct 2
//            self.remote.request_ripple_path_find("alice", "bob", "25/USD/bob",
//              [ { 'currency' : "USD" } ])
//              .on('success', function (m) {
//                  // console.log("proposed: %s", JSON.stringify(m));
//
//                  // 0 alternatives.
//                  buster.assert.equals(0, m.alternatives.length)
//
//                  callback();
//                })
//              .request();
//          },
        ], function (error) {
          buster.refute(error, self.what);
          done();
        });
    },

  "// Via gateway : FIX ME fails due to XRP rounding and not properly handling dry." :
    // carol holds mtgoxAUD, sells mtgoxAUD for XRP
    // bob will hold mtgoxAUD
    // alice pays bob mtgoxAUD using XRP
    function (done) {
      var self = this;

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

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

            self.remote.transaction()
              .account_set("mtgox")
              .transfer_rate(1005000000)
              .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,
              {
                "bob" : [ "100/AUD/mtgox" ],
                "carol" : [ "100/AUD/mtgox" ],
              },
              callback);
          },
          function (callback) {
            self.what = "Distribute funds.";

            testutils.payments(self.remote,
              {
                "mtgox" : "50/AUD/carol",
              },
              callback);
          },
          function (callback) {
            self.what = "Carol create offer.";

            self.remote.transaction()
              .offer_create("carol", "50", "50/AUD/mtgox")
              .on('proposed', function (m) {
                  // console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
                  callback(m.result !== 'tesSUCCESS');

                  seq_carol = m.tx_json.Sequence;
                })
              .submit();
          },
          function (callback) {
            self.what = "Alice sends bob 10/AUD/mtgox using XRP.";

            // XXX Also try sending 10/AUX/bob
            self.remote.transaction()
              .payment("alice", "bob", "10/AUD/mtgox")
              .build_path(true)
              .send_max("100")
              .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,
              {
                "bob"   : "10/AUD/mtgox",
                "carol"   : "39.95/AUD/mtgox",
              },
              callback);
          },
//          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 = "Find path from alice to bob";
//
//            // 5. acct 1 sent a 25 usd iou to acct 2
//            self.remote.request_ripple_path_find("alice", "bob", "25/USD/bob",
//              [ { 'currency' : "USD" } ])
//              .on('success', function (m) {
//                  // console.log("proposed: %s", JSON.stringify(m));
//
//                  // 0 alternatives.
//                  buster.assert.equals(0, m.alternatives.length)
//
//                  callback();
//                })
//              .request();
//          },
        ], function (error) {
          buster.refute(error, self.what);
          done();
        });
    },
});

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

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

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

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

            testutils.credit_limits(self.remote,
              {
                "bob"   : "1000/USD/alice",
                "carol" : "2000/USD/bob",
              },
              callback);
          },
          function (callback) {
            self.what = "Find path from alice to carol";

            self.remote.request_ripple_path_find("alice", "carol", "5/USD/carol",
              [ { 'currency' : "USD" } ])
              .on('success', function (m) {
                  // console.log("proposed: %s", JSON.stringify(m));

                  // 1 alternative.
                  buster.assert.equals(1, m.alternatives.length)
                  // Path is empty.
                  buster.assert.equals(0, m.alternatives[0].paths_canonical.length)

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

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

  "quality set and test" :
    function (done) {
      var self = this;

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

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

            testutils.credit_limits(self.remote,
              {
                "bob"   : "1000/USD/alice:2000,1400000000",
              },
              callback);
          },
          function (callback) {
            self.what = "Verify credit limits extended.";

            testutils.verify_limit(self.remote, "bob", "1000/USD/alice:2000,1400000000", callback);
          },
        ], function (error) {
          buster.refute(error, self.what);
          done();
        });
    },

  "// quality payment (BROKEN DUE TO ROUNDING)" :
    function (done) {
      var self = this;

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

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

            testutils.credit_limits(self.remote,
              {
                "bob"   : "1000/USD/alice:" + .9*1e9 + "," + 1e9,
              },
              callback);
          },
          function (callback) {
            self.what = "Payment with auto path";

            self.remote.transaction()
              .payment('alice', 'bob', "100/USD/bob")
              .send_max("120/USD/alice")
//              .set_flags('PartialPayment')
//              .build_path(true)
              .once('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', { accounts: true, expand: true })
              .on('success', function (m) {
                  console.log("Ledger: %s", JSON.stringify(m, undefined, 2));

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

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

  "trust normal clear" :
    function (done) {
      var self = this;

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

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

            // Mutual trust.
            testutils.credit_limits(self.remote,
              {
                "alice"   : "1000/USD/bob",
                "bob"   : "1000/USD/alice",
              },
              callback);
          },
          function (callback) {
            self.what = "Verify credit limits.";

            testutils.verify_limit(self.remote, "bob", "1000/USD/alice", callback);
          },
          function (callback) {
            self.what = "Clear credit limits.";

            // Mutual trust.
            testutils.credit_limits(self.remote,
              {
                "alice"   : "0/USD/bob",
                "bob"   : "0/USD/alice",
              },
              callback);
          },
          function (callback) {
            self.what = "Verify credit limits.";

            testutils.verify_limit(self.remote, "bob", "0/USD/alice", function (m) {
                var success = m && 'remoteError' === m.error && 'entryNotFound' === m.remote.error;

                callback(!success);
              });
          },
          // YYY Could verify owner counts are zero.
        ], function (error) {
          buster.refute(error, self.what);
          done();
        });
    },

  "trust auto clear" :
    function (done) {
      var self = this;

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

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

            // Mutual trust.
            testutils.credit_limits(self.remote,
              {
                "alice" : "1000/USD/bob",
              },
              callback);
          },
          function (callback) {
            self.what = "Distribute funds.";

            testutils.payments(self.remote,
              {
                "bob" : [ "50/USD/alice" ],
              },
              callback);
          },
          function (callback) {
            self.what = "Clear credit limits.";

            // Mutual trust.
            testutils.credit_limits(self.remote,
              {
                "alice"   : "0/USD/bob",
              },
              callback);
          },
          function (callback) {
            self.what = "Verify credit limits.";

            testutils.verify_limit(self.remote, "alice", "0/USD/bob", callback);
          },
          function (callback) {
            self.what = "Return funds.";

            testutils.payments(self.remote,
              {
                "alice" : [ "50/USD/bob" ],
              },
              callback);
          },
          function (callback) {
            self.what = "Verify credit limit gone.";

            testutils.verify_limit(self.remote, "bob", "0/USD/alice", function (m) {
                var success = m && 'remoteError' === m.error && 'entryNotFound' === m.remote.error;

                callback(!success);
              });
          },
        ], function (error) {
          buster.refute(error, self.what);
          done();
        });
    },
});
// vim:sw=2:sts=2:ts=8:et
