// clang-format off
#include <STM32Timer.hpp>
#include <system_time.hpp>
#include <bsp.hpp>
#include <_bsp_private.hpp> // Exception granted: this is for a test
#include <cstring>
#include <CppUTest/CommandLineTestRunner.h>
// clang-format on

static volatile bool timer_callback_called;
static volatile uint32_t timer_callback_count;

static void timer_callback()
{
	timer_callback_called = true;
}

static void counting_timer_callback()
{
	timer_callback_count++;
}

static void __attribute__((unused)) led_timer_callback()
{
	timer_callback_count++;
	toggleLED(LED::OK);
	if(0 == timer_callback_count % 5)
	{
		toggleLED(LED::ERROR);
	}
}

TEST_GROUP(STM32TimerTests){};

TEST(STM32TimerTests, OneShotTimer)
{
	auto timeout = std::chrono::milliseconds(1);
	timer_callback_called = false;
	STM32Timer timer(STM32Timer::Device::CH5, timeout, STM32Timer::TickRate::microsecond);

	CHECK(timer.config() == Timer::Config::oneshot);
	CHECK(timer.period() == timeout);

	CHECK(Timer::State::stopped == timer.state());
	timer.registerCallback(timer_callback);
	timer.start();
	CHECK_FALSE(timer_callback_called);
	CHECK(Timer::State::armed == timer.state());
	CHECK(timer.count().count() > 0);
	systime::delay(std::chrono::milliseconds(5));
	CHECK(timer_callback_called);
	CHECK(Timer::State::stopped == timer.state());
}

TEST(STM32TimerTests, PeriodicTimer)
{
	auto timeout = std::chrono::milliseconds(1);
	timer_callback_count = 0;

	STM32Timer timer(STM32Timer::Device::CH5, timeout, STM32Timer::TickRate::microsecond);
	timer.config(Timer::Config::periodic);
	timer.registerCallback(counting_timer_callback);
	CHECK(timer.config() == Timer::Config::periodic);
	CHECK(timer.period() == timeout);
	CHECK(Timer::State::stopped == timer.state());

	timer.start();
	systime::delay(std::chrono::milliseconds(5));
	timer.stop();
	CHECK(Timer::State::stopped == timer.state());
	CHECK(timer_callback_count >= 4);
}

// This test can be used for a visual confirmation of the timer
// accuracy, or at least, you can get a sense of it...
IGNORE_TEST(STM32TimerTests, PeriodicBlinkTest)
{
	auto timeout = std::chrono::seconds(1);
	timer_callback_count = 0;

	bsp_private::initializeLEDs();

	STM32Timer timer(STM32Timer::Device::CH2, timeout);
	timer.config(Timer::Config::periodic);
	timer.registerCallback(led_timer_callback);

	timer.start();
	systime::delay(std::chrono::seconds(10));
	timer.stop();
}

TEST(STM32TimerTests, TimerDevice1)
{
	auto timeout = std::chrono::milliseconds(1);
	timer_callback_called = false;
	STM32Timer timer(STM32Timer::Device::CH1, timeout);
	timer.registerCallback(timer_callback);
	timer.start();
	systime::delay(std::chrono::milliseconds(5));
	CHECK(timer_callback_called);
	CHECK(Timer::State::stopped == timer.state());
}

TEST(STM32TimerTests, TimerDevice3)
{
	auto timeout = std::chrono::milliseconds(1);
	timer_callback_called = false;
	STM32Timer timer(STM32Timer::Device::CH3, timeout);
	timer.registerCallback(timer_callback);
	timer.start();
	systime::delay(std::chrono::milliseconds(5));
	CHECK(timer_callback_called);
	CHECK(Timer::State::stopped == timer.state());
}

TEST(STM32TimerTests, TimerDevice4)
{
	auto timeout = std::chrono::milliseconds(1);
	timer_callback_called = false;
	STM32Timer timer(STM32Timer::Device::CH4, timeout);
	timer.registerCallback(timer_callback);
	timer.start();
	systime::delay(std::chrono::milliseconds(5));
	CHECK(timer_callback_called);
	CHECK(Timer::State::stopped == timer.state());
}

TEST(STM32TimerTests, TimerDevice5)
{
	auto timeout = std::chrono::milliseconds(1);
	timer_callback_called = false;
	STM32Timer timer(STM32Timer::Device::CH5, timeout);
	timer.registerCallback(timer_callback);
	timer.start();
	systime::delay(std::chrono::milliseconds(5));
	CHECK(timer_callback_called);
	CHECK(Timer::State::stopped == timer.state());
}

TEST(STM32TimerTests, TimerDevice6)
{
	auto timeout = std::chrono::milliseconds(1);
	timer_callback_called = false;
	STM32Timer timer(STM32Timer::Device::CH6, timeout);
	timer.registerCallback(timer_callback);
	timer.start();
	systime::delay(std::chrono::milliseconds(5));
	CHECK(timer_callback_called);
	CHECK(Timer::State::stopped == timer.state());
}

TEST(STM32TimerTests, TimerDevice7)
{
	auto timeout = std::chrono::milliseconds(1);
	timer_callback_called = false;
	STM32Timer timer(STM32Timer::Device::CH7, timeout);
	timer.registerCallback(timer_callback);
	timer.start();
	systime::delay(std::chrono::milliseconds(5));
	CHECK(timer_callback_called);
	CHECK(Timer::State::stopped == timer.state());
}

TEST(STM32TimerTests, TimerDevice8)
{
	auto timeout = std::chrono::milliseconds(1);
	timer_callback_called = false;
	STM32Timer timer(STM32Timer::Device::CH8, timeout);
	timer.registerCallback(timer_callback);
	timer.start();
	systime::delay(std::chrono::milliseconds(5));
	CHECK(timer_callback_called);
	CHECK(Timer::State::stopped == timer.state());
}
