// Copyright 2018 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 "services/audio/delay_buffer.h"

#include <algorithm>

#include "media/base/audio_bus.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace audio {
namespace {

constexpr int kChannels = 1;
constexpr int kMaxFrames = 32;

#define EXPECT_BUS_VALUES_EQ(bus, begin, end, value)                        \
  {                                                                         \
    const auto IsValue = [](float x) { return x == (value); };              \
    EXPECT_TRUE(std::all_of((bus)->channel(0) + (begin),                    \
                            (bus)->channel(0) + (end) - (begin), IsValue)); \
  }

TEST(DelayBufferTest, RecordsAMaximumNumberOfFrames) {
  DelayBuffer buffer(kMaxFrames);
  ASSERT_EQ(buffer.GetBeginPosition(), buffer.GetEndPosition());

  constexpr int frames_per_bus = kMaxFrames / 4;
  const auto bus = media::AudioBus::Create(kChannels, frames_per_bus);
  std::fill(bus->channel(0), bus->channel(0) + frames_per_bus, 1.0);

  // Fill the buffer.
  DelayBuffer::FrameTicks position = 0;
  for (int i = 0; i < 4; ++i) {
    buffer.Write(position, *bus, 1.0);
    position += frames_per_bus;
    EXPECT_EQ(0, buffer.GetBeginPosition());
    EXPECT_EQ(position, buffer.GetEndPosition());
  }

  // Writing just one more bus should cause the leading frames to be dropped.
  buffer.Write(position, *bus, 1.0);
  position += frames_per_bus;
  EXPECT_EQ(position - kMaxFrames, buffer.GetBeginPosition());
  EXPECT_EQ(position, buffer.GetEndPosition());

  // Now, simulate a gap in the recording by recording the next bus late.
  position += frames_per_bus * 2;
  buffer.Write(position, *bus, 1.0);
  position += frames_per_bus;
  EXPECT_EQ(position - kMaxFrames, buffer.GetBeginPosition());
  EXPECT_EQ(position, buffer.GetEndPosition());
}

TEST(DelayBufferTest, ReadsSilenceIfNothingWasRecorded) {
  DelayBuffer buffer(kMaxFrames);
  ASSERT_EQ(buffer.GetBeginPosition(), buffer.GetEndPosition());

  DelayBuffer::FrameTicks position = 0;
  constexpr int frames_per_bus = kMaxFrames / 4;
  const auto bus = media::AudioBus::Create(kChannels, frames_per_bus);

  for (int i = 0; i < 10; ++i) {
    // Set data in the bus to confirm it is all going to be overwritten.
    std::fill(bus->channel(0), bus->channel(0) + frames_per_bus, 1.0);

    buffer.Read(position, frames_per_bus, bus.get());
    EXPECT_EQ(buffer.GetBeginPosition(), buffer.GetEndPosition());
    EXPECT_BUS_VALUES_EQ(bus, 0, frames_per_bus, 0.0);

    position += frames_per_bus;
  }
}

TEST(DelayBufferTest, ReadsSilenceIfOutsideRecordedRange) {
  DelayBuffer buffer(kMaxFrames);
  ASSERT_EQ(buffer.GetBeginPosition(), buffer.GetEndPosition());

  constexpr int frames_per_bus = kMaxFrames / 4;
  const auto bus = media::AudioBus::Create(kChannels, frames_per_bus);
  std::fill(bus->channel(0), bus->channel(0) + frames_per_bus, 1.0);

  // Fill the buffer.
  DelayBuffer::FrameTicks position = 0;
  for (int i = 0; i < 4; ++i) {
    buffer.Write(position, *bus, 1.0);
    position += frames_per_bus;
  }
  EXPECT_EQ(0, buffer.GetBeginPosition());
  EXPECT_EQ(position, buffer.GetEndPosition());

  // Read before the begin position and expect to get silence.
  std::fill(bus->channel(0), bus->channel(0) + frames_per_bus, 0.5);
  buffer.Read(-kMaxFrames, frames_per_bus, bus.get());
  EXPECT_BUS_VALUES_EQ(bus, 0, frames_per_bus, 0.0);

  // Read at a position one before the begin position. Expect the first sample
  // to be 0.0, and the rest 1.0.
  std::fill(bus->channel(0), bus->channel(0) + frames_per_bus, 0.5);
  buffer.Read(buffer.GetBeginPosition() - 1, frames_per_bus, bus.get());
  EXPECT_EQ(0.0, bus->channel(0)[0]);
  EXPECT_BUS_VALUES_EQ(bus, 1, frames_per_bus - 1, 1.0);

  // Read at a position where the last sample should be 0.0 and the rest 1.0.
  std::fill(bus->channel(0), bus->channel(0) + frames_per_bus, 0.5);
  buffer.Read(buffer.GetEndPosition() - frames_per_bus + 1, frames_per_bus,
              bus.get());
  EXPECT_BUS_VALUES_EQ(bus, 0, frames_per_bus - 1, 1.0);
  EXPECT_EQ(0.0, bus->channel(0)[frames_per_bus - 1]);

  // Read after the end position and expect to get silence.
  std::fill(bus->channel(0), bus->channel(0) + frames_per_bus, 0.5);
  buffer.Read(kMaxFrames, frames_per_bus, bus.get());
  EXPECT_BUS_VALUES_EQ(bus, 0, frames_per_bus, 0.0);
}

TEST(DelayBufferTest, ReadsGapsInRecording) {
  DelayBuffer buffer(kMaxFrames);
  ASSERT_EQ(buffer.GetBeginPosition(), buffer.GetEndPosition());

  constexpr int frames_per_bus = kMaxFrames / 4;
  const auto bus = media::AudioBus::Create(kChannels, frames_per_bus);
  std::fill(bus->channel(0), bus->channel(0) + frames_per_bus, 1.0);

  // Fill the buffer, but with a gap in the third quarter of it.
  DelayBuffer::FrameTicks record_position = 0;
  for (int i = 0; i < 4; ++i) {
    if (i != 2) {
      buffer.Write(record_position, *bus, 1.0);
    }
    record_position += frames_per_bus;
  }
  EXPECT_EQ(0, buffer.GetBeginPosition());
  EXPECT_EQ(record_position, buffer.GetEndPosition());

  // Read through the whole range, but offset by one frame early. Confirm the
  // silence gap appears in the right place.
  DelayBuffer::FrameTicks read_position = -1;
  std::fill(bus->channel(0), bus->channel(0) + frames_per_bus, 0.5);
  buffer.Read(read_position, frames_per_bus, bus.get());
  read_position += frames_per_bus;
  EXPECT_EQ(0.0, bus->channel(0)[0]);
  EXPECT_BUS_VALUES_EQ(bus, 1, frames_per_bus - 1, 1.0);

  std::fill(bus->channel(0), bus->channel(0) + frames_per_bus, 0.5);
  buffer.Read(read_position, frames_per_bus, bus.get());
  read_position += frames_per_bus;
  EXPECT_BUS_VALUES_EQ(bus, 0, frames_per_bus, 1.0);

  std::fill(bus->channel(0), bus->channel(0) + frames_per_bus, 0.5);
  buffer.Read(read_position, frames_per_bus, bus.get());
  read_position += frames_per_bus;
  EXPECT_EQ(1.0, bus->channel(0)[0]);
  // The gap begins.
  EXPECT_BUS_VALUES_EQ(bus, 1, frames_per_bus - 1, 0.0);

  std::fill(bus->channel(0), bus->channel(0) + frames_per_bus, 0.5);
  buffer.Read(read_position, frames_per_bus, bus.get());
  read_position += frames_per_bus;
  EXPECT_EQ(0.0, bus->channel(0)[0]);
  // The gap ends.
  EXPECT_BUS_VALUES_EQ(bus, 1, frames_per_bus - 1, 1.0);
}

}  // namespace
}  // namespace audio
