#!/usr/bin/env node
/* -*- Mode:js */
/* vim: set expandtab ts=4 sw=4: */
/*
 * You may redistribute this program and/or modify it under the terms of
 * the GNU General Public License as published by the Free Software Foundation,
 * either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
var Cjdns = require('./lib/cjdnsadmin/cjdnsadmin');
var nThen = require('nthen');
var Dns =  require('dns');

var WAIT_TIME = 5000;
var INTERVAL = 1000;

var now = function () { return (new Date()).getTime(); };
var nowSeconds = function () { return Math.floor(now() / 1000); };

var IP6_REGEX = new RegExp('^' + new Array(9).join(':[0-9a-f]{1,4}').substring(1) + '$');
var LABEL_REGEX = new RegExp('^' + new Array(5).join('.[0-9a-f]{4}').substring(1) + '$');
var ADDR_REGEX = new RegExp('^v[0-9]+' + new Array(5).join('\\.[0-9a-f]{4}') + '\\.[a-z0-9]{52}\\.k$');
var validTarget = function (target) {
    if (IP6_REGEX.test(target)) { return true; }
    if (LABEL_REGEX.test(target)) { return true; }
    if (ADDR_REGEX.test(target)) { return true; }
    return false;
};

var main = function (argv) {

    var maxCount = Math.Infinity;
    var keyPing = (argv.indexOf('-k') !== -1);
    var switchMode = (argv.indexOf('-s') !== -1) || keyPing;
    var data = '';
    if (argv.indexOf('-d') !== -1) {
        data = argv[argv.indexOf('-d') + 1];
        if (!switchMode) {
            console.log('-d data flag not possible without -s');
            return;
        }
    }
    if (argv.indexOf('-c') !== -1) {
        maxCount = argv[argv.indexOf('-c') + 1];
    }
    var target = argv[argv.length-1];

    var cjdns;
    nThen(function (waitFor) {

        Cjdns.connectWithAdminInfo(waitFor(function (c) { cjdns = c; }));

        if (!validTarget(target)) {
            Dns.lookup(target, 6, waitFor(function (err, res) {
                if (err) { throw err; }
                console.log(target + ' has ip address ' + res);
                target = res;
            }));
        }

    }).nThen(function (waitFor) {

        var count = 0;
        var routerPing = function () {
            if (count++ >= maxCount) { return; }
            var startTime = now();
            cjdns.RouterModule_pingNode(target, WAIT_TIME, waitFor(function (err, ret) {
                if (err) { throw err; }
                if (ret.result === 'timeout') {
                    console.log(
                        nowSeconds() + ' timeout  p' + ret.version + ' ' + ret.ms + 'ms');
                } else if (ret.result === 'pong') {
                    console.log(nowSeconds() + ' ' + ret.addr + ' ' + ret.ms + 'ms');
                } else if (ret.error === 'not_found') {
                    console.log(nowSeconds() + " No route to host");
                } else {
                    console.log(ret);
                }
                var pingIn = INTERVAL - (now() - startTime);
                if (pingIn < 0) { pingIn = 0; }
                setTimeout(waitFor(routerPing), pingIn);
            }));
        };

        var switchPing = function (keyPing) {
            if (count++ >= maxCount) { return; }
            var startTime = now();
            cjdns.SwitchPinger_ping(target, WAIT_TIME, keyPing^0, data, waitFor(function (err, ret) {
                if (err) { throw err; }

                var out = nowSeconds() + ' switchPing ';
                if (ret.result === 'timeout') {
                    out += 'timeout ' + ret.ms + 'ms';
                } else if (ret.result === 'pong') {
                    out += ret.path + '  p' + ret.version + ' ' + ret.ms + 'ms';
                    if (ret.key) {
                         out += ' ' + ret.key;
                    }
                } else {
                    out += JSON.stringify(ret);
                }
                console.log(out);

                var pingIn = INTERVAL - (now() - startTime);
                if (pingIn < 0) { pingIn = 0; }
                setTimeout(waitFor(switchPing), pingIn);
            }));
        };

        if (switchMode) {
            switchPing(keyPing);
        } else {
            routerPing();
        }
    }).nThen(function (waitFor) {
        cjdns.disconnect();
    });
};

main(process.argv);
