#include "core/signal_generator.h"

#include <cmath>

namespace aptt {
namespace {

constexpr double kPi = 3.141592653589793238462643383279502884;
constexpr double kTwoPi = 2.0 * kPi;
constexpr double kDegreesToRadians = kPi / 180.0;

bool ChannelEnabled(ChannelAssignment assignment,
                    std::uint32_t frame_index,
                    std::uint32_t channel_index) {
  switch (assignment) {
    case ChannelAssignment::kAllChannels:
      return true;
    case ChannelAssignment::kLeftOnly:
      return channel_index == 0;
    case ChannelAssignment::kRightOnly:
      return channel_index == 1;
    case ChannelAssignment::kAlternatingLeftRight:
      return (frame_index % 2) == (channel_index % 2);
  }
  return false;
}

}  // namespace

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

void PcmSineGenerator::ResetPhase() {
  phase_radians_ = 0.0;
}

std::vector<float> PcmSineGenerator::GenerateInterleavedFloat32(
    const PcmSineConfig& config,
    std::uint32_t frame_count) {
  const std::uint32_t channel_count = config.channel_count == 0 ? 1 : config.channel_count;
  std::vector<float> samples(static_cast<std::size_t>(frame_count) * channel_count);
  const double amplitude = DbfsToLinear(config.amplitude_dbfs);
  const double phase_increment = kTwoPi * config.frequency_hz / config.sample_rate_hz;
  const double right_phase_offset =
      config.right_channel_phase_offset_degrees * kDegreesToRadians;

  for (std::uint32_t frame = 0; frame < frame_count; ++frame) {
    for (std::uint32_t channel = 0; channel < channel_count; ++channel) {
      const double channel_phase =
          channel == 1 ? phase_radians_ + right_phase_offset : phase_radians_;
      const double value = std::sin(channel_phase) * amplitude;
      double channel_value = ChannelEnabled(config.channel_assignment, frame, channel)
                                 ? value
                                 : 0.0;
      if ((channel == 0 && config.invert_left) ||
          (channel == 1 && config.invert_right)) {
        channel_value = -channel_value;
      }
      samples[static_cast<std::size_t>(frame) * channel_count + channel] =
          static_cast<float>(channel_value);
    }

    phase_radians_ += phase_increment;
    while (phase_radians_ >= kTwoPi) {
      phase_radians_ -= kTwoPi;
    }
  }

  return samples;
}

std::vector<float> GenerateInterleavedSilenceFloat32(std::uint32_t frame_count,
                                                     std::uint32_t channel_count) {
  const std::uint32_t safe_channel_count = channel_count == 0 ? 1 : channel_count;
  return std::vector<float>(static_cast<std::size_t>(frame_count) * safe_channel_count);
}

}  // namespace aptt
