// 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 "media/mojo/services/mojo_cdm_service_context.h"

#include "base/logging.h"
#include "media/base/callback_registry.h"
#include "media/base/cdm_context.h"
#include "media/base/content_decryption_module.h"
#include "media/cdm/cdm_context_ref_impl.h"
#include "media/mojo/services/mojo_cdm_service.h"

#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
#include "media/mojo/services/mojo_cdm_proxy_service.h"
#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)

namespace media {

namespace {

// Helper function to get the next unique (per-process) CDM ID to be assigned to
// a CDM or CdmProxy. It will be used to locate the CDM by the media players
// living in the same process.
int GetNextCdmId() {
  static int g_next_cdm_id = CdmContext::kInvalidCdmId + 1;
  return g_next_cdm_id++;
}

#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
class CdmProxyContextRef : public CdmContextRef, public CdmContext {
 public:
  explicit CdmProxyContextRef(base::WeakPtr<CdmContext> cdm_context)
      : cdm_context_(cdm_context) {}
  ~CdmProxyContextRef() final {}

  // CdmContextRef implementation.
  CdmContext* GetCdmContext() final { return this; }

 private:
  // CdmContext implementation.
  std::unique_ptr<CallbackRegistration> RegisterNewKeyCB(
      base::RepeatingClosure new_key_cb) final {
    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
    return cdm_context_ ? cdm_context_->RegisterNewKeyCB(std::move(new_key_cb))
                        : nullptr;
  }

  Decryptor* GetDecryptor() final {
    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
    return cdm_context_ ? cdm_context_->GetDecryptor() : nullptr;
  }

  CdmProxyContext* GetCdmProxyContext() final {
    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
    return cdm_context_ ? cdm_context_->GetCdmProxyContext() : nullptr;
  }

  base::WeakPtr<CdmContext> cdm_context_;
  THREAD_CHECKER(thread_checker_);

  DISALLOW_COPY_AND_ASSIGN(CdmProxyContextRef);
};
#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)

}  // namespace

MojoCdmServiceContext::MojoCdmServiceContext() = default;

MojoCdmServiceContext::~MojoCdmServiceContext() = default;

int MojoCdmServiceContext::RegisterCdm(MojoCdmService* cdm_service) {
  DCHECK(cdm_service);
  int cdm_id = GetNextCdmId();
  cdm_services_[cdm_id] = cdm_service;
  DVLOG(1) << __func__ << ": CdmService registered with CDM ID " << cdm_id;
  return cdm_id;
}

void MojoCdmServiceContext::UnregisterCdm(int cdm_id) {
  DVLOG(1) << __func__ << ": cdm_id = " << cdm_id;
  DCHECK(cdm_services_.count(cdm_id));
  cdm_services_.erase(cdm_id);
}

#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
int MojoCdmServiceContext::RegisterCdmProxy(
    MojoCdmProxyService* cdm_proxy_service) {
  DCHECK(cdm_proxy_service);
  int cdm_id = GetNextCdmId();
  cdm_proxy_services_[cdm_id] = cdm_proxy_service;
  DVLOG(1) << __func__ << ": CdmProxyService registered with CDM ID " << cdm_id;
  return cdm_id;
}

void MojoCdmServiceContext::UnregisterCdmProxy(int cdm_id) {
  DVLOG(1) << __func__ << ": cdm_id = " << cdm_id;
  DCHECK(cdm_proxy_services_.count(cdm_id));
  cdm_proxy_services_.erase(cdm_id);
}
#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)

std::unique_ptr<CdmContextRef> MojoCdmServiceContext::GetCdmContextRef(
    int cdm_id) {
  DVLOG(1) << __func__ << ": cdm_id = " << cdm_id;

  // Check all CDMs first.
  auto cdm_service = cdm_services_.find(cdm_id);
  if (cdm_service != cdm_services_.end()) {
    if (!cdm_service->second->GetCdm()->GetCdmContext()) {
      NOTREACHED() << "All CDMs should support CdmContext.";
      return nullptr;
    }
    return std::make_unique<CdmContextRefImpl>(cdm_service->second->GetCdm());
  }

#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
  // Next check all CdmProxies.
  auto cdm_proxy_service = cdm_proxy_services_.find(cdm_id);
  if (cdm_proxy_service != cdm_proxy_services_.end()) {
    return std::make_unique<CdmProxyContextRef>(
        cdm_proxy_service->second->GetCdmContext());
  }
#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)

  LOG(ERROR) << "CdmContextRef cannot be obtained for CDM ID: " << cdm_id;
  return nullptr;
}

}  // namespace media
