#pragma once

#include <chrono>
#include <cstdint>
#include <map>
#include <string>
#include <thread>

#include "core/audio_types.h"

namespace aptt {

inline bool HighLoadDiagnosticsActive(const PlaybackConfig& config) {
  return config.high_load_diagnostics_enabled &&
         config.high_load_render_delay_ms > 0;
}

inline void AddHighLoadDiagnosticFields(
    const PlaybackConfig& config,
    std::map<std::string, std::string>& fields) {
  fields["high_load_mode"] =
      config.high_load_diagnostics_enabled ? "true" : "false";
  fields["high_load_expert_opt_in"] =
      config.high_load_diagnostics_enabled ? "true" : "false";
  fields["high_load_risk_acknowledged"] =
      config.high_load_risk_acknowledged ? "true" : "false";
  fields["high_load_mechanism"] =
      config.high_load_diagnostics_enabled
          ? "bounded_render_callback_delay"
          : "off";
  fields["high_load_delay_ms"] =
      std::to_string(config.high_load_render_delay_ms);
  fields["high_load_delay_every_callbacks"] =
      std::to_string(config.high_load_delay_every_callbacks);
  fields["high_load_bounded"] = "true";
}

inline bool HighLoadDelayDue(const PlaybackConfig& config,
                             std::uint64_t callback_count) {
  if (!HighLoadDiagnosticsActive(config)) {
    return false;
  }
  const std::uint32_t every =
      config.high_load_delay_every_callbacks == 0
          ? 1
          : config.high_load_delay_every_callbacks;
  return (callback_count % every) == 0;
}

inline void ApplyHighLoadRenderDelay(const PlaybackConfig& config,
                                     std::uint64_t callback_count) {
  if (!HighLoadDelayDue(config, callback_count)) {
    return;
  }
  std::this_thread::sleep_for(
      std::chrono::milliseconds(config.high_load_render_delay_ms));
}

inline bool HighLoadDelayLikelyLate(const PlaybackConfig& config,
                                    double expected_interval_ms) {
  return HighLoadDiagnosticsActive(config) &&
         expected_interval_ms > 0.0 &&
         static_cast<double>(config.high_load_render_delay_ms) >=
             expected_interval_ms;
}

inline bool HighLoadLateCallbackJustOccurred(const PlaybackConfig& config,
                                             const RealtimeStats& stats) {
  const std::uint64_t callback_count_before_delay =
      stats.callback_count == 0 ? 0 : stats.callback_count - 1;
  return HighLoadDelayDue(config, callback_count_before_delay) &&
         HighLoadDelayLikelyLate(config, stats.expected_callback_interval_ms);
}

inline void CountHighLoadLateCallbackIfNeeded(const PlaybackConfig& config,
                                              RealtimeStats& stats) {
  if (HighLoadLateCallbackJustOccurred(config, stats)) {
    ++stats.underrun_count;
  }
}

}  // namespace aptt
