#pragma once
#include <array>
#include <string>
#include <stdint.h>

/**
 * @brief Print a formatted row of titles with of gaps seperated by a separator.
 *
 * @param gaps List of size gaps
 * @param titles List of titles
 * @param separator Separator character between each gap
 */
template<size_t N>
constexpr void formatted_row(const std::array<int32_t, N>& gaps, const std::array<const char* const, N>& titles,
					const char* const separator = "│") noexcept {
	for(size_t i = 0; i < N; ++i)
		printf("%s %*s ", separator, -gaps[i], titles[i]);
	printf("%s\n", separator);
}

/**
 * @brief Print a formatted line of repeated characters.
 *
 * @param gaps List of size gaps
 * @param right Character on the left
 * @param middle Character between each separator
 * @param separator Separator character between each gap
 * @param left Character on the right
 */
template<size_t N>
constexpr void formatted_line(const std::array<int32_t, N>& gaps, const char* const left, const char* const middle,
			const char* const separator, const char* const right) noexcept {
	printf("%s", left);
	for(size_t i = 0; i < N; ++i){
		for(int32_t j = std::abs(gaps[i]) + 2; j > 0; --j)
			printf("%s", separator);
		if(i != N - 1)
			printf("%s", middle);
	}

	printf("%s\n", right);
}

/**
 * @brief Print a formatted header with the given titles and sizes.
 *
 * @param gaps List of size gaps
 * @param titles List of titles
 */
template<size_t N>
constexpr void header(const std::array<int32_t, N>& gaps, const std::array<const char* const, N>& titles) noexcept {
	formatted_line(gaps, "┌", "┬", "─", "┐");
	formatted_row(gaps, titles);
	formatted_line(gaps, "├", "┼", "─", "┤");
}

/**
 * @brief Print a formatted footer with the given sizes.
 *
 * @param gaps List of size gaps
 */
template<size_t N>
constexpr inline void footer(const std::array<int32_t, N>& gaps) noexcept {
	formatted_line(gaps, "└", "┴", "─", "┘");
}

#define duration_ns(a) std::chrono::duration_cast<std::chrono::nanoseconds>(a).count()
#define perf_counter_ns() std::chrono::high_resolution_clock::now()

/**
 * @brief Format the time in seconds in human readable format.
 *
 * @param time number of seconds
 * @return The formatted human readable string
 */
std::string format_time(uint64_t) noexcept;

/**
 * @brief Format the time in nanoseconds in human readable format.
 *
 * @param time Time in nanoseconds
 * @return std::string The formatted human readable string
 */
std::string format_time_ns(uint64_t) noexcept;

/**
 * @brief Convert the number of byte in JEDEC standard form.
 * See more : https://en.wikipedia.org/wiki/JEDEC_memory_standards
 *
 * @param bytes Number of bytes
 * @return JEDEC compliant formatted number of bytes
 */
std::string format_byte_size(uint64_t) noexcept;

/**
 * @brief Format a number with a separator (i.e. 1000 as 1,000)
 *
 * @param k number to format
 * @param separator used between each thouand
 * @return Formatted number
 */
std::string thousand_sep(uint64_t, const char& = ',') noexcept;