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

#include "components/audio_modem/audio_player.h"

#include "base/bind.h"
#include "base/memory/weak_ptr.h"
#include "components/audio_modem/audio_player_impl.h"
#include "components/audio_modem/public/audio_modem_types.h"
#include "components/audio_modem/test/random_samples.h"
#include "media/audio/audio_manager.h"
#include "media/audio/audio_manager_base.h"
#include "media/base/audio_bus.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

class TestAudioOutputStream : public media::AudioOutputStream {
 public:
  using GatherSamplesCallback =
      base::Callback<void(scoped_ptr<media::AudioBus>, int frames)>;
  TestAudioOutputStream(int default_frame_count,
                        int max_frame_count,
                        GatherSamplesCallback gather_callback)
      : default_frame_count_(default_frame_count),
        max_frame_count_(max_frame_count),
        gather_callback_(gather_callback),
        callback_(nullptr) {
    caller_loop_ = base::MessageLoop::current();
  }

  ~TestAudioOutputStream() override {}

  bool Open() override { return true; }
  void Start(AudioSourceCallback* callback) override {
    callback_ = callback;
    GatherPlayedSamples();
  }
  void Stop() override {}
  void SetVolume(double volume) override {}
  void GetVolume(double* volume) override {}
  void Close() override {}

 private:
  void GatherPlayedSamples() {
    int frames = 0, total_frames = 0;
    do {
      // Call back into the player to get samples that it wants us to play.
      scoped_ptr<media::AudioBus> dest =
          media::AudioBus::Create(1, default_frame_count_);
      frames = callback_->OnMoreData(dest.get(), 0);
      total_frames += frames;
      // Send the samples given to us by the player to the gather callback.
      caller_loop_->PostTask(
          FROM_HERE, base::Bind(gather_callback_, base::Passed(&dest), frames));
    } while (frames && total_frames < max_frame_count_);
  }

  int default_frame_count_;
  int max_frame_count_;
  GatherSamplesCallback gather_callback_;
  AudioSourceCallback* callback_;
  base::MessageLoop* caller_loop_;

  DISALLOW_COPY_AND_ASSIGN(TestAudioOutputStream);
};

}  // namespace

namespace audio_modem {

class AudioPlayerTest : public testing::Test,
                        public base::SupportsWeakPtr<AudioPlayerTest> {
 public:
  AudioPlayerTest() : buffer_index_(0), player_(nullptr) {
    if (!media::AudioManager::Get())
      media::AudioManager::CreateForTesting();
  }

  ~AudioPlayerTest() override { DeletePlayer(); }

  void CreatePlayer() {
    DeletePlayer();
    player_ = new AudioPlayerImpl();
    player_->set_output_stream_for_testing(new TestAudioOutputStream(
        kDefaultFrameCount,
        kMaxFrameCount,
        base::Bind(&AudioPlayerTest::GatherSamples, AsWeakPtr())));
    player_->Initialize();
  }

  void DeletePlayer() {
    if (!player_)
      return;
    player_->Finalize();
    player_ = nullptr;
  }

  void PlayAndVerifySamples(
      const scoped_refptr<media::AudioBusRefCounted>& samples) {
    DCHECK_LT(samples->frames(), kMaxFrameCount);

    buffer_ = media::AudioBus::Create(1, kMaxFrameCount);
    buffer_index_ = 0;
    player_->Play(samples);
    player_->FlushAudioLoopForTesting();
    player_->Stop();

    int differences = 0;
    for (int i = 0; i < kMaxFrameCount; ++i) {
      differences += (buffer_->channel(0)[i] !=
                      samples->channel(0)[i % samples->frames()]);
    }
    ASSERT_EQ(0, differences);

    buffer_.reset();
  }

  void GatherSamples(scoped_ptr<media::AudioBus> bus, int frames) {
    if (!buffer_.get())
      return;
    bus->CopyPartialFramesTo(0, frames, buffer_index_, buffer_.get());
    buffer_index_ += frames;
  }

 protected:
  bool IsPlaying() {
    player_->FlushAudioLoopForTesting();
    return player_->is_playing_;
  }

  static const int kDefaultFrameCount = 1024;
  static const int kMaxFrameCount = 1024 * 100;

  scoped_ptr<media::AudioBus> buffer_;
  int buffer_index_;

  // Deleted by calling Finalize() on the object.
  AudioPlayerImpl* player_;
  base::MessageLoop message_loop_;
};

TEST_F(AudioPlayerTest, BasicPlayAndStop) {
  CreatePlayer();
  scoped_refptr<media::AudioBusRefCounted> samples =
      media::AudioBusRefCounted::Create(1, 7331);

  player_->Play(samples);
  EXPECT_TRUE(IsPlaying());
  player_->Stop();
  EXPECT_FALSE(IsPlaying());
  player_->Play(samples);

  EXPECT_TRUE(IsPlaying());
  player_->Stop();
  EXPECT_FALSE(IsPlaying());
  player_->Play(samples);

  EXPECT_TRUE(IsPlaying());
  player_->Stop();
  EXPECT_FALSE(IsPlaying());

  DeletePlayer();
}

TEST_F(AudioPlayerTest, OutOfOrderPlayAndStopMultiple) {
  CreatePlayer();
  scoped_refptr<media::AudioBusRefCounted> samples =
      media::AudioBusRefCounted::Create(1, 1337);

  player_->Stop();
  player_->Stop();
  player_->Stop();
  EXPECT_FALSE(IsPlaying());

  player_->Play(samples);
  player_->Play(samples);
  EXPECT_TRUE(IsPlaying());

  player_->Stop();
  player_->Stop();
  EXPECT_FALSE(IsPlaying());

  DeletePlayer();
}

TEST_F(AudioPlayerTest, PlayingEndToEnd) {
  const int kNumSamples = kDefaultFrameCount * 7 + 321;
  CreatePlayer();

  PlayAndVerifySamples(CreateRandomAudioRefCounted(0x1337, 1, kNumSamples));

  DeletePlayer();
}

TEST_F(AudioPlayerTest, PlayingEndToEndRepeated) {
  const int kNumSamples = kDefaultFrameCount * 7 + 537;
  CreatePlayer();

  PlayAndVerifySamples(CreateRandomAudioRefCounted(0x1337, 1, kNumSamples));

  PlayAndVerifySamples(
      CreateRandomAudioRefCounted(0x7331, 1, kNumSamples - 3123));

  PlayAndVerifySamples(CreateRandomAudioRefCounted(0xf00d, 1, kNumSamples * 2));

  DeletePlayer();
}

}  // namespace audio_modem