/*
 * Copyright (c) 2018-2019 SignalWire, Inc
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "libks/ks.h"
#include "tap.h"

int test1(void)
{
	ks_pool_t *pool;
	ks_hash_t *hash;
	int i, sum1 = 0, sum2 = 0;

	ks_pool_open(&pool);
	ks_hash_create(&hash, KS_HASH_MODE_DEFAULT, KS_HASH_FREE_BOTH | KS_HASH_FLAG_RWLOCK, pool);

	for (i = 1; i < 1001; i++) {
		char *key = ks_pprintf(pool, "KEY %d", i);
		char *val = ks_pprintf(pool, "%d", i);
		ks_hash_insert(hash, key, val);
		sum1 += i;
	}



	ks_hash_iterator_t *itt;

	ks_hash_write_lock(hash);
	for (itt = ks_hash_first(hash, KS_UNLOCKED); itt; ) {
		const void *key;
		void *val;

		ks_hash_this(itt, &key, NULL, &val);

		printf("%s=%s\n", (char *)key, (char *)val);
		sum2 += atoi(val);

		itt = ks_hash_next(&itt);
		ks_hash_remove(hash, (char *)key);
	}
	ks_hash_write_unlock(hash);

	ks_hash_destroy(&hash);

	ks_pool_close(&pool);

	return (sum1 == sum2);
}

#define MAX 100

static void *test2_thread(ks_thread_t *thread, void *data)
{
	ks_hash_iterator_t *itt;
	ks_hash_t *hash = (ks_hash_t *) data;

	while(!ks_thread_stop_requested(thread)) {
		for (itt = ks_hash_first(hash, KS_READLOCKED); itt; itt = ks_hash_next(&itt)) {
			const void *key;
			void *val;

			ks_hash_this(itt, &key, NULL, &val);

			printf("%u ITT %s=%s\n", (int)ks_thread_self_id(), (char *)key, (char *)val);
		}
		ks_sleep(100000);
	}


	return NULL;
}

int test2(void)
{
	ks_thread_t *threads[MAX];
	int ttl = 5;
	int runs = 5;
	ks_pool_t *pool;
	ks_hash_t *hash;
	int i;
	ks_hash_iterator_t *itt;

	ks_pool_open(&pool);
	ks_hash_create(&hash, KS_HASH_MODE_DEFAULT, KS_HASH_FREE_BOTH | KS_HASH_FLAG_RWLOCK, pool);

	for (i = 0; i < ttl; i++) {
		ks_thread_create(&threads[i], test2_thread, hash, pool);
	}

	for(i = 0; i < runs; i++) {
		int x = rand() % 5;
		int j;

		for (j = 0; j < 100; j++) {
			char *key = ks_pprintf(pool, "KEY %d", j);
			char *val = ks_pprintf(pool, "%d", j);
			ks_hash_insert(hash, key, val);
		}

		ks_sleep(x * 1000000);

		ks_hash_write_lock(hash);
		for (itt = ks_hash_first(hash, KS_UNLOCKED); itt; ) {
			const void *key;
			void *val;

			ks_hash_this(itt, &key, NULL, &val);

			printf("DEL %s=%s\n", (char *)key, (char *)val);

			itt = ks_hash_next(&itt);
			ks_hash_remove(hash, (char *)key);
		}
		ks_hash_write_unlock(hash);

	}

	for (i = 0; i < ttl; i++) {
		ks_thread_destroy(&threads[i]);
	}


	ks_hash_destroy(&hash);
	ks_pool_close(&pool);

	return 1;
}

#define TEST3_SIZE 20
int test3(void)
{
	ks_pool_t *pool;
	ks_hash_t *hash;
	ks_byte_t data[TEST3_SIZE] = { 52, 116, 29, 120, 56, 135, 31, 196, 165, 219, 102, 169, 217, 228, 24, 163, 203, 93, 98, 71 };
	ks_byte_t data2[TEST3_SIZE] = { 248, 96, 216, 171, 94, 116, 77, 48, 114, 0, 49, 61, 93, 229, 224, 10, 6, 8, 112, 248 };
	ks_byte_t data3[TEST3_SIZE] = { 171, 58, 43, 4, 49, 222, 42, 253, 18, 122, 230, 51, 180, 66, 154, 130, 114, 117, 172, 193 };
	char *A, *B, *C;

	ks_pool_open(&pool);
	ks_hash_create(&hash, KS_HASH_MODE_ARBITRARY, KS_HASH_FLAG_NOLOCK, pool);
	ks_hash_set_keysize(hash, TEST3_SIZE);

	ks_hash_insert(hash, data, "FOO");
	ks_hash_insert(hash, data2, "BAR");
	ks_hash_insert(hash, data3, "BAZ");


	A = (char *)ks_hash_search(hash, data, KS_UNLOCKED);
	B = (char *)ks_hash_search(hash, data2, KS_UNLOCKED);
	C = (char *)ks_hash_search(hash, data3, KS_UNLOCKED);


	printf("RESULT [%s][%s][%s]\n", A, B, C);

	ks_hash_destroy(&hash);

	ks_pool_close(&pool);

	return !strcmp(A, "FOO") && !strcmp(B, "BAR") && !strcmp(C, "BAZ");

}


int main(int argc, char **argv)
{

	ks_init();
	ks_global_set_default_logger(KS_LOG_LEVEL_DEBUG);

	plan(3);

	ok(test1());
	ok(test2());
	ok(test3());

	ks_shutdown();

	done_testing();
}
