/*
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "webrtc/modules/audio_processing/test/wav_based_simulator.h"

#include <stdio.h>
#include <iostream>

#include "webrtc/base/checks.h"
#include "webrtc/modules/audio_processing/test/test_utils.h"
#include "webrtc/test/testsupport/trace_to_stderr.h"

namespace webrtc {
namespace test {

std::vector<WavBasedSimulator::SimulationEventType>
WavBasedSimulator::GetCustomEventChain(const std::string& filename) {
  std::vector<WavBasedSimulator::SimulationEventType> call_chain;
  FILE* stream = OpenFile(filename.c_str(), "r");

  RTC_CHECK(stream) << "Could not open the custom call order file, reverting "
                       "to using the default call order";

  char c;
  size_t num_read = fread(&c, sizeof(char), 1, stream);
  while (num_read > 0) {
    switch (c) {
      case 'r':
        call_chain.push_back(SimulationEventType::kProcessReverseStream);
        break;
      case 'c':
        call_chain.push_back(SimulationEventType::kProcessStream);
        break;
      case '\n':
        break;
      default:
        FATAL() << "Incorrect custom call order file, reverting to using the "
                   "default call order";
        fclose(stream);
        return WavBasedSimulator::GetDefaultEventChain();
    }

    num_read = fread(&c, sizeof(char), 1, stream);
  }

  fclose(stream);
  return call_chain;
}

WavBasedSimulator::WavBasedSimulator(const SimulationSettings& settings)
      : AudioProcessingSimulator(settings) {}

WavBasedSimulator::~WavBasedSimulator() = default;

std::vector<WavBasedSimulator::SimulationEventType>
WavBasedSimulator::GetDefaultEventChain() {
  std::vector<WavBasedSimulator::SimulationEventType> call_chain(2);
  call_chain[0] = SimulationEventType::kProcessStream;
  call_chain[1] = SimulationEventType::kProcessReverseStream;
  return call_chain;
}

void WavBasedSimulator::PrepareProcessStreamCall() {
  if (settings_.fixed_interface) {
    CopyToAudioFrame(*in_buf_, &fwd_frame_);
  }
  ap_->set_stream_key_pressed(settings_.use_ts && (*settings_.use_ts));

  RTC_CHECK_EQ(AudioProcessing::kNoError,
               ap_->set_stream_delay_ms(
                   settings_.stream_delay ? *settings_.stream_delay : 0));

  ap_->echo_cancellation()->set_stream_drift_samples(
      settings_.stream_drift_samples ? *settings_.stream_drift_samples : 0);

  RTC_CHECK_EQ(AudioProcessing::kNoError,
               ap_->gain_control()->set_stream_analog_level(
                   last_specified_microphone_level_));
}

void WavBasedSimulator::PrepareReverseProcessStreamCall() {
  if (settings_.fixed_interface) {
    CopyToAudioFrame(*reverse_in_buf_, &rev_frame_);
  }
}

void WavBasedSimulator::Process() {
  std::unique_ptr<test::TraceToStderr> trace_to_stderr;
  if (settings_.use_verbose_logging) {
    trace_to_stderr.reset(new test::TraceToStderr(true));
  }

  if (settings_.custom_call_order_filename) {
    call_chain_ = WavBasedSimulator::GetCustomEventChain(
        *settings_.custom_call_order_filename);
  } else {
    call_chain_ = WavBasedSimulator::GetDefaultEventChain();
  }
  CreateAudioProcessor();

  Initialize();

  bool samples_left_to_process = true;
  int call_chain_index = 0;
  int num_forward_chunks_processed = 0;
  const int kOneBykChunksPerSecond =
      1.f / AudioProcessingSimulator::kChunksPerSecond;
  while (samples_left_to_process) {
    switch (call_chain_[call_chain_index]) {
      case SimulationEventType::kProcessStream:
        samples_left_to_process = HandleProcessStreamCall();
        ++num_forward_chunks_processed;
        break;
      case SimulationEventType::kProcessReverseStream:
        if (settings_.reverse_input_filename) {
          samples_left_to_process = HandleProcessReverseStreamCall();
        }
        break;
      default:
        RTC_CHECK(false);
    }

    call_chain_index = (call_chain_index + 1) % call_chain_.size();

    if (trace_to_stderr) {
      trace_to_stderr->SetTimeSeconds(num_forward_chunks_processed *
                                      kOneBykChunksPerSecond);
    }
  }

  DestroyAudioProcessor();
}

bool WavBasedSimulator::HandleProcessStreamCall() {
  bool samples_left_to_process = buffer_reader_->Read(in_buf_.get());
  if (samples_left_to_process) {
    PrepareProcessStreamCall();
    ProcessStream(settings_.fixed_interface);
    // Call stream analog level to ensure that any side-effects are triggered.
    (void)ap_->gain_control()->stream_analog_level();
    last_specified_microphone_level_ =
        ap_->gain_control()->stream_analog_level();
  }
  return samples_left_to_process;
}

bool WavBasedSimulator::HandleProcessReverseStreamCall() {
  bool samples_left_to_process =
      reverse_buffer_reader_->Read(reverse_in_buf_.get());
  if (samples_left_to_process) {
    PrepareReverseProcessStreamCall();
    ProcessReverseStream(settings_.fixed_interface);
  }
  return samples_left_to_process;
}

void WavBasedSimulator::Initialize() {
  std::unique_ptr<WavReader> in_file(
      new WavReader(settings_.input_filename->c_str()));
  int input_sample_rate_hz = in_file->sample_rate();
  int input_num_channels = in_file->num_channels();
  buffer_reader_.reset(new ChannelBufferWavReader(std::move(in_file)));

  int output_sample_rate_hz = settings_.output_sample_rate_hz
                                  ? *settings_.output_sample_rate_hz
                                  : input_sample_rate_hz;
  int output_num_channels = settings_.output_num_channels
                                ? *settings_.output_num_channels
                                : input_num_channels;

  int reverse_sample_rate_hz = 48000;
  int reverse_num_channels = 1;
  int reverse_output_sample_rate_hz = 48000;
  int reverse_output_num_channels = 1;
  if (settings_.reverse_input_filename) {
    std::unique_ptr<WavReader> reverse_in_file(
        new WavReader(settings_.reverse_input_filename->c_str()));
    reverse_sample_rate_hz = reverse_in_file->sample_rate();
    reverse_num_channels = reverse_in_file->num_channels();
    reverse_buffer_reader_.reset(
        new ChannelBufferWavReader(std::move(reverse_in_file)));

    reverse_output_sample_rate_hz =
        settings_.reverse_output_sample_rate_hz
            ? *settings_.reverse_output_sample_rate_hz
            : reverse_sample_rate_hz;
    reverse_output_num_channels = settings_.reverse_output_num_channels
                                      ? *settings_.reverse_output_num_channels
                                      : reverse_num_channels;
  }

  SetupBuffersConfigsOutputs(
      input_sample_rate_hz, output_sample_rate_hz, reverse_sample_rate_hz,
      reverse_output_sample_rate_hz, input_num_channels, output_num_channels,
      reverse_num_channels, reverse_output_num_channels);
}

}  // namespace test
}  // namespace webrtc
