/* Test bread_backwards to make sure it can read backwards even for large files. */

#include <toku_portability.h>

#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <zlib.h>

#include "../brttypes.h"
#include "../bread.h"
#include "test.h"

#define FNAME "test1305.data"

// THe buffer size in units of 64-bit integers.
#define N_BIGINTS (1<<20)
#define BIGINT_SIZE (sizeof(u_int64_t))
// How big is the readback buffer (in 8-bit integers)?
#define READBACK_BUFSIZE (1<<20)


static void
test (u_int64_t fsize) {
    unlink(FNAME);
    // Create a file of size fsize.  Fill it with 8-byte values which are integers, in order)
    assert(fsize%(N_BIGINTS*sizeof(u_int64_t)) == 0); // Make sure the fsize is a multiple of the buffer size.
    u_int64_t i = 0;
    {
	int fd = open(FNAME, O_CREAT+O_RDWR+O_BINARY, 0777);
	assert(fd>=0);
	static u_int64_t buf[N_BIGINTS]; //windows cannot handle this on the stack
	static char compressed_buf[N_BIGINTS*2 + 1000]; // this is more than compressbound returns
	uLongf compressed_len;
	while (i*BIGINT_SIZE < fsize) {
            if (verbose>0 && i % (1<<25) == 0) {
                printf("   %s:test (%"PRIu64") forwards [%"PRIu64"%%]\n", __FILE__, fsize, 100*BIGINT_SIZE*((u_int64_t)i) / fsize);
                fflush(stdout);
            }

	    int j;
	    for (j=0; j<N_BIGINTS; j++) {
		buf[j] = i++;
	    }
assert(sizeof(buf) == N_BIGINTS * BIGINT_SIZE);
	    {
		compressed_len = sizeof(compressed_buf);
		int r = compress2((Bytef*)compressed_buf, &compressed_len, (Bytef*)buf, sizeof(buf), 1);
		assert(r==Z_OK);
	    }
	    {
		u_int32_t v = toku_htod32(compressed_len);
		ssize_t r = write(fd, &v, sizeof(v));
		assert(r==sizeof(v));
	    }
	    {
		ssize_t r = write(fd, compressed_buf, compressed_len);
		assert(r==(ssize_t)compressed_len);
	    }
	    {
		u_int32_t v = toku_htod32(sizeof(buf));
		ssize_t r = write(fd, &v, sizeof(v));
		assert(r==sizeof(v));
	    }
	    {
		u_int32_t v = toku_htod32(compressed_len);
		ssize_t r = write(fd, &v, sizeof(v));
		assert(r==sizeof(v));
	    }
	}
	{ int r = close(fd); assert(r==0); }
    }
    assert(i*BIGINT_SIZE == fsize);
    // Now read it all backward
    {
	int fd = open(FNAME, O_RDONLY+O_BINARY);  	assert(fd>=0);
	BREAD br = create_bread_from_fd_initialize_at(fd);
	while (bread_has_more(br)) {
            if (verbose>0 && (fsize/BIGINT_SIZE - i) % (1<<25) == 0) {
                printf("   %s:test (%"PRIu64") backwards [%"PRIu64"%%]\n", __FILE__, fsize, 100*BIGINT_SIZE*((u_int64_t)i) / fsize);
                fflush(stdout);
            }
	    assert(i>0);
	    i--;
	    u_int64_t storedi;
	    { int r = bread_backwards(br, &storedi, sizeof(storedi)); assert(r==sizeof(storedi)); }
	    assert(storedi==i);
	}
	assert(i==0);
	{ int r=close_bread_without_closing_fd(br); assert(r==0); }
	{ int r=close(fd); assert(r==0); }
    }
    //printf("Did %" PRIu64 "\n", fsize);
    //system("ls -l " FNAME);
    unlink(FNAME);
}

int
test_main (int argc, const char *argv[]) {
    default_parse_args(argc, argv);
    test(1LL<<23);
    test(1LL<<30);
    test(1LL<<31);
    test(1LL<<32);
    test(1LL<<33);
    return 0;
}