#!/usr/bin/env node

"use strict";

const fs = require('fs');
const tape = require('blue-tape');

const RZ_PATH = require('which').sync('rz', {nothrow: true});

if (!RZ_PATH) {
    tape.only('SKIP: no “rz” in PATH!', (t) => {
        t.end();
    });
}

Object.assign(
    global,
    {
        Zmodem: require('./lib/zmodem'),
    }
);

var helper = require('./lib/testhelp');

var dir_before = process.cwd();
tape.onFinish( () => process.chdir( dir_before ) );

let TEST_STRINGS = [
    "",
    "0",
    "123",
    "\x00",
    "\x18",
    "\x18\x18\x18\x18\x18", //invalid as UTF-8
    "\x8a\x9a\xff\xfe",     //invalid as UTF-8
    "épée",
    "Hi diddle-ee, dee! A sailor’s life for me!",
];

var text_encoder = require('text-encoding').TextEncoder;
text_encoder = new text_encoder();

function _send_batch(t, batch, on_offer) {
    batch = batch.slice(0);

    return helper.exec_lrzsz_steps( t, RZ_PATH, [], [
        (zsession, child) => {
            function offer_sender() {
                if (!batch.length) {
                    zsession.close();
                    return;  //batch finished
                }

                return zsession.send_offer(
                    batch[0][0]
                ).then( (xfer) => {
                    if (on_offer) {
                        on_offer(xfer, batch[0]);
                    }

                    let file_contents = batch.shift()[1];

                    var octets;
                    if ("string" === typeof file_contents) {
                        octets = text_encoder.encode(file_contents);
                    }
                    else {
                        octets = file_contents; // Buffer
                    }

                    return xfer && xfer.end( Array.from(octets) );
                } ).then( offer_sender );
            }

            return offer_sender();
        },
        (zsession, child) => {
            return zsession.has_ended();
        },
    ] );
}

function _do_in_temp_dir( todo ) {
    var ret;

    process.chdir( helper.make_temp_dir() );

    try {
        ret = todo();
    }
    catch(e) {
        throw e;
    }
    finally {
        if (!ret) {
            process.chdir( dir_before );
        }
    }

    if (ret) {
        ret = ret.then( () => process.chdir( dir_before ) );
    }

    return ret;
}

tape("rz accepts one, then skips next", (t) => {
    return _do_in_temp_dir( () => {
        let filename = "no-clobberage";

        var batch = [
            [
                { name: filename },
                "the first",
            ],
            [
                { name: filename },
                "the second",
            ],
        ];

        var offers = [];
        function offer_cb(xfer, batch_item) {
            offers.push( xfer );
        }

        return _send_batch(t, batch, offer_cb).then( () => {
            var got_contents = fs.readFileSync(filename, "utf-8");
            t.equals( got_contents, "the first", 'second offer was rejected' );

            t.notEquals( offers[0], undefined, 'got an offer at first' );
            t.equals( offers[1], undefined, '… but no offer second' );
        } );
    } );
});

tape("send batch", (t) => {
    return _do_in_temp_dir( () => {
        var string_num = 0;

        var base = "batch_";
        var mtime_1990 = new Date("1990-01-01T00:00:00Z");

        var batch = TEST_STRINGS.map( (str, i) => {
            return [
                {
                    name: base + i,
                    mtime: mtime_1990,
                },
                str,
            ];
        } );

        return _send_batch(t, batch).then( () => {
            for (var sn=0; sn < TEST_STRINGS.length; sn++) {
                var got_contents = fs.readFileSync(base + sn, "utf-8");
                t.equals( got_contents, TEST_STRINGS[sn], `rz wrote out the file: ` + JSON.stringify(TEST_STRINGS[sn]) );
                t.equals( 0 + fs.statSync(base + sn).mtime, 0 + mtime_1990, `... and observed the sent mtime` );
            }
        } );
    } );
});

tape("send one at a time", (t) => {
    return _do_in_temp_dir( () => {
        var xfer;

        let test_strings = TEST_STRINGS.slice(0);

        function doer() {
            var file_contents = test_strings.shift();
            if (typeof(file_contents) !== "string") return;     //we’re done

            return helper.exec_lrzsz_steps( t, RZ_PATH, ["--overwrite"], [
                (zsession, child) => {
                    zsession.send_offer( { name: "single" } ).then( (xf) => {
                        t.ok( !!xf, 'rz accepted offer' );
                        xfer = xf;
                    } ).then(
                        () => xfer.end( Array.from( text_encoder.encode(file_contents) ) )
                    ).then(
                        () => zsession.close()
                    );

                    return true;
                },
                (zsession, child) => {
                    return zsession.has_ended();
                },
            ] ).then( () => {
                var got_contents = fs.readFileSync("single", "utf-8");
                t.equals( got_contents, file_contents, `rz wrote out the file: ` + JSON.stringify(file_contents) );
            } ).then( doer );
        }

        return doer();
    } );
});

tape("send single large file", (t) => {
    return _do_in_temp_dir( () => {
        var string_num = 0;

        var mtime_1990 = new Date("1990-01-01T00:00:00Z");
        var big_string = Array(30 * 1024 * 1024).fill('x').join("");

        var batch = [
            [
                {
                    name: "big_kahuna",
                },
                big_string,
            ],
        ];

        return _send_batch(t, batch).then( () => {
            var got_contents = fs.readFileSync("big_kahuna", "utf-8");
            t.equals( got_contents, big_string, 'rz wrote out the file');
        } );
    } );
});

tape("send single random file", (t) => {
    return _do_in_temp_dir( () => {
        var string_num = 0;

        var mtime_1990 = new Date("1990-01-01T00:00:00Z");

        var big_buffer = new Buffer(1024 * 1024);
        for (var i=0; i<big_buffer.length; i++) {
            big_buffer[i] = Math.floor( Math.random(256) );
        }

        var batch = [
            [
                {
                    name: "big_kahuna",
                },
                big_buffer,
            ],
        ];

        return _send_batch(t, batch).then( () => {
            var got_contents = fs.readFileSync("big_kahuna");
            t.equals( got_contents.join(), big_buffer.join(), 'rz wrote out the file');
        } );
    } );
});
