#include "../test.h"

#include <aerospike/as_arraylist.h>
#include <aerospike/as_arraylist_iterator.h>
#include <aerospike/as_integer.h>
#include <aerospike/as_list.h>
#include <aerospike/as_list_iterator.h>
#include <aerospike/as_msgpack.h>
#include <aerospike/as_serializer.h>
#include <string.h>

/******************************************************************************
 * TEST CASES
 *****************************************************************************/

TEST(types_arraylist_empty, "as_arraylist is empty")
{
	as_arraylist l;
	as_arraylist_init(&l,0,0);
	assert(as_list_size((as_list *) &l) == 0);
	as_list_destroy((as_list *) &l);
}

TEST(types_arraylist_cap0, "as_arraylist initial capacity zero")
{
	as_arraylist list;
	as_arraylist_init(&list, 0, 8);
	as_arraylist_append_int64(&list, 3);

	int64_t val = as_arraylist_get_int64(&list, 0);
	assert_int_eq(val, 3);

	as_arraylist_destroy(&list);
}

TEST(types_arraylist_cap10_blk0, "as_arraylist w/ capacity 10, block_size 0")
{
	int rc = 0;

	as_arraylist l;
	as_arraylist_init(&l,10,0);

	assert_int_eq(l.capacity, 10);
	assert_int_eq(l.block_size, 0);
	assert_int_eq(l.size, 0);

	assert(l.size == as_list_size((as_list *) &l));
	assert_int_eq(l.size, 0);

	for (int i = 1; i < 6; i++) {
		rc = as_arraylist_append_integer(&l, as_integer_new(i));
		assert_int_eq(rc, AS_ARRAYLIST_OK);
		assert(l.size == as_arraylist_size(&l));
		assert_int_eq(l.size, i);
		assert_int_eq(l.capacity, 10);
	}

	for (int i = 6; i < 11; i++) {
		rc = as_arraylist_append_integer(&l, as_integer_new(i));
		assert_int_eq(rc, AS_ARRAYLIST_OK);
		assert(l.size == as_arraylist_size(&l));
		assert_int_eq(l.size, i);
		assert_int_eq(l.capacity, 10);
	}

	as_integer * i11 = as_integer_new(11);

	rc = as_arraylist_append_integer(&l, i11);
	assert_int_ne(rc, AS_ARRAYLIST_OK);
	assert(l.size == as_arraylist_size(&l));
	assert_int_eq(l.size, 10);
	assert_int_eq(l.capacity, 10);

	as_integer * i12 = as_integer_new(12);

	rc = as_arraylist_append_integer(&l, i12);
	assert_int_ne(rc, AS_ARRAYLIST_OK);
	assert(l.size == as_arraylist_size(&l));
	assert_int_eq(l.size, 10);
	assert_int_eq(l.capacity, 10);
	
	as_arraylist_destroy(&l);
	as_integer_destroy(i11);
	as_integer_destroy(i12);
}

TEST(types_arraylist_cap10_blk10, "as_arraylist w/ capacity 10, block_size 10")
{
	int rc = 0;

	as_arraylist l;
	as_arraylist_init(&l,10,10);

	assert_int_eq(l.capacity, 10);
	assert_int_eq(l.block_size, 10);
	assert_int_eq(l.size, 0);

	assert(l.size == as_list_size((as_list *) &l));
	assert_int_eq(l.size, 0);

	for (int i = 1; i < 6; i++) {
		rc = as_arraylist_append(&l, (as_val *) as_integer_new(i));
		assert_int_eq(rc, AS_ARRAYLIST_OK);
		assert(l.size == as_list_size((as_list *) &l));
		assert_int_eq(l.size, i);
		assert_int_eq(l.capacity, 10);
	}

	for (int i = 6; i < 11; i++) {
		rc = as_arraylist_append(&l, (as_val *) as_integer_new(i));
		assert_int_eq(rc, AS_ARRAYLIST_OK);
		assert(l.size == as_list_size((as_list *) &l));
		assert_int_eq(l.size, i);
		assert_int_eq(l.capacity, 10);
	}

	rc = as_arraylist_append(&l, (as_val *) as_integer_new(11));
	assert_int_eq(rc, AS_ARRAYLIST_OK);
	assert(l.size == as_list_size((as_list *) &l));
	assert_int_eq(l.size, 11);
	assert_int_eq(l.capacity, 20);

	rc = as_arraylist_append(&l, (as_val *) as_integer_new(12));
	assert_int_eq(rc, AS_ARRAYLIST_OK);
	assert(l.size == as_list_size((as_list *) &l));
	assert_int_eq(l.size, 12);
	assert_int_eq(l.capacity, 20);
	
	as_arraylist_destroy(&l);

}

