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

#include "chromeos/components/cdm_factory_daemon/cdm_context_for_oopvd_impl.h"

#include "base/functional/callback.h"
#include "base/task/bind_post_task.h"
#include "chromeos/components/cdm_factory_daemon/chromeos_cdm_context.h"
#include "chromeos/components/cdm_factory_daemon/chromeos_cdm_factory.h"
#include "media/mojo/common/media_type_converters.h"
#include "media/mojo/common/validation_utils.h"

namespace chromeos {

CdmContextForOOPVDImpl::CdmContextForOOPVDImpl(media::CdmContext* cdm_context)
    : cdm_context_(cdm_context) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK(cdm_context_);
  DCHECK(cdm_context_->GetChromeOsCdmContext());
  cdm_context_ref_ = cdm_context_->GetChromeOsCdmContext()->GetCdmContextRef();
}

CdmContextForOOPVDImpl::~CdmContextForOOPVDImpl() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}

void CdmContextForOOPVDImpl::GetHwKeyData(
    media::mojom::DecryptConfigPtr decrypt_config,
    const std::vector<uint8_t>& hw_identifier,
    GetHwKeyDataCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  std::unique_ptr<media::DecryptConfig> media_decrypt_config =
      media::ValidateAndConvertMojoDecryptConfig(std::move(decrypt_config));
  if (!media_decrypt_config) {
    CHECK(mojo::IsInMessageDispatch());
    mojo::ReportBadMessage("Invalid DecryptConfig received");
    return;
  }

  cdm_context_->GetChromeOsCdmContext()->GetHwKeyData(
      media_decrypt_config.get(), hw_identifier,
      base::BindPostTaskToCurrentDefault(std::move(callback)));
}

void CdmContextForOOPVDImpl::RegisterEventCallback(
    mojo::PendingRemote<media::mojom::CdmContextEventCallback> callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  // Note: we don't need to use base::BindPostTaskToCurrentDefault() for either
  // |callback| or the callback we pass to RegisterEventCB() because the
  // documentation for media::CdmContext::RegisterEventCB() says that "[t]he
  // registered callback will always be called on the thread where
  // RegisterEventCB() is called."
  remote_event_callbacks_.Add(std::move(callback));
  if (!callback_registration_) {
    callback_registration_ = cdm_context_->RegisterEventCB(
        base::BindRepeating(&CdmContextForOOPVDImpl::CdmEventCallback,
                            weak_ptr_factory_.GetWeakPtr()));
  }
}

void CdmContextForOOPVDImpl::GetHwConfigData(GetHwConfigDataCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  ChromeOsCdmFactory::GetHwConfigData(
      base::BindPostTaskToCurrentDefault(std::move(callback)));
}

void CdmContextForOOPVDImpl::GetScreenResolutions(
    GetScreenResolutionsCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  ChromeOsCdmFactory::GetScreenResolutions(
      base::BindPostTaskToCurrentDefault(std::move(callback)));
}

void CdmContextForOOPVDImpl::AllocateSecureBuffer(
    uint32_t size,
    AllocateSecureBufferCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  ChromeOsCdmFactory::AllocateSecureBuffer(
      size, base::BindPostTaskToCurrentDefault(std::move(callback)));
}

void CdmContextForOOPVDImpl::ParseEncryptedSliceHeader(
    uint64_t secure_handle,
    uint32_t offset,
    const std::vector<uint8_t>& stream_data,
    ParseEncryptedSliceHeaderCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  ChromeOsCdmFactory::ParseEncryptedSliceHeader(
      secure_handle, offset, stream_data,
      base::BindPostTaskToCurrentDefault(std::move(callback)));
}

void CdmContextForOOPVDImpl::DecryptVideoBuffer(
    media::mojom::DecoderBufferPtr decoder_buffer,
    const std::vector<uint8_t>& bytes,
    DecryptVideoBufferCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  CHECK(cdm_context_->GetDecryptor());

  scoped_refptr<media::DecoderBuffer> media_decoder_buffer =
      media::ValidateAndConvertMojoDecoderBuffer(std::move(decoder_buffer));
  if (!media_decoder_buffer) {
    CHECK(mojo::IsInMessageDispatch());
    mojo::ReportBadMessage("Invalid DecoderBuffer received");
    return;
  }
  CHECK_EQ(media_decoder_buffer->size(), bytes.size());
  UNSAFE_TODO(memcpy(media_decoder_buffer->writable_data(), bytes.data(),
                     bytes.size()));
  cdm_context_->GetDecryptor()->Decrypt(
      media::Decryptor::StreamType::kVideo, media_decoder_buffer,
      base::BindPostTaskToCurrentDefault(
          base::BindOnce(&CdmContextForOOPVDImpl::OnDecryptDone,
                         weak_ptr_factory_.GetWeakPtr(), std::move(callback))));
}

void CdmContextForOOPVDImpl::CdmEventCallback(media::CdmContext::Event event) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  for (auto& cb : remote_event_callbacks_) {
    cb->EventCallback(event);
  }
}

void CdmContextForOOPVDImpl::OnDecryptDone(
    DecryptVideoBufferCallback decrypt_video_buffer_cb,
    media::Decryptor::Status status,
    scoped_refptr<media::DecoderBuffer> decoder_buffer) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  std::vector<uint8_t> bytes;
  if (decoder_buffer) {
    bytes.insert(bytes.begin(), decoder_buffer->begin(), decoder_buffer->end());
  }

  media::mojom::DecoderBufferPtr mojo_decoder_buffer;
  if (decoder_buffer) {
    mojo_decoder_buffer = media::mojom::DecoderBuffer::From(*decoder_buffer);
    CHECK(mojo_decoder_buffer);
  }
  std::move(decrypt_video_buffer_cb)
      .Run(status, std::move(mojo_decoder_buffer), bytes);
}

}  // namespace chromeos
