#include <assert.h>

#ifdef __needExternC
#error Only compile observation.c (once) in plain C
#endif

#include <stdio.h>
#include <sys/param.h>

#include "observation.h"

static int NumNodes;

void bobs_init(int NumNodes_) {
    // printf("at bobs_init; bobs_ops_completed = %zd\n", bobs_ops_completed);

    // todo: register a signal handler that prints progress info
    // note function bobs_XXX in driver.c are untested until that's working
    // to be driven as:
    //       ./a.out &          # then, very quickly
    //       kill -s USR2 $!

    NumNodes = NumNodes_;
}

#ifdef BOBS_SHOW_ADDRESSES
#define SNAP \
    void * userAddr = bobs_getCurrentLoc(); \
    int userValue = bobs_getCurrentVal();
#define SHOW(pfx, sfx) \
    printf(" " pfx "%d@%p" sfx, userValue, userAddr);
#else
#define SNAP \
    int userValue = bobs_getCurrentVal();
#define SHOW(pfx, sfx) \
    printf(" " pfx "%d" sfx, userValue);
#endif

static void printPreds(unsigned int leash) {
    if (leash==0)
        return;
    if (!bobs_hasCurrent()) {
        printf(" |");
        return;
    }
    SNAP
    bobs_movePrev();
    printPreds(leash - 1);
    SHOW("", "")
}
static void printSuccs(unsigned int leash) {
    if (leash==0)
        return;
    if (!bobs_hasCurrent()) {
        printf(" |");
        return;
    }
    SNAP
    SHOW("", "")
    bobs_moveNext();
    printSuccs(leash - 1);
}
static void explore(int here) {
    bobs_seek(here);
    if (!bobs_hasCurrent()) {
        printf(" <X>");
        return;
    }
    bobs_movePrev();
    printPreds(2);

    bobs_seek(here);
    SNAP
    SHOW("<", ">")

    bobs_moveNext();
    printSuccs(2);
}
static void exploreRange(int validFrom, int validTo) {
    int gapsize = validTo - validFrom;
    int midpoint = (validTo + validFrom) / 2;

    int listFirstmost, listLastmost;
    switch(bobs_op_polarity) {
        case insfirst:
            listFirstmost = validTo;
            listLastmost = validFrom;
            break;
        case inslast:
            listFirstmost = validFrom;
            listLastmost = validTo;
            break;
        default:
            assert(0 && "unsupported bobs_op_movement value");
    }

    explore(listFirstmost);
    printf(" ...");
    if (gapsize > 5) {
        explore(midpoint);
        printf(" ...");
    }
    explore(listLastmost);
}

void bobs_report(void) {
    if (bobs_prog_rollover_flag) {
        printf("%8zd + ? (rolling over)\n", bobs_ops_completed);
    } else {
        printf("%8zd + %6d/2 + %6d/2", bobs_ops_completed, bobs_prog_inserting, bobs_prog_removing);

        int validFrom, validTo;
        switch(bobs_op_movement) {
            case stack:
                validFrom = 0;
                validTo = MIN((signed)bobs_prog_inserting-1, (signed)NumNodes - (signed)bobs_prog_removing - 1);
                break;
            case queue:
                validFrom = (signed)bobs_prog_removing;
                validTo = (signed)bobs_prog_inserting-1;
                break;
            default:
                assert(0 && "unsupported bobs_op_movement value");
        }

        printf("  ");

        if (validTo < validFrom) {
            printf(" (list is empty)");
        } else {
            exploreRange(validFrom, validTo);
        }

        printf("\n");
    }
}