TEST(types_arraylist_1, "as_arraylist w/ as_arraylist ops")
{
	int rc = 0;

	as_arraylist l;
	as_arraylist_init((as_arraylist *) &l,10,10);

	assert_int_eq(l.capacity, 10);
	assert_int_eq(l.block_size, 10);
	assert_int_eq(l.size, 0);

	assert(l.size == as_arraylist_size(&l));
	assert_int_eq(l.size, 0);

	for (int i = 1; i < 6; i++) {
		rc = as_arraylist_append(&l, (as_val *) as_integer_new(i));
		assert_int_eq(rc, AS_ARRAYLIST_OK);
		assert(l.size == as_arraylist_size(&l));
		assert_int_eq(l.size, i);
		assert_int_eq(l.capacity, 10);
	}
	// list is now: 1 2 3 4 5

	for (int i = 6; i < 11; i++) {
		rc = as_arraylist_prepend(&l, (as_val *) as_integer_new(i));
		assert_int_eq(rc, AS_ARRAYLIST_OK);
		assert(l.size == as_arraylist_size(&l));
		assert_int_eq(l.size, i);
		assert_int_eq(l.capacity, 10);
	}
	// list is now: 10 9 8 7 6 1 2 3 4 5
	// indexes:	  0 1 2 3 4 5 6 7 8 9

	assert_int_eq(9, as_integer_toint((as_integer *) as_arraylist_get(&l, 1)));
	assert_int_eq(2, as_integer_toint((as_integer *) as_arraylist_get(&l, 6)));
	assert(NULL == as_arraylist_get(&l,10)); // off end is safe
		
	as_arraylist * t = as_arraylist_take(&l, 5); // take first five
	assert_int_eq(as_arraylist_size(t), 5);

	assert_int_eq(10, as_integer_toint((as_integer *) as_arraylist_get(t,0)));
	assert_int_eq(6, as_integer_toint((as_integer *) as_arraylist_get(t,4)));

	as_integer * t_head = (as_integer *) as_arraylist_head(t);
	as_integer * l_head = (as_integer *) as_arraylist_head(&l);

	assert(as_integer_toint(t_head) == as_integer_toint(l_head));
	
	as_arraylist_destroy(t);
	t = 0;

	as_arraylist * d = as_arraylist_drop(&l, 5); // drop returns list from 5 forward, does not change l
	assert_int_eq(as_arraylist_size(d), 5);
	assert_int_eq(as_arraylist_size(&l), 10);

	assert_int_eq(2, as_integer_toint((as_integer *) as_arraylist_get(d,1)));

	as_integer * d_0 = (as_integer *) as_arraylist_get(d, 0); // should be 1
	as_integer * l_5 = (as_integer *) as_arraylist_get(&l, 5); // 5 from 

	assert(as_integer_toint(d_0) == as_integer_toint(l_5));

	as_arraylist_destroy(d);

	rc = as_arraylist_insert(&l, 5, (as_val *) as_integer_new(99));
	assert_int_eq(rc, AS_ARRAYLIST_OK);
	assert_int_eq(l.size, 11);
	assert_int_eq(6, as_integer_toint((as_integer *) as_arraylist_get(&l, 4)));
	assert_int_eq(99, as_integer_toint((as_integer *) as_arraylist_get(&l, 5)));
	assert_int_eq(1, as_integer_toint((as_integer *) as_arraylist_get(&l, 6)));
	assert_int_eq(5, as_integer_toint((as_integer *) as_arraylist_get(&l, 10)));
	// list is now: 10 9 8 7 6 99 1 2 3 4  5
	// indexes:	  0 1 2 3 4  5 6 7 8 9 10

	rc = as_arraylist_remove(&l, 11);
	assert_int_eq(rc, AS_ARRAYLIST_ERR_INDEX);
	rc = as_arraylist_remove(&l, 4);
	assert_int_eq(rc, AS_ARRAYLIST_OK);
	assert_int_eq(l.size, 10);
	assert_int_eq(7, as_integer_toint((as_integer *) as_arraylist_get(&l, 3)));
	assert_int_eq(99, as_integer_toint((as_integer *) as_arraylist_get(&l, 4)));
	assert_int_eq(1, as_integer_toint((as_integer *) as_arraylist_get(&l, 5)));
	assert_int_eq(5, as_integer_toint((as_integer *) as_arraylist_get(&l, 9)));
	// list is now: 10 9 8 7 99 1 2 3 4 5
	// indexes:	  0 1 2 3  4 5 6 7 8 9

	as_arraylist c;
	as_arraylist_init((as_arraylist *) &c, 10, 10);

	for (int i = 1; i < 4; i++) {
		rc = as_arraylist_append(&c, (as_val *) as_integer_new(100 + i));
		assert_int_eq(rc, AS_ARRAYLIST_OK);
	}

	assert_int_eq(c.size, 3);
	rc = as_arraylist_concat(&l, &c);
	assert_int_eq(rc, AS_ARRAYLIST_OK);
	assert_int_eq(l.size, 13);
	assert_int_eq(5, as_integer_toint((as_integer *) as_arraylist_get(&l, 9)));
	assert_int_eq(101, as_integer_toint((as_integer *) as_arraylist_get(&l, 10)));
	assert_int_eq(103, as_integer_toint((as_integer *) as_arraylist_get(&l, 12)));
	// list is now: 10 9 8 7 99 1 2 3 4 5 101 102 103
	// indexes:	  0 1 2 3  4 5 6 7 8 9  10  11  12

	as_arraylist_destroy(&c);

	rc = as_arraylist_trim(&l, 13);
	assert_int_eq(rc, AS_ARRAYLIST_ERR_INDEX);
	rc = as_arraylist_trim(&l, 7);
	assert_int_eq(rc, AS_ARRAYLIST_OK);
	assert_int_eq(l.size, 7);
	assert_int_eq(2, as_integer_toint((as_integer *) as_arraylist_get(&l, 6)));
	// list is now: 10 9 8 7 99 1 2
	// indexes:	  0 1 2 3  4 5 6

	as_arraylist_destroy(&l);
}

