#pragma once

#include <SDL2/SDL.h>

#include <fstream>
#include <mutex>
#include <string>

namespace frostbite2D {

class StartupTrace {
public:
  static constexpr bool enabled() {
#ifndef NDEBUG
    return true;
#else
    return false;
#endif
  }

  static void reset(const char* label = nullptr) {
    if (!enabled()) {
      return;
    }

    Uint64 now = SDL_GetPerformanceCounter();
    std::string line;
    {
      std::lock_guard<std::mutex> lock(mutex());
      startCounter() = now;
      if (label && label[0] != '\0') {
        line = std::string("[Startup] reset: ") + label;
        clearLogFileUnlocked();
        writeLineUnlocked(line);
      } else {
        clearLogFileUnlocked();
      }
    }
    if (!line.empty()) {
      SDL_Log("%s", line.c_str());
    }
  }

  static double totalElapsedMs() {
    if (!enabled()) {
      return 0.0;
    }

    Uint64 start = 0;
    {
      std::lock_guard<std::mutex> lock(mutex());
      start = startCounter();
    }

    if (start == 0) {
      return 0.0;
    }

    Uint64 now = SDL_GetPerformanceCounter();
    Uint64 frequency = SDL_GetPerformanceFrequency();
    if (frequency == 0) {
      return 0.0;
    }

    return static_cast<double>(now - start) * 1000.0 /
           static_cast<double>(frequency);
  }

  static void mark(const char* label) {
    if (!enabled()) {
      return;
    }

    char buffer[256] = {};
    SDL_snprintf(buffer, sizeof(buffer),
                 "[Startup] %s reached at %.2f ms (thread=%u)",
                 label ? label : "<unnamed>", totalElapsedMs(), SDL_ThreadID());
    logLine(buffer);
    SDL_Log("%s", buffer);
  }

  static void logLine(const std::string& line) {
    if (!enabled()) {
      return;
    }

    std::lock_guard<std::mutex> lock(mutex());
    writeLineUnlocked(line);
  }

private:
  static Uint64& startCounter() {
    static Uint64 counter = 0;
    return counter;
  }

  static std::mutex& mutex() {
    static std::mutex value;
    return value;
  }

  static const char* logFilePath() {
    return "startup_trace.log";
  }

  static void clearLogFileUnlocked() {
    std::ofstream stream(logFilePath(), std::ios::trunc);
  }

  static void writeLineUnlocked(const std::string& line) {
    std::ofstream stream(logFilePath(), std::ios::app);
    if (!stream) {
      return;
    }
    stream << line << '\n';
  }
};

class ScopedStartupTrace {
public:
  explicit ScopedStartupTrace(const char* label)
      : label_(label ? label : "<unnamed>"),
        startCounter_(SDL_GetPerformanceCounter()) {}

  ~ScopedStartupTrace() {
    if (!StartupTrace::enabled()) {
      return;
    }

    Uint64 now = SDL_GetPerformanceCounter();
    Uint64 frequency = SDL_GetPerformanceFrequency();
    double elapsedMs = 0.0;
    if (frequency != 0) {
      elapsedMs = static_cast<double>(now - startCounter_) * 1000.0 /
                  static_cast<double>(frequency);
    }

    char buffer[256] = {};
    SDL_snprintf(buffer, sizeof(buffer),
                 "[Startup] %s took %.2f ms (total %.2f ms, thread=%u)",
                 label_, elapsedMs, StartupTrace::totalElapsedMs(),
                 SDL_ThreadID());
    StartupTrace::logLine(buffer);
    SDL_Log("%s", buffer);
  }

private:
  const char* label_;
  Uint64 startCounter_ = 0;
};

} // namespace frostbite2D
