// 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 "media/remoting/fake_media_resource.h"

#include <vector>

#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "media/base/decoder_buffer.h"
#include "media/base/media_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"

using testing::_;
using testing::Invoke;
using testing::Return;

namespace media {
namespace remoting {

FakeDemuxerStream::FakeDemuxerStream(bool is_audio) {
  type_ = is_audio ? DemuxerStream::AUDIO : DemuxerStream::VIDEO;
  if (is_audio) {
    audio_config_.Initialize(
        AudioCodec::kAAC, kSampleFormatS16, ChannelLayoutConfig::Stereo(),
        38400, std::vector<uint8_t>(), EncryptionScheme::kUnencrypted,
        base::TimeDelta(), 0);
  } else {
    gfx::Size size(640, 480);
    gfx::Rect rect(0, 0, 640, 480);
    video_config_.Initialize(VideoCodec::kH264, H264PROFILE_BASELINE,
                             VideoDecoderConfig::AlphaMode::kIsOpaque,
                             VideoColorSpace::REC601(), kNoTransformation, size,
                             rect, size, std::vector<uint8_t>(),
                             EncryptionScheme::kUnencrypted);
  }
  ON_CALL(*this, Read)
      .WillByDefault(Invoke(this, &FakeDemuxerStream::FakeRead));
}

FakeDemuxerStream::~FakeDemuxerStream() = default;

// Only return one buffer at a time so we ignore the count.
void FakeDemuxerStream::FakeRead(uint32_t /*count*/, ReadCB read_cb) {
  if (buffer_queue_.empty()) {
    // Silent return to simulate waiting for buffer available.
    pending_read_cb_ = std::move(read_cb);
    return;
  }
  scoped_refptr<DecoderBuffer> buffer = buffer_queue_.front();
  buffer_queue_.pop_front();
  std::move(read_cb).Run(kOk, {std::move(buffer)});
}

AudioDecoderConfig FakeDemuxerStream::audio_decoder_config() {
  return audio_config_;
}

VideoDecoderConfig FakeDemuxerStream::video_decoder_config() {
  return video_config_;
}

DemuxerStream::Type FakeDemuxerStream::type() const {
  return type_;
}

StreamLiveness FakeDemuxerStream::liveness() const {
  return StreamLiveness::kUnknown;
}

bool FakeDemuxerStream::SupportsConfigChanges() {
  return false;
}

void FakeDemuxerStream::CreateFakeFrame(size_t size,
                                        bool key_frame,
                                        int pts_ms) {
  std::vector<uint8_t> buffer(size);
  // Assign each byte in the buffer its index mod 256.
  for (size_t i = 0; i < size; ++i) {
    buffer[i] = static_cast<uint8_t>(i & 0xFF);
  }
  base::TimeDelta pts = base::Milliseconds(pts_ms);

  // To DecoderBuffer
  scoped_refptr<DecoderBuffer> input_buffer = DecoderBuffer::CopyFrom(buffer);
  input_buffer->set_timestamp(pts);
  input_buffer->set_is_key_frame(key_frame);

  // Sends frame out if there is pending read callback. Otherwise, stores it
  // in the buffer queue.
  if (!pending_read_cb_) {
    buffer_queue_.push_back(input_buffer);
  } else {
    std::move(pending_read_cb_).Run(kOk, {std::move(input_buffer)});
  }
}

FakeMediaResource::FakeMediaResource()
    : audio_stream_(new FakeDemuxerStream(true)),
      video_stream_(new FakeDemuxerStream(false)) {}

FakeMediaResource::~FakeMediaResource() = default;

std::vector<raw_ptr<DemuxerStream>> FakeMediaResource::GetAllStreams() {
  std::vector<raw_ptr<DemuxerStream>> streams;
  streams.push_back(audio_stream_.get());
  streams.push_back(video_stream_.get());
  return streams;
}

}  // namespace remoting
}  // namespace media