TEST(types_arraylist_list, "as_arraylist w/ as_list ops")
{
	int rc = 0;

	as_arraylist l;
	as_arraylist_init((as_arraylist *) &l,10,10);

	assert_int_eq(l.capacity, 10);
	assert_int_eq(l.block_size, 10);
	assert_int_eq(l.size, 0);

	assert(l.size == as_list_size((as_list *) &l));
	assert_int_eq(l.size, 0);

	for (int i = 1; i < 6; i++) {
		rc = as_list_append((as_list *) &l, (as_val *) as_integer_new(i));
		assert_int_eq(rc, AS_ARRAYLIST_OK);
		assert(l.size == as_list_size((as_list *) &l));
		assert_int_eq(l.size, i);
		assert_int_eq(l.capacity, 10);
	}
	// list is now: 1 2 3 4 5

	for (int i = 6; i < 11; i++) {
		rc = as_list_prepend((as_list *) &l, (as_val *) as_integer_new(i));
		assert_int_eq(rc, AS_ARRAYLIST_OK);
		assert(l.size == as_list_size((as_list *) &l));
		assert_int_eq(l.size, i);
		assert_int_eq(l.capacity, 10);
	}
	// list is now: 10 9 8 7 6 1 2 3 4 5
	// indexes:	  0 1 2 3 4 5 6 7 8 9

	assert_int_eq(9, as_integer_toint((as_integer *) as_list_get((as_list *) &l, 1)));
	assert_int_eq(2, as_integer_toint((as_integer *) as_list_get((as_list *) &l, 6)));
	assert(NULL == as_list_get((as_list *) &l,10)); // off end is safe
		
	as_list * t = as_list_take((as_list *) &l, 5); // take first five
	assert_int_eq(as_list_size((as_list *) t), 5);

	assert_int_eq(10, as_integer_toint((as_integer *) as_list_get((as_list *) t,0)));
	assert_int_eq(6, as_integer_toint((as_integer *) as_list_get((as_list *) t,4)));

	as_integer * t_head = (as_integer *) as_list_head((as_list *) t);
	as_integer * l_head = (as_integer *) as_list_head((as_list *) &l);

	assert(as_integer_toint(t_head) == as_integer_toint(l_head));
	
	as_list_destroy((as_list *) t);
	t = 0;

	as_list * d = as_list_drop((as_list *) &l, 5); // drop returns list from 5 forward, does not change l
	assert_int_eq(as_list_size((as_list *) d), 5);
	assert_int_eq(as_list_size((as_list *) &l), 10);

	assert_int_eq(2, as_integer_toint((as_integer *) as_list_get((as_list *) d,1)));

	as_integer * d_0 = (as_integer *) as_list_get((as_list *) d, 0); // should be 1
	as_integer * l_5 = (as_integer *) as_list_get((as_list *) &l, 5); // 5 from 

	assert(as_integer_toint(d_0) == as_integer_toint(l_5));

	as_list_destroy((as_list *) d);

	rc = as_list_insert((as_list *) &l, 5, (as_val *) as_integer_new(99));
	assert_int_eq(rc, AS_ARRAYLIST_OK);
	assert_int_eq(l.size, 11);
	assert_int_eq(6, as_integer_toint((as_integer *) as_list_get((as_list *) &l, 4)));
	assert_int_eq(99, as_integer_toint((as_integer *) as_list_get((as_list *) &l, 5)));
	assert_int_eq(1, as_integer_toint((as_integer *) as_list_get((as_list *) &l, 6)));
	assert_int_eq(5, as_integer_toint((as_integer *) as_list_get((as_list *) &l, 10)));
	// list is now: 10 9 8 7 6 99 1 2 3 4  5
	// indexes:	  0 1 2 3 4  5 6 7 8 9 10

	rc = as_list_remove((as_list *) &l, 11);
	assert_int_eq(rc, AS_ARRAYLIST_ERR_INDEX);
	rc = as_list_remove((as_list *) &l, 4);
	assert_int_eq(rc, AS_ARRAYLIST_OK);
	assert_int_eq(l.size, 10);
	assert_int_eq(7, as_integer_toint((as_integer *) as_list_get((as_list *) &l, 3)));
	assert_int_eq(99, as_integer_toint((as_integer *) as_list_get((as_list *) &l, 4)));
	assert_int_eq(1, as_integer_toint((as_integer *) as_list_get((as_list *) &l, 5)));
	assert_int_eq(5, as_integer_toint((as_integer *) as_list_get((as_list *) &l, 9)));
	// list is now: 10 9 8 7 99 1 2 3 4 5
	// indexes:	  0 1 2 3  4 5 6 7 8 9

	as_arraylist c;
	as_arraylist_init(&c, 10, 10);

	for (int i = 1; i < 4; i++) {
		rc = as_list_append((as_list *) &c, (as_val *) as_integer_new(100 + i));
		assert_int_eq(rc, AS_ARRAYLIST_OK);
	}

	assert_int_eq(c.size, 3);
	rc = as_list_concat((as_list *) &l, (const as_list *) &c);
	assert_int_eq(rc, AS_ARRAYLIST_OK);
	assert_int_eq(l.size, 13);
	assert_int_eq(5, as_integer_toint((as_integer *) as_list_get((as_list *) &l, 9)));
	assert_int_eq(101, as_integer_toint((as_integer *) as_list_get((as_list *) &l, 10)));
	assert_int_eq(103, as_integer_toint((as_integer *) as_list_get((as_list *) &l, 12)));
	// list is now: 10 9 8 7 99 1 2 3 4 5 101 102 103
	// indexes:	  0 1 2 3  4 5 6 7 8 9  10  11  12

	as_arraylist_destroy(&c);

	rc = as_list_trim((as_list *) &l, 13);
	assert_int_eq(rc, AS_ARRAYLIST_ERR_INDEX);
	rc = as_list_trim((as_list *) &l, 7);
	assert_int_eq(rc, AS_ARRAYLIST_OK);
	assert_int_eq(l.size, 7);
	assert_int_eq(2, as_integer_toint((as_integer *) as_list_get((as_list *) &l, 6)));
	// list is now: 10 9 8 7 99 1 2
	// indexes:	  0 1 2 3  4 5 6

	as_list_destroy((as_list *) &l);
}

