#pragma once

// public:

/** Use UpperCamelCase for suite and test only */
#define TEST(suite, test) \
	void _TEST_##suite##_##test(); \
	ACCUMULATE int TEST_RunAll_accumulated(int f) { \
		if (!TEST_Run(#suite "_" #test)) ++f; \
		return = f; \
	} \
	void _TEST_##suite##_##test()

/** Must be present at the end of a test */
#define SUCCEED() (TEST_ok = true)

/** Add a failure, but continue */
#define ADD_FAILURE(msg) MACRO_BEGIN \
	++TEST_failed; \
	LOG_WARN(msg); \
MACRO_END

/** Add a failure and return */
#define FAIL(msg) _TEST_ASSERT(ADD_FAILURE(msg))

#define HasFatalFailure() (TEST_fatal > 0)

bool RUN_ALL_TESTS();

// difference between expect/assert: assert returns early

#define EXPECT_EQ(expected_, actual_) MACRO_BEGIN \
	int expected = expected_; \
	int actual = actual_; \
	if ((expected) != (actual)) { \
		ADD_FAILURE(sprintf( \
			"Value of: " #actual_ "\n" \
			"  Actual: %d\n" \
			"Expected: %d\n", \
			actual, expected \
		)); \
	} \
MACRO_END
#define ASSERT_EQ(expected, actual) _TEST_ASSERT(EXPECT_EQ(expected, actual))

#define EXPECT_TRUE(condition) EXPECT_EQ(true, condition)
#define ASSERT_TRUE(condition) ASSERT_EQ(true, condition)

#define EXPECT_FALSE(condition) EXPECT_EQ(false, condition)
#define ASSERT_FALSE(condition) ASSERT_EQ(false, condition)

#define EXPECT_NE(val1, val2) EXPECT_TRUE((val1) != (val2))
#define ASSERT_NE(val1, val2) _TEST_ASSERT(EXPECT_NE(val1, val2))

#define EXPECT_LT(val1, val2) EXPECT_TRUE((val1) < (val2))
#define ASSERT_LT(val1, val2) _TEST_ASSERT(EXPECT_LT(val1, val2))

#define EXPECT_LE(val1, val2) EXPECT_TRUE((val1) <= (val2))
#define ASSERT_LE(val1, val2) _TEST_ASSERT(EXPECT_LE(val1, val2))

#define EXPECT_GT(val1, val2) EXPECT_TRUE((val1) > (val2))
#define ASSERT_GT(val1, val2) _TEST_ASSERT(EXPECT_GT(val1, val2))

#define EXPECT_GE(val1, val2) EXPECT_TRUE((val1) >= (val2))
#define ASSERT_GE(val1, val2) _TEST_ASSERT(EXPECT_GE(val1, val2))

#define EXPECT_NO_FATAL_FAILURE(statement) EXPECT_NO_FATAL_FAILURE_(statement, { })
#define ASSERT_NO_FATAL_FAILURE(statement) EXPECT_NO_FATAL_FAILURE_(statement, { ++TEST_fatal; return; })

// private:

bool TEST_Run(string test);
int TEST_fatal;
bool TEST_ok;
int TEST_failed;

#define _TEST_ASSERT(statement) \
	MACRO_BEGIN \
		LAMBDA(statement); \
		++TEST_fatal; return; \
	MACRO_END

#define EXPECT_NO_FATAL_FAILURE__(statement, then) \
	MACRO_BEGIN \
		int TEST_prevfatal = TEST_fatal; \
		LAMBDA(statement); \
		if (TEST_fatal != TEST_prevfatal) \
			LAMBDA(then); \
	MACRO_END

#define EXPECT_NO_FATAL_FAILURE_(statement, then) \
	EXPECT_NO_FATAL_FAILURE__(statement, { \
		LOG_WARNF( \
			"  Actual: %d fatal failures\n" \
			"Expected: no fatal failures\n", \
			TEST_fatal - TEST_prevfatal \
		); \
		LAMBDA(then); \
	})
