#pragma once

#include <cstdint>
#include <memory>
#include <string>
#include <vector>

#include "core/audio_backend.h"
#include "core/diagnostic_log.h"
#include "core/dop_generator.h"

namespace aptt {

enum class AsioPcmSampleType {
  kInt16Lsb,
  kInt24Lsb,
  kInt24In32Lsb,
  kInt32Lsb,
  kFloat32Lsb,
  kFloat64Lsb,
};

struct AsioPcmPolicy {
  bool requires_explicit_driver = true;
  bool keep_warm_after_signal = true;
  bool release_after_drain = false;
  bool opens_control_panel = false;
  bool unloads_driver_during_session = false;
  bool uses_repeated_asio_exit = false;
  bool uses_output_ready_if_supported = true;
  std::uint32_t release_cooldown_ms = 1000;
};

struct AsioBufferConstraints {
  long min_size = 0;
  long max_size = 0;
  long preferred_size = 0;
  long granularity = 0;
  long input_latency = 0;
  long output_latency = 0;
  bool output_ready_supported = false;
};

AsioPcmPolicy MakeAsioPcmPolicy(const PlaybackConfig& config);
bool IsAsioPcmPlaybackFormat(const FormatDescriptor& format);
bool IsAsioDopCarrierFormat(const FormatDescriptor& format);
std::uint32_t AsioBytesPerSample(AsioPcmSampleType sample_type);

std::vector<std::vector<std::uint8_t>> ConvertFloat32InterleavedToAsioPlanar(
    const std::vector<float>& interleaved_samples,
    std::uint32_t channel_count,
    AsioPcmSampleType sample_type);

std::vector<std::vector<std::uint8_t>> ConvertInterleavedDop24In32ToAsioPlanar(
    const std::vector<std::uint8_t>& interleaved_dop_bytes,
    std::uint32_t channel_count);

DiagnosticEvent MakeAsioPcmPreparedEvent(
    std::uint64_t sequence,
    std::string device_id,
    const FormatDescriptor& format,
    const AsioBufferConstraints& constraints);

DiagnosticEvent MakeAsioPcmRuntimeEvent(
    std::uint64_t sequence,
    std::string device_id,
    const RealtimeStats& stats,
    std::int32_t buffer_index,
    std::int32_t buffer_frames,
    double callback_duration_ms,
    bool direct_process,
    bool output_ready_called);

class AsioPcmBackend final : public IAudioBackend {
 public:
  AsioPcmBackend();
  ~AsioPcmBackend() override;

  BackendApi api() const override;
  DeviceList enumerateDevices() override;
  FormatProbeResult probeFormats(const DeviceInfo& device) override;
  FormatProbeResult probeDopFormats(const DeviceInfo& device);
  FormatProbeResult probeDopFormats(const DeviceInfo& device,
                                    const DopConfig& requested_dop);
  OperationResult initialize(const PlaybackConfig& config) override;
  OperationResult start() override;
  OperationResult stop() override;
  OperationResult reset() override;
  RealtimeStats getRealtimeStats() const override;
  BackendError getLastError() const override;
  OperationResult shutdown() override;

  DeviceLifecycleState lifecycle_state() const;
  std::vector<DiagnosticEvent> diagnostic_events() const;

 private:
  struct Impl;
  std::unique_ptr<Impl> impl_;
};

}  // namespace aptt
