// =====================================================================================================================
//  fennec-test, a program to execute unit tests for fennec
//  Copyright © 2025 - 2026  Medusa Slockbower
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================

#ifndef FENNEC_TEST_H
#define FENNEC_TEST_H

#include <iostream>
#include <ostream>
#include <string>

#include <fennec/lang/limits.h>
#include <fennec/math/vector_traits.h>

#include "printing.h"

namespace fennec
{

namespace test
{

// Core test function
template<typename ResultT>
inline void __fennec_test_run(const std::string& expression, const ResultT& result, const ResultT& expected)
{
	// Print out the expression as a string and the resulting value
	std::cout << std::boolalpha;
	std::cout << '\t' << expression << " = " << result;

	bool passed = false;

	// Check scalar arithmetic values
	if constexpr(is_arithmetic_v<ResultT>)
	{
		passed = result == expected;

		if constexpr(not is_bool_v<ResultT>)
			passed |= fennec::abs(expected - result) <= numeric_limits<ResultT>::epsilon();
	}
	// Check non-boolean vector values
	else if constexpr(is_vector_v<ResultT>)
	{
		passed = result == expected;

		if constexpr(not is_bool_v<typename ResultT::scalar_t>)
			passed |= fennec::all(fennec::lessThanEqual(fennec::abs(expected - result), ResultT(numeric_limits<typename ResultT::scalar_t>::epsilon())));
	}
	// Base Case, simple comparison
	else
	{
		passed = result == expected;
	}

	// Print out expected when failed
	if (not passed)
		std::cout << ", expected " << expected;

	std::cout << std::endl;

	// Assert to halt and get some debug info
	assertf(passed, "Test Failed");
}

}

}

#define fennec_test_run(Expression, Expected) __fennec_test_run(#Expression, Expression, Expected)

#define fennec_test_header(Header) std::cout << std::string(80, '=') << std::endl \
	                                         << (Header)             << std::endl \
	                                         << std::string(80, '=') << std::endl

#define fennec_test_subheader(Header) std::cout << (Header)  << ' ' << std::string(80 - sizeof(Header), '=')  << std::endl
#define fennec_test_section(Section)  std::cout << (Section) << ' ' << std::string(80 - sizeof(Section), '-') << std::endl

#define fennec_test_spacer(Count) std::cout << std::string(Count, std::cout.widen('\n'))

#endif // FENNEC_TEST_H