TEST(types_arraylist_iterator, "as_arraylist w/ as_iterator ops")
{
	as_arraylist l;
	as_arraylist_init(&l,10,10);
	
	for (int i = 1; i < 6; i++) {
		as_list_append((as_list *) &l, (as_val *) as_integer_new(i));
	}

	assert_int_eq(as_list_size((as_list *) &l), 5);

	as_iterator * i = (as_iterator *) as_arraylist_iterator_new(&l);

	as_integer * v = NULL;

	assert_true(as_iterator_has_next(i));

	v = (as_integer *) as_iterator_next(i);
	assert_int_eq(as_integer_toint(v), 1);

	v = (as_integer *) as_iterator_next(i);
	assert_int_eq(as_integer_toint(v), 2);

	v = (as_integer *) as_iterator_next(i);
	assert_int_eq(as_integer_toint(v), 3);

	v = (as_integer *) as_iterator_next(i);
	assert_int_eq(as_integer_toint(v), 4);

	v = (as_integer *) as_iterator_next(i);
	assert_int_eq(as_integer_toint(v), 5);

	assert_false(as_iterator_has_next(i));

	as_iterator_destroy(i);

	as_arraylist_destroy(&l);
}

TEST(types_arraylist_msgpack, "as_arraylist msgpack")
{
	as_arraylist l1;
	as_arraylist_init(&l1,10,0);
	as_arraylist_append_int64(&l1, 123);
	as_arraylist_append_int64(&l1, 456);
	as_arraylist_append_int64(&l1, 789);
	as_arraylist_append_double(&l1, 92.1);
	as_arraylist_append_double(&l1, -23.596);
	as_arraylist_append_str(&l1, "abc");
	as_arraylist_append_str(&l1, "def");
	as_arraylist_append_str(&l1, "ghi");
	
	assert_int_eq(as_arraylist_size(&l1), 8);

	as_serializer ser;
	as_msgpack_init(&ser);

	as_buffer b;
	as_buffer_init(&b);

	as_serializer_serialize(&ser, (as_val *) &l1, &b);

	as_val * v2 = NULL;

	as_serializer_deserialize(&ser, &b, &v2);

	assert_not_null(v2);
	assert_int_eq(as_val_type(v2), AS_LIST);

	as_list * l2 = as_list_fromval(v2);

	assert_int_eq(as_arraylist_size(&l1), as_list_size(l2));
	assert_int_eq(as_arraylist_get_int64(&l1, 0), 123);
	assert_int_eq(as_arraylist_get_int64(&l1, 1), 456);
	assert_int_eq(as_arraylist_get_int64(&l1, 2), 789);
	assert_double_eq(as_arraylist_get_double(&l1, 3), 92.1);
	assert_double_eq(as_arraylist_get_double(&l1, 4), -23.596);
	assert_string_eq(as_arraylist_get_str(&l1, 5), "abc");
	assert_string_eq(as_arraylist_get_str(&l1, 6), "def");
	assert_string_eq(as_arraylist_get_str(&l1, 7), "ghi");

	as_arraylist_destroy(&l1);
	
	as_list_destroy(l2);
	
	as_buffer_destroy(&b);
}

/******************************************************************************
 * TEST SUITE
 *****************************************************************************/

SUITE(types_arraylist, "as_arraylist")
{
	suite_add(types_arraylist_empty);
	suite_add(types_arraylist_cap0);
	suite_add(types_arraylist_cap10_blk0);
	suite_add(types_arraylist_cap10_blk10);
	suite_add(types_arraylist_1);
	suite_add(types_arraylist_list);
	suite_add(types_arraylist_iterator);
	suite_add(types_arraylist_msgpack);
}
