#include "trace.h"

#include <algorithm>
#include <chrono>

#include "itoa.h"
#include "lock.h"
#include "uart.h"

namespace tracing {
namespace {

struct Event {
    uint32_t timestamp;
    TraceEvent event;
};

constexpr size_t kTraceBufferSize = 256;
std::array<Event, kTraceBufferSize> buffer;
size_t write_ptr = 0;
size_t size = 0;

}  // namespace

void trace(int raw_event) { trace(static_cast<TraceEvent>(raw_event)); }

void trace(TraceEvent event) {
    const std::chrono::system_clock::time_point now =
        std::chrono::system_clock::now();
    const uint32_t uptime_ticks = now.time_since_epoch().count();

    {
        InterruptLock lock;

        buffer[write_ptr] = {.timestamp = uptime_ticks, .event = event};

        write_ptr = (write_ptr + 1) % buffer.size();
        size = std::min(size + 1, kTraceBufferSize);

#if TRACE_DUMP_WHEN_FULL
        if (size == kTraceBufferSize) {
            dump();
        }
#endif  // TRACE_DUMP_WHEN_FULL
    }
}

void dump() {
    InterruptLock lock;

    if (size == kTraceBufferSize) {
        std::rotate(buffer.begin(), buffer.begin() + write_ptr, buffer.end());
    }

    char number[] = "00000000";

    UartWriteCrash("----\r\n");
    for (Event event : std::span{buffer}.subspan(0, size)) {
        itoa(static_cast<int>(event.timestamp), number);
        UartWriteCrash(number);
        UartWriteCrash(" ");
        itoa(static_cast<int>(event.event), number);
        UartWriteCrash(number);
        UartWriteCrash("\r\n");
    }
    UartWriteCrash("----\r\n");

    size = 0;
    write_ptr = 0;
}
}  // namespace tracing
