// ========================================================= //
//                                                           //
//   File      : arb_sync.cxx                                //
//   Purpose   : synchronize processes via arb database      //
//                                                           //
//   Coded by Ralf Westram (coder@reallysoft.de) in Sep 25   //
//   http://www.arb-home.de/                                 //
//                                                           //
// ========================================================= //

#include <algorithm>

#include <arbdbt.h>
#include <arb_sleep.h>

static void report_error(GB_ERROR err) {
    fprintf(stderr, "Error running arb_sync: %s\n", err);
}

static int exitcodeFrom(GB_ERROR error) {
    if (error) {
        report_error(error);
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

static int arb_sync_write(const char *ID, const char *value) {
    GB_shell  shell;
    GBDATA   *gb_main = GB_open(":", "r");
    GB_ERROR  error   = NULp;

    if (gb_main) {
        GB_transaction ta(gb_main);
        error = GB_write_sync_value(gb_main, ID, value);
        ta.close(error);

        GB_close(gb_main);
    }
    else {
        error = GB_await_error();
    }

    return exitcodeFrom(error);
}
static int arb_sync_wait(const char *ID, const char *wantedValue) {
    GB_shell  shell;
    GBDATA   *gb_main = GB_open(":", "r");
    GB_ERROR  error   = NULp;

    if (gb_main) {
        bool       value_reached = false;
        const long MIN_DELAY     = 250;
        const long MAX_DELAY     = 3500;
        long       loop          = 0;

        while (!value_reached) {
            const char *currentValue = NULp;
            {
                GB_transaction ta(gb_main);
                currentValue = GB_read_sync_value(gb_main, ID);
                if (!currentValue) {
                    error = GB_await_error();
                    fprintf(stderr, "Error running arb_sync: failed to GB_read_sync_value: %s (will retry)\n", error);
                }
            }

            long delay_ms = 0; // ms to sleep
            if (currentValue) {
                if (strcmp(currentValue, wantedValue) == 0) {
                    fprintf(stderr, "Sync '%s' reached wanted value '%s'\n", ID, currentValue);
                    delay_ms      = 0;
                    value_reached = true;
                }
                else {
                    fprintf(stderr, "Sync '%s' has value '%s' (waiting for '%s')\n", ID, currentValue, wantedValue);
                    delay_ms = std::min(MAX_DELAY, loop * 77 + MIN_DELAY);
                }
            }
            else {
                delay_ms = 1554;
            }

            ARB_sleep(delay_ms, MS);
            loop++;
        }

        GB_close(gb_main);
    }
    else {
        error = GB_await_error();
    }

    return exitcodeFrom(error);
}

static int show_usage(const char *error) {
    const char *usage =
        "Usage:   arb_sync <modeflag> <ID> <value>\n"
        "Purpose: Use the running arb database to synchronise processes.\n"
        "<ID>::= a unique identifier for synchronisation\n"
        "<value>::= used value\n"
        "<modeflag>::=\n"
        "    --write         store specified value for ID\n"
        "    --wait          wait until ID reaches specified value\n"
        ;
    fputs(usage, stderr);

    if (error) {
        fprintf(stderr, "Error in arb_sync: %s\n", error);
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

static bool requests_help(const char *arg) {
    return
        strcmp(arg, "--help") == 0 ||
        strcmp(arg, "-h") == 0;
}

int ARB_main(int argc, char *argv[]) {
    int exitcode = EXIT_SUCCESS;

    const char *mode  = NULp;
    const char *ID    = NULp;
    const char *value = NULp;

    while (argc>1) {
        const char *arg = argv[1];

        if      (!mode)  mode = arg;
        else if (!ID)    ID = arg;
        else if (!value) value = arg;
        else             exitcode = show_usage(GBS_global_string("too many arguments (%s)", arg));

        argc--;argv++;
    }

    if      (!mode)               exitcode = show_usage("missing argument <modeflag>");
    else if (requests_help(mode)) exitcode = show_usage(NULp);
    else if (!ID)                 exitcode = show_usage("missing argument <ID>");
    else if (!value)              exitcode = show_usage("missing argument <value>");
    else {
        if      (strcmp(mode, "--write") == 0) exitcode = arb_sync_write(ID, value);
        else if (strcmp(mode, "--wait")  == 0) exitcode = arb_sync_wait(ID, value);
        else                                   exitcode = show_usage(GBS_global_string("Unknown <modeflag> '%s'", mode));
    }

    return exitcode;
}


