const REPORT_URL = 'http://localhost:8081/report_perf_data'
// Set this to enforce that the perf server must be up.
// Typically used for debugging.
const fail_on_no_perf = false;

function benchmarkAndReport(benchName, setupFn, testFn, teardownFn) {
    try {
        let ctx = {};
        // warmup 3 times (arbitrary choice)
        setupFn(ctx);
        testFn(ctx);
        testFn(ctx);
        testFn(ctx);
        teardownFn(ctx);

        ctx = {};
        setupFn(ctx);
        let start = Date.now();
        let now = start;
        times = 0;
        // See how many times we can do it in 100ms (arbitrary choice)
        while (now - start < 100) {
            testFn(ctx);
            now = Date.now();
            times++;
        }

        teardownFn(ctx);

        // Try to make it go for 2 seconds (arbitrarily chosen)
        // Since the pre-try took 100ms, multiply by 20 to get
        // approximate tries in 2s (unless now - start >> 100 ms)
        let goalTimes = times * 20;
        ctx = {};
        setupFn(ctx);
        times = 0;
        start = Date.now();
        while (times < goalTimes) {
            testFn(ctx);
            times++;
        }
        const end = Date.now();
        teardownFn(ctx);

        const us = (end - start) * 1000 / times;
        console.log(benchName, `${us} microseconds`)
        return _report(us, benchName);
    } catch(e) {
        console.error('caught error', e);
        return Promise.reject(e);
    }
}

// The same as benchmarkAndReport, except expects the third parameter, testFn, to return a promise
async function asyncBenchmarkAndReport(benchName, setupFn, testFn, teardownFn) {
    try {
        let ctx = {};
        // warmup 3 times (arbitrary choice)
        setupFn(ctx);
        await testFn(ctx);
        await testFn(ctx);
        await testFn(ctx);
        teardownFn(ctx);

        ctx = {};
        setupFn(ctx);
        let start = Date.now();
        let now = start;
        times = 0;
        // See how many times we can do it in 100ms (arbitrary choice)
        while (now - start < 100) {
            await testFn(ctx);
            now = Date.now();
            times++;
        }

        teardownFn(ctx);

        // Try to make it go for 2 seconds (arbitrarily chosen)
        // Since the pre-try took 100ms, multiply by 20 to get
        // approximate tries in 2s (unless now - start >> 100 ms)
        let goalTimes = times * 20;
        ctx = {};
        setupFn(ctx);
        times = 0;
        start = Date.now();
        while (times < goalTimes) {
            await testFn(ctx);
            times++;
        }
        const end = Date.now();
        teardownFn(ctx);

        const us = (end - start) * 1000 / times;
        console.log(benchName, `${us} microseconds`)
        return _report(us, benchName);
    } catch(e) {
        console.error('caught error', e);
        return Promise.reject(e);
    }
}


function _report(microseconds, benchName) {
    return fetch(REPORT_URL, {
        method: 'POST',
        mode: 'no-cors',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            'bench_name': benchName,
            'time_us': microseconds,
        })
    }).then(() => console.log(`Successfully reported ${benchName} to perf aggregator`));
}

function reportError(done) {
    return (e) => {
        console.log("Error with fetching. Likely could not connect to aggegator server", e.message);
        if (fail_on_no_perf) {
            expect(e).toBeUndefined();
        }
        done();
    };
}
