#include "system_time.hpp"
#include <time.h>
#include <cassert>

constexpr uint8_t MONTH_ARR_SIZE = 13;

static const std::string_view monthText[MONTH_ARR_SIZE] = {
	"NaMonth", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};

static constexpr systime::CPFTimestamp timeNotSet{"2000-01-01T00:00:00.000"};

/// Array of days for each month
static constexpr uint8_t monthDays[MONTH_ARR_SIZE] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

std::string_view systime::getMonthText(uint8_t month_num)
{
	assert(month_num < MONTH_ARR_SIZE);
	return monthText[month_num];
}

void systime::delay(std::chrono::duration<uint32_t, std::milli> delay_in_ms)
{
	systime::delay(delay_in_ms.count());
}

int64_t systime::getDuration_ms(const DateTimeStruct finish, const DateTimeStruct start)
{
	int64_t finish_sec = secondsSince1900Epoch(finish);
	int64_t start_sec = secondsSince1900Epoch(start);

	int64_t ms = (finish_sec - start_sec) * MS_PER_SEC + (finish.milliSecond - start.milliSecond);

	return ms;
}

int64_t systime::secondsSince1900Epoch(const DateTimeStruct t)
{
	struct tm time = {};
	time.tm_year = t.year - (1900 - 70); // Newlib appears to compute seconds since year 70 in their
										 // mktime implementation... what!?
	time.tm_mon = t.month - 1; // Since January
	time.tm_mday = t.date;
	time.tm_hour = t.hour;
	time.tm_min = t.minute;
	time.tm_sec = t.second;
	time.tm_isdst = 0;

	int64_t seconds = mktime(&time);

	return seconds;
}

systime::CPFTimestamp systime::dateTimeToTimestamp(const systime::DateTimeStruct& input)
{
	systime::CPFTimestamp timestamp;
	snprintf(timestamp.data(), systime::timestampSize, "%02d-%02d-%02dT%02d:%02d:%02d.%03d",
			 input.year, input.month, input.date, input.hour, input.minute, input.second,
			 input.milliSecond);
	return timestamp;
}

systime::CPFTimestamp systime::getTimestamp()
{
	auto now = systime::getDateTimeNow();
	systime::CPFTimestamp timestamp;

	if(now.year != 0) // If == 0, we have not yet set the time
	{
		timestamp = dateTimeToTimestamp(now);
	}
	else
	{
		timestamp = timeNotSet;
	}

	return timestamp;
}

systime::DateTimeStruct systime::adjustTime(systime::DateTimeStruct inTime, std::chrono::milliseconds adjustment_milliseconds)
{
	/// This all assumes sysTickCounter is 1ms increments
	uint8_t seconds_adjustment = static_cast<uint8_t>((adjustment_milliseconds.count() + inTime.milliSecond) / systime::MS_PER_SEC);
	inTime.second += seconds_adjustment;
	while(inTime.second >= systime::SECS_PER_MIN)
	{
		inTime.minute++;
		inTime.second -= systime::SECS_PER_MIN;
	}
	if(inTime.minute >= systime::MINS_PER_HOUR)
	{
		inTime.hour++;
		inTime.minute -= systime::MINS_PER_HOUR;
	}
	if(inTime.hour >= 24)
	{
		inTime.date++;
		inTime.hour -= 24;
		if(inTime.date > monthDays[inTime.month])
		{
			inTime.month++;
			inTime.date = 1;
			if(inTime.month > 12)
			{
				inTime.year++;
				inTime.month = 1;
			}
		}
	}

	return inTime;
}
