// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/protocol/audio_decode_scheduler.h"

#include <utility>

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/run_loop.h"
#include "base/test/task_environment.h"
#include "base/threading/thread.h"
#include "remoting/base/auto_thread.h"
#include "remoting/base/auto_thread_task_runner.h"
#include "remoting/proto/audio.pb.h"
#include "remoting/protocol/session_config.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace remoting::protocol {

namespace {

const int kAudioSampleBytes = 4;
const uint8_t kDummyAudioData = 0x8B;

class FakeAudioConsumer : public AudioStub {
 public:
  FakeAudioConsumer() {}

  FakeAudioConsumer(const FakeAudioConsumer&) = delete;
  FakeAudioConsumer& operator=(const FakeAudioConsumer&) = delete;

  ~FakeAudioConsumer() override = default;

  base::WeakPtr<FakeAudioConsumer> GetWeakPtr() {
    return weak_factory_.GetWeakPtr();
  }

  // AudioStub implementation.
  void ProcessAudioPacket(std::unique_ptr<AudioPacket> packet,
                          base::OnceClosure done) override {
    if (!done.is_null()) {
      std::move(done).Run();
    }
  }

 private:
  base::WeakPtrFactory<FakeAudioConsumer> weak_factory_{this};
};

}  // namespace

class AudioDecodeSchedulerTest : public ::testing::Test {
 public:
  AudioDecodeSchedulerTest() = default;

  void SetUp() override;
  void TearDown() override;

 protected:
  base::test::SingleThreadTaskEnvironment task_environment_;
  base::RunLoop run_loop_;
  scoped_refptr<AutoThreadTaskRunner> audio_decode_task_runner_;
  scoped_refptr<AutoThreadTaskRunner> main_task_runner_;
  std::unique_ptr<SessionConfig> session_config_;
};

void AudioDecodeSchedulerTest::SetUp() {
  main_task_runner_ = new AutoThreadTaskRunner(
      task_environment_.GetMainThreadTaskRunner(), run_loop_.QuitClosure());
  audio_decode_task_runner_ = AutoThread::Create("decode", main_task_runner_);
  session_config_ = SessionConfig::ForTestWithAudio();
}

void AudioDecodeSchedulerTest::TearDown() {
  // Release the task runners, so that the test can quit.
  audio_decode_task_runner_ = nullptr;
  main_task_runner_ = nullptr;

  // Run the MessageLoop until everything has torn down.
  run_loop_.Run();
}

// TODO(nicholss): Could share the following in a common class for use
// in other places.
std::unique_ptr<AudioPacket> CreatePacketWithSamplingRate_(
    AudioPacket::SamplingRate rate,
    int samples) {
  std::unique_ptr<AudioPacket> packet(new AudioPacket());
  packet->set_encoding(AudioPacket::ENCODING_RAW);
  packet->set_sampling_rate(rate);
  packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2);
  packet->set_channels(AudioPacket::CHANNELS_STEREO);

  // The data must be a multiple of 4 bytes (channels x bytes_per_sample).
  std::string data;
  data.resize(samples * kAudioSampleBytes, kDummyAudioData);
  packet->add_data(data);

  return packet;
}

std::unique_ptr<AudioPacket> CreatePacket44100Hz_(int samples) {
  return CreatePacketWithSamplingRate_(AudioPacket::SAMPLING_RATE_44100,
                                       samples);
}

std::unique_ptr<AudioPacket> CreatePacket48000Hz_(int samples) {
  return CreatePacketWithSamplingRate_(AudioPacket::SAMPLING_RATE_48000,
                                       samples);
}

TEST_F(AudioDecodeSchedulerTest, Shutdown) {
  std::unique_ptr<FakeAudioConsumer> audio_consumer(new FakeAudioConsumer());
  std::unique_ptr<AudioDecodeScheduler> audio_scheduler(
      new AudioDecodeScheduler(audio_decode_task_runner_,
                               audio_consumer->GetWeakPtr()));

  audio_scheduler->Initialize(*session_config_);

  audio_scheduler->ProcessAudioPacket(CreatePacket44100Hz_(1000),
                                      base::DoNothing());

  audio_scheduler.reset();
  audio_consumer.reset();
  // TODO(nicholss): This test does not really test anything. Add a way to get a
  // count of the calls to AddAudioPacket.
}

}  // namespace remoting::protocol