#pragma once

#include <cstdint>
#include <optional>
#include <string>
#include <vector>

namespace aptt {

enum class BackendApi {
  kNull,
  kMme,
  kDirectSound,
  kWasapiShared,
  kWasapiExclusive,
  kAsio,
  kKernelStreaming,
};

enum class SignalMode {
  kPcm,
  kDop,
  kAsioNativeDsd,
};

enum class SampleType {
  kInt16,
  kInt24Packed,
  kInt24In32,
  kInt32,
  kFloat32,
  kFloat64,
  kDsd1BitLsbFirst,
  kDsd1BitMsbFirst,
  kDsd8Bit,
};

enum class HoldMode {
  kKeepWarmAfterStop,
  kHoldInitialized,
  kReleaseAfterDrain,
  kImmediateRelease,
};

struct ChannelMap {
  std::uint32_t channel_count = 2;
  std::uint32_t channel_mask = 0;
  std::vector<std::string> labels;
};

struct FormatDescriptor {
  SignalMode signal_mode = SignalMode::kPcm;
  std::uint32_t sample_rate_hz = 48000;
  SampleType sample_type = SampleType::kFloat32;
  std::uint32_t valid_bits_per_sample = 32;
  std::uint32_t container_bits_per_sample = 32;
  ChannelMap channels;
  std::optional<std::uint32_t> dsd_rate_multiplier;
  bool dop_marker_starts_with_0x05 = true;
};

struct DeviceInfo {
  BackendApi backend = BackendApi::kNull;
  std::string display_name;
  std::string stable_id;
  std::string driver_name;
  bool is_default = false;
  bool is_known_problem_device = false;
};

struct PlaybackConfig {
  BackendApi backend = BackendApi::kNull;
  DeviceInfo device;
  FormatDescriptor format;
  double frequency_hz = 1000.0;
  double amplitude_dbfs = -20.0;
  std::uint32_t duration_ms = 1000;
  bool continuous = false;
  std::uint32_t fade_in_ms = 10;
  std::uint32_t fade_out_ms = 10;
  std::uint32_t silence_preroll_ms = 0;
  std::uint32_t silence_postroll_ms = 0;
  HoldMode hold_mode = HoldMode::kKeepWarmAfterStop;
  std::uint32_t release_cooldown_ms = 1000;
  std::uint32_t reacquire_cooldown_ms = 2000;
  std::uint32_t max_reacquire_attempts = 1;
  std::uint32_t mme_buffer_count = 4;
  std::uint32_t mme_buffer_duration_ms = 20;
  std::uint32_t directsound_secondary_buffer_duration_ms = 250;
  std::uint32_t directsound_write_chunk_ms = 20;
  std::uint32_t directsound_cursor_poll_interval_ms = 5;
  std::uint32_t wasapi_shared_buffer_duration_ms = 100;
  std::uint32_t wasapi_shared_render_poll_interval_ms = 5;
  std::uint32_t wasapi_exclusive_buffer_duration_ms = 100;
  std::uint32_t wasapi_exclusive_periodicity_ms = 100;
  std::uint32_t wasapi_exclusive_render_poll_interval_ms = 5;
  bool wasapi_exclusive_event_callback_enabled = true;
  bool wasapi_exclusive_alignment_retry_enabled = true;
  std::uint32_t wasapi_exclusive_event_timeout_ms = 2000;
  bool audio_thread_mmcss_enabled = true;
  std::string audio_thread_mmcss_task_name = "Pro Audio";
  std::int32_t audio_thread_mmcss_priority = 0;
  bool asio_use_output_ready_if_supported = true;
  bool high_load_diagnostics_enabled = false;
  bool high_load_risk_acknowledged = false;
  std::uint32_t high_load_render_delay_ms = 0;
  std::uint32_t high_load_delay_every_callbacks = 1;
  double right_channel_phase_offset_degrees = 0.0;
  bool left_channel_enabled = true;
  bool right_channel_enabled = true;
  bool invert_right_phase = false;
};

struct RealtimeStats {
  std::uint64_t frames_generated = 0;
  std::uint64_t frames_submitted = 0;
  std::uint64_t frames_accepted = 0;
  std::uint64_t callback_count = 0;
  double expected_callback_interval_ms = 0.0;
  double last_callback_interval_ms = 0.0;
  double max_callback_delay_ms = 0.0;
  std::uint64_t underrun_count = 0;
  std::uint64_t discontinuity_count = 0;
  std::uint64_t device_invalidated_count = 0;
};

const char* ToString(BackendApi backend);
const char* ToString(SignalMode signal_mode);
const char* ToString(SampleType sample_type);
const char* ToString(HoldMode hold_mode);

}  // namespace aptt
