#include "stdafx.h"
#include "Core/Array.h"
#include "Core/Queue.h"
#include "Core/PQueue.h"
#include "Test/Core/GcCanary.h"
#include "Compiler/Debug.h"

using storm::debug::DbgVal;

// Helper to get the GC to run finalizers. Uses DbgVal to detect the status.
static bool DbgVal_finalizers(Engine &e) {
	const Nat MAX_ATTEMPTS = 10;
	for (Nat i = 0; i < MAX_ATTEMPTS; i++) {
		{
			os::Lock::L z(DbgVal::liveLock);
			if (DbgVal::live.empty()) {
				// PLN(L"Needed " << i << L" attempts");
				return true;
			}
		}

		e.gc.collect();
	}

	// Causes a print of live objects.
	return DbgVal::clear();
}

// Noinline to make sure that the array is not retained by the GC.
NOINLINE void createArray(Engine &e) {
	Array<DbgVal> *arr = new (e) Array<DbgVal>();
	// Note: minimum array size is 16, so make sure we need to grow it!
	for (Nat i = 0; i < 40; i++) {
		arr->push(DbgVal(i));
	}
}

BEGIN_TEST(ArrayDtor, Storm) {
	Engine &e = gEngine();

	DbgVal::clear();

	createArray(e);
	clearStack();

	DbgVal_finalizers(e);

	CHECK(DbgVal::clear());
} END_TEST


NOINLINE void createQueue(Engine &e) {
	Queue<DbgVal> *q = new (e) Queue<DbgVal>();
	// Note: Large enough to make sure the queue grows.
	for (Nat i = 0; i < 40; i++) {
		q->push(DbgVal(i));
	}

	for (Nat i = 0; i < 20; i++) {
		q->pop();
	}
}

BEGIN_TEST(QueueDtor, Storm) {
	Engine &e = gEngine();

	DbgVal::clear();

	createQueue(e);
	clearStack();

	DbgVal_finalizers(e);

	CHECK(DbgVal::clear());
} END_TEST

NOINLINE void createPQueue(Engine &e) {
	PQueue<DbgVal> *q = new (e) PQueue<DbgVal>();
	for (Nat i = 0; i < 40; i++) {
		q->push(DbgVal(i));
	}

	// Remove some elements as well.
	for (Nat i = 0; i < 20; i++)
		q->pop();
}

BEGIN_TEST(PQueueDtor, Storm) {
	Engine &e = gEngine();

	DbgVal::clear();

	createPQueue(e);
	clearStack();

	DbgVal_finalizers(e);

	CHECK(DbgVal::clear());
} END_TEST

NOINLINE void createSet(Engine &e) {
	Set<DbgVal> *s = new (e) Set<DbgVal>();
	for (Nat i = 0; i < 20; i++) {
		s->put(DbgVal(i));
	}

	s->remove(DbgVal(5));
	s->remove(DbgVal(3));
}

BEGIN_TEST(SetDtor, Storm) {
	Engine &e = gEngine();

	DbgVal::clear();

	createSet(e);
	clearStack();

	DbgVal_finalizers(e);

	CHECK(DbgVal::clear());
} END_TEST

NOINLINE void createMap(Engine &e) {
	Map<DbgVal, DbgVal> *m = new (e) Map<DbgVal, DbgVal>();
	for (Nat i = 0; i < 20; i++) {
		m->put(DbgVal(i), DbgVal(100 + i));
	}

	m->remove(DbgVal(5));
	m->remove(DbgVal(8));
}

BEGIN_TEST(MapDtor, Storm) {
	Engine &e = gEngine();

	DbgVal::clear();

	createMap(e);
	clearStack();

	DbgVal_finalizers(e);

	CHECK(DbgVal::clear());
} END_TEST
