/* Worst-case insert patterns. */

#include "gpma.h"
#include "toku_assert.h"
#include "memory.h"
#include <string.h>
#include <stdio.h>

int verbose;

static int count_frees=0;
static void free_callback (u_int32_t len __attribute__((__unused__)), void*freeme, void *extra) {
    assert(extra==(void*)&verbose);
    toku_free(freeme);
}

static int compare_strings(u_int32_t alen, void *aval, u_int32_t blen, void *bval, void *extra __attribute__((__unused__))) {
    assert(alen==strlen(aval)+1);
    assert(blen==strlen(bval)+1);
    return strcmp(aval, bval);
}

static int rcall_ok (u_int32_t nitems __attribute__((__unused__)), u_int32_t *froms __attribute__((__unused__)), u_int32_t *tos __attribute__((__unused__)),  struct gitem *items __attribute__((__unused__)), u_int32_t old_N __attribute__((__unused__)), u_int32_t new_N __attribute__((__unused__)), void *extra  __attribute__((__unused__))) {
    return 0;
}

static int delete_callback (u_int32_t slotnum __attribute__((__unused__)), u_int32_t len, void *data, void *extra) {
    assert(strlen(data)+1==len);
    assert(strcmp(data, extra)==0);
    toku_free(data);
    return 0;
}

static const int initial_N=1000;
static const int N=100000;
static const int w=6;

static void insert_n (GPMA pma, int n) {
    char buf[w+1];
    int l = snprintf(buf, sizeof(buf), "%0*d", w, n);
    assert(l==w);
    int r = toku_gpma_insert(pma, strlen(buf)+1, strdup(buf), compare_strings, 0, rcall_ok, 0, 0);
    assert(r==0);
}

static void delete_n (GPMA pma, int n) {
    char buf[w+1];
    int l = snprintf(buf, sizeof(buf), "%0*d", w, n);
    assert(l==w);
    int r = toku_gpma_delete_item(pma,
				  strlen(buf)+1, buf,
				  compare_strings, 0,
				  delete_callback, buf,
				  0, 0);
    if (r!=0) printf("deleted %d\n", n);
    assert(r==0);
}

static int inum (int direction, int itemnum) {
    switch (direction) {
    case 1:
	// Insert things from left to right
	return itemnum;
    case -1:
	// Insert things from right to left
	return 2*N-1-itemnum;
    case 0:
	// Insert things at the outer edges
	if (itemnum%2) {
	    return itemnum/2;
	} else {
	    return 2*N-1-itemnum/2;
	}
    default: assert(0); return 0;
    }
}

static void test_worst_insert(int direction) {
    int r;
    GPMA pma;
    r = toku_gpma_create(&pma, 0);
    assert(r==0);
    count_frees=0;
    int i;
    int next_to_insert=0;
    int next_to_delete=0;
    int max_size = 0;
    for (i=0; i<initial_N; i++) {
	insert_n(pma, inum(direction,next_to_insert++));
    }
    for (; i<N; i++) {
	insert_n(pma, inum(direction,next_to_insert++));
	if (i%10==0) continue; // Make the table get slowly larger
	delete_n(pma, inum(direction, next_to_delete++));
    }
    for (; i<2*N; i++) {
	int this_size = toku_gpma_index_limit(pma);
	if (this_size>max_size) max_size=this_size;
	delete_n(pma, inum(direction,next_to_delete++));
	if (i%20==0) continue; // Make the table get slowly smaller
	insert_n(pma, inum(direction,next_to_insert++));
    }
    assert(count_frees==0);
    if (verbose) printf("size=%d max_size=%d\n", toku_gpma_index_limit(pma), max_size);
    toku_gpma_free(&pma, free_callback, &verbose);
}

int main (int argc, const char *argv[]) {
    int i;
    int which = 0;
    for (i = 1; i < argc; i++) {
        const char *arg = argv[i];
        if (0 == strcmp(arg, "-v") || 0 == strcmp(arg, "--verbose"))
            verbose = 1;
        else if (0 == strcmp(arg, "-q") || 0 == strcmp(arg, "--quiet"))
            verbose = 0;
	else if (0 == strcmp(arg, "-a"))
	    which = 1;
	else if (0 == strcmp(arg, "-b"))
	    which = 2;	    
	else if (0 == strcmp(arg, "-c"))
	    which = 3;
    }
    if (which==0 || which==1) test_worst_insert(+1);
    if (which==0 || which==2) test_worst_insert(-1);
    if (which==0 || which==3) test_worst_insert( 0);
    return 0;
}