#include "core/native_dsd_generator.h"

#include <algorithm>
#include <array>
#include <cmath>
#include <sstream>
#include <utility>

namespace aptt {
namespace {

constexpr std::uint32_t kDsdBaseRateHz = 44100;
constexpr std::uint8_t kDsdIdleLsbFirst = 0x69;
constexpr std::uint8_t kDsdIdleMsbFirst = 0x96;
constexpr double kPi = 3.14159265358979323846;

NativeDsdGenerationResult MakeNativeDsdFailure(const NativeDsdConfig& config,
                                               std::string error) {
  NativeDsdGenerationResult result;
  result.ok = false;
  result.error = std::move(error);
  result.format = MakeNativeDsdFormat(config);
  return result;
}

bool ValidateNativeDsdConfig(const NativeDsdConfig& config,
                             std::string& error) {
  if (!IsSupportedNativeDsdRate(config.dsd_rate_multiplier)) {
    error = "unsupported native DSD rate multiplier";
    return false;
  }
  if (config.channel_count == 0) {
    error = "native DSD channel count must be non-zero";
    return false;
  }
  if (!IsSupportedNativeDsdSampleType(config.sample_type)) {
    error = "unsupported native DSD sample type";
    return false;
  }
  return true;
}

double DbfsToLinearAmplitude(double dbfs) {
  return std::pow(10.0, dbfs / 20.0);
}

std::uint8_t PackOneBitDsdByte(SampleType sample_type,
                               const bool bits[8]) {
  std::uint8_t packed = 0;
  for (std::uint32_t bit = 0; bit < 8; ++bit) {
    const std::uint32_t output_bit =
        sample_type == SampleType::kDsd1BitMsbFirst ? 7 - bit : bit;
    if (bits[bit]) {
      packed |= static_cast<std::uint8_t>(1u << output_bit);
    }
  }
  return packed;
}

}  // namespace

std::uint32_t NativeDsdRateHz(std::uint32_t dsd_rate_multiplier) {
  if (!IsSupportedNativeDsdRate(dsd_rate_multiplier)) {
    return 0;
  }
  return kDsdBaseRateHz * dsd_rate_multiplier;
}

bool IsSupportedNativeDsdRate(std::uint32_t dsd_rate_multiplier) {
  return dsd_rate_multiplier == 64 ||
         dsd_rate_multiplier == 128 ||
         dsd_rate_multiplier == 256 ||
         dsd_rate_multiplier == 512 ||
         dsd_rate_multiplier == 1024 ||
         dsd_rate_multiplier == 2048;
}

bool IsSupportedNativeDsdSampleType(SampleType sample_type) {
  return sample_type == SampleType::kDsd1BitLsbFirst ||
         sample_type == SampleType::kDsd1BitMsbFirst ||
         sample_type == SampleType::kDsd8Bit;
}

const char* NativeDsdSampleTypeName(SampleType sample_type) {
  switch (sample_type) {
    case SampleType::kDsd1BitLsbFirst:
      return "ASIOSTDSDInt8LSB1";
    case SampleType::kDsd1BitMsbFirst:
      return "ASIOSTDSDInt8MSB1";
    case SampleType::kDsd8Bit:
      return "ASIOSTDSDInt8NER8";
    default:
      return "unsupported";
  }
}

const char* NativeDsdByteOrderName(SampleType sample_type) {
  switch (sample_type) {
    case SampleType::kDsd1BitLsbFirst:
      return "lsb-first";
    case SampleType::kDsd1BitMsbFirst:
      return "msb-first";
    case SampleType::kDsd8Bit:
      return "ner8";
    default:
      return "unsupported";
  }
}

FormatDescriptor MakeNativeDsdFormat(const NativeDsdConfig& config) {
  FormatDescriptor descriptor;
  descriptor.signal_mode = SignalMode::kAsioNativeDsd;
  descriptor.sample_rate_hz = NativeDsdRateHz(config.dsd_rate_multiplier);
  descriptor.sample_type = config.sample_type;
  descriptor.valid_bits_per_sample =
      config.sample_type == SampleType::kDsd8Bit ? 8 : 1;
  descriptor.container_bits_per_sample = 8;
  descriptor.channels.channel_count = config.channel_count == 0
      ? 1
      : config.channel_count;
  descriptor.channels.channel_mask =
      descriptor.channels.channel_count == 2 ? 3 : 0;
  descriptor.dsd_rate_multiplier = config.dsd_rate_multiplier;
  return descriptor;
}

NativeDsdGenerationResult GenerateNativeDsdIdle(
    const NativeDsdConfig& config,
    std::uint32_t byte_count_per_channel) {
  std::string error;
  if (!ValidateNativeDsdConfig(config, error)) {
    return MakeNativeDsdFailure(config, std::move(error));
  }

  NativeDsdGenerationResult result;
  result.ok = true;
  result.format = MakeNativeDsdFormat(config);
  result.channel_payloads.assign(
      config.channel_count,
      std::vector<std::uint8_t>(byte_count_per_channel, 0));

  if (config.sample_type == SampleType::kDsd1BitLsbFirst ||
      config.sample_type == SampleType::kDsd1BitMsbFirst) {
    const std::uint8_t idle = config.sample_type == SampleType::kDsd1BitMsbFirst
        ? kDsdIdleMsbFirst
        : kDsdIdleLsbFirst;
    for (auto& channel : result.channel_payloads) {
      std::fill(channel.begin(), channel.end(), idle);
    }
    return result;
  }

  for (auto& channel : result.channel_payloads) {
    for (std::uint32_t index = 0; index < byte_count_per_channel; ++index) {
      channel[index] = (index % 2) == 0 ? 0x00 : 0x01;
    }
  }
  return result;
}

NativeDsdGenerationResult GenerateNativeDsdTestPattern(
    const NativeDsdConfig& config,
    std::uint32_t byte_count_per_channel) {
  std::string error;
  if (!ValidateNativeDsdConfig(config, error)) {
    return MakeNativeDsdFailure(config, std::move(error));
  }

  NativeDsdGenerationResult result;
  result.ok = true;
  result.format = MakeNativeDsdFormat(config);
  result.channel_payloads.resize(config.channel_count);
  for (std::uint32_t channel = 0; channel < config.channel_count; ++channel) {
    auto& payload = result.channel_payloads[channel];
    payload.reserve(byte_count_per_channel);
    for (std::uint32_t index = 0; index < byte_count_per_channel; ++index) {
      payload.push_back(static_cast<std::uint8_t>(
          (channel * 0x31u + index * 0x11u) & 0xFFu));
    }
  }
  return result;
}

void NativeDsdSineGenerator::Reset() {
  dsd_sample_index_ = 0;
  modulators_.clear();
}

void NativeDsdSineGenerator::EnsureChannelState(std::uint32_t channel_count) {
  if (modulators_.size() != channel_count) {
    modulators_.assign(channel_count, 0.0);
  }
}

NativeDsdGenerationResult NativeDsdSineGenerator::GenerateSine(
    const NativeDsdSineConfig& config,
    std::uint32_t byte_count_per_channel) {
  std::string error;
  if (!ValidateNativeDsdConfig(config.dsd, error)) {
    return MakeNativeDsdFailure(config.dsd, std::move(error));
  }
  if (!std::isfinite(config.frequency_hz) || config.frequency_hz <= 0.0) {
    return MakeNativeDsdFailure(config.dsd,
                                "native DSD sine frequency must be positive");
  }
  if (!std::isfinite(config.amplitude_dbfs) || config.amplitude_dbfs > 0.0) {
    return MakeNativeDsdFailure(config.dsd,
                                "native DSD sine amplitude must be <= 0 dBFS");
  }

  const std::uint32_t dsd_rate_hz =
      NativeDsdRateHz(config.dsd.dsd_rate_multiplier);
  if (config.frequency_hz >= static_cast<double>(dsd_rate_hz) / 2.0) {
    return MakeNativeDsdFailure(config.dsd,
                                "native DSD sine frequency exceeds DSD Nyquist");
  }

  EnsureChannelState(config.dsd.channel_count);
  NativeDsdGenerationResult result;
  result.ok = true;
  result.format = MakeNativeDsdFormat(config.dsd);
  result.channel_payloads.resize(config.dsd.channel_count);
  for (auto& payload : result.channel_payloads) {
    payload.reserve(byte_count_per_channel);
  }

  const double amplitude = DbfsToLinearAmplitude(config.amplitude_dbfs);
  const double phase_step =
      2.0 * kPi * config.frequency_hz / static_cast<double>(dsd_rate_hz);

  for (std::uint32_t byte_index = 0;
       byte_index < byte_count_per_channel;
       ++byte_index) {
    if (config.dsd.sample_type == SampleType::kDsd8Bit) {
      for (std::uint32_t channel = 0;
           channel < config.dsd.channel_count;
           ++channel) {
        const double target =
            amplitude * std::sin(phase_step * static_cast<double>(dsd_sample_index_));
        double& modulator = modulators_[channel];
        modulator += target;
        const bool one = modulator >= 0.0;
        modulator += one ? -1.0 : 1.0;
        result.channel_payloads[channel].push_back(one ? 0x01 : 0x00);
      }
      ++dsd_sample_index_;
      continue;
    }

    std::vector<std::array<bool, 8>> channel_bits(config.dsd.channel_count);
    for (std::uint32_t bit = 0; bit < 8; ++bit) {
      const double target =
          amplitude * std::sin(phase_step * static_cast<double>(dsd_sample_index_));
      for (std::uint32_t channel = 0;
           channel < config.dsd.channel_count;
           ++channel) {
        double& modulator = modulators_[channel];
        modulator += target;
        const bool one = modulator >= 0.0;
        modulator += one ? -1.0 : 1.0;
        channel_bits[channel][bit] = one;
      }
      ++dsd_sample_index_;
    }

    for (std::uint32_t channel = 0;
         channel < config.dsd.channel_count;
         ++channel) {
      result.channel_payloads[channel].push_back(
          PackOneBitDsdByte(config.dsd.sample_type,
                            channel_bits[channel].data()));
    }
  }

  return result;
}

NativeDsdGenerationResult NativeDsdSineGenerator::GenerateIdle(
    const NativeDsdConfig& config,
    std::uint32_t byte_count_per_channel) {
  return GenerateNativeDsdIdle(config, byte_count_per_channel);
}

}  // namespace aptt
