// Copyright (c) 2012 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 "ppapi/cpp/private/content_decryptor_private.h"

#include <cstring>  // memcpy

#include "ppapi/c/ppb_var.h"
#include "ppapi/c/private/ppb_content_decryptor_private.h"
#include "ppapi/c/private/ppp_content_decryptor_private.h"
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/instance_handle.h"
#include "ppapi/cpp/logging.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/module_impl.h"
#include "ppapi/cpp/var.h"
#include "ppapi/cpp/var_array.h"

namespace pp {

namespace {

static const char kPPPContentDecryptorInterface[] =
    PPP_CONTENTDECRYPTOR_PRIVATE_INTERFACE;

void Initialize(PP_Instance instance,
                PP_Var key_system_arg) {
  void* object =
      Instance::GetPerInstanceObject(instance, kPPPContentDecryptorInterface);
  if (!object)
    return;

  pp::Var key_system_var(pp::PASS_REF, key_system_arg);
  if (!key_system_var.is_string())
    return;

  static_cast<ContentDecryptor_Private*>(object)->Initialize(
      key_system_var.AsString());
}

void SetServerCertificate(PP_Instance instance,
                          uint32_t promise_id,
                          PP_Var server_certificate_arg) {
  void* object =
      Instance::GetPerInstanceObject(instance, kPPPContentDecryptorInterface);
  if (!object)
    return;

  pp::Var server_certificate_var(server_certificate_arg);
  if (!server_certificate_var.is_array_buffer())
    return;
  pp::VarArrayBuffer server_certificate(server_certificate_var);

  static_cast<ContentDecryptor_Private*>(object)
      ->SetServerCertificate(promise_id, server_certificate);
}

void CreateSession(PP_Instance instance,
                   uint32_t promise_id,
                   PP_Var init_data_type_arg,
                   PP_Var init_data_arg,
                   PP_SessionType session_type) {
  void* object =
      Instance::GetPerInstanceObject(instance, kPPPContentDecryptorInterface);
  if (!object)
    return;

  pp::Var init_data_type_var(pp::PASS_REF, init_data_type_arg);
  if (!init_data_type_var.is_string())
    return;

  pp::Var init_data_var(pp::PASS_REF, init_data_arg);
  if (!init_data_var.is_array_buffer())
    return;
  pp::VarArrayBuffer init_data_array_buffer(init_data_var);

  static_cast<ContentDecryptor_Private*>(object)
      ->CreateSession(promise_id,
                      init_data_type_var.AsString(),
                      init_data_array_buffer,
                      session_type);
}

void LoadSession(PP_Instance instance,
                 uint32_t promise_id,
                 PP_Var web_session_id_arg) {
  void* object =
      Instance::GetPerInstanceObject(instance, kPPPContentDecryptorInterface);
  if (!object)
    return;

  pp::Var web_session_id_var(web_session_id_arg);
  if (!web_session_id_var.is_string())
    return;

  static_cast<ContentDecryptor_Private*>(object)
      ->LoadSession(promise_id, web_session_id_var.AsString());
}

void UpdateSession(PP_Instance instance,
                   uint32_t promise_id,
                   PP_Var web_session_id_arg,
                   PP_Var response_arg) {
  void* object =
      Instance::GetPerInstanceObject(instance, kPPPContentDecryptorInterface);
  if (!object)
    return;

  pp::Var web_session_id_var(web_session_id_arg);
  if (!web_session_id_var.is_string())
    return;

  pp::Var response_var(response_arg);
  if (!response_var.is_array_buffer())
    return;
  pp::VarArrayBuffer response(response_var);

  static_cast<ContentDecryptor_Private*>(object)
      ->UpdateSession(promise_id, web_session_id_var.AsString(), response);
}

void CloseSession(PP_Instance instance,
                  uint32_t promise_id,
                  PP_Var web_session_id_arg) {
  void* object =
      Instance::GetPerInstanceObject(instance, kPPPContentDecryptorInterface);
  if (!object)
    return;

  pp::Var web_session_id_var(web_session_id_arg);
  if (!web_session_id_var.is_string())
    return;

  static_cast<ContentDecryptor_Private*>(object)
      ->CloseSession(promise_id, web_session_id_var.AsString());
}

void RemoveSession(PP_Instance instance,
                   uint32_t promise_id,
                   PP_Var web_session_id_arg) {
  void* object =
      Instance::GetPerInstanceObject(instance, kPPPContentDecryptorInterface);
  if (!object)
    return;

  pp::Var web_session_id_var(web_session_id_arg);
  if (!web_session_id_var.is_string())
    return;

  static_cast<ContentDecryptor_Private*>(object)
      ->RemoveSession(promise_id, web_session_id_var.AsString());
}

void GetUsableKeyIds(PP_Instance instance,
                     uint32_t promise_id,
                     PP_Var web_session_id_arg) {
  void* object =
      Instance::GetPerInstanceObject(instance, kPPPContentDecryptorInterface);
  if (!object)
    return;

  pp::Var web_session_id_var(web_session_id_arg);
  if (!web_session_id_var.is_string())
    return;

  static_cast<ContentDecryptor_Private*>(object)
      ->GetUsableKeyIds(promise_id, web_session_id_var.AsString());
}

void Decrypt(PP_Instance instance,
             PP_Resource encrypted_resource,
             const PP_EncryptedBlockInfo* encrypted_block_info) {
  pp::Buffer_Dev encrypted_block(encrypted_resource);

  void* object =
      Instance::GetPerInstanceObject(instance, kPPPContentDecryptorInterface);
  if (!object)
    return;

  static_cast<ContentDecryptor_Private*>(object)->Decrypt(
      encrypted_block,
      *encrypted_block_info);
}

void InitializeAudioDecoder(
    PP_Instance instance,
    const PP_AudioDecoderConfig* decoder_config,
    PP_Resource extra_data_resource) {
  pp::Buffer_Dev extra_data_buffer(extra_data_resource);

  void* object =
      Instance::GetPerInstanceObject(instance, kPPPContentDecryptorInterface);
  if (!object)
    return;

  static_cast<ContentDecryptor_Private*>(object)->InitializeAudioDecoder(
      *decoder_config,
      extra_data_buffer);
}

void InitializeVideoDecoder(
    PP_Instance instance,
    const PP_VideoDecoderConfig* decoder_config,
    PP_Resource extra_data_resource) {
  pp::Buffer_Dev extra_data_buffer(extra_data_resource);

  void* object =
      Instance::GetPerInstanceObject(instance, kPPPContentDecryptorInterface);
  if (!object)
    return;

  static_cast<ContentDecryptor_Private*>(object)->InitializeVideoDecoder(
      *decoder_config,
      extra_data_buffer);
}

void DeinitializeDecoder(PP_Instance instance,
                         PP_DecryptorStreamType decoder_type,
                         uint32_t request_id) {
  void* object =
      Instance::GetPerInstanceObject(instance, kPPPContentDecryptorInterface);
  if (!object)
    return;
  static_cast<ContentDecryptor_Private*>(object)->DeinitializeDecoder(
      decoder_type,
      request_id);
}

void ResetDecoder(PP_Instance instance,
                  PP_DecryptorStreamType decoder_type,
                  uint32_t request_id) {
  void* object =
      Instance::GetPerInstanceObject(instance, kPPPContentDecryptorInterface);
  if (!object)
    return;
  static_cast<ContentDecryptor_Private*>(object)->ResetDecoder(decoder_type,
                                                               request_id);
}

void DecryptAndDecode(PP_Instance instance,
                      PP_DecryptorStreamType decoder_type,
                      PP_Resource encrypted_resource,
                      const PP_EncryptedBlockInfo* encrypted_block_info) {
  pp::Buffer_Dev encrypted_buffer(encrypted_resource);

  void* object =
      Instance::GetPerInstanceObject(instance, kPPPContentDecryptorInterface);
  if (!object)
    return;

  static_cast<ContentDecryptor_Private*>(object)->DecryptAndDecode(
      decoder_type,
      encrypted_buffer,
      *encrypted_block_info);
}

const PPP_ContentDecryptor_Private ppp_content_decryptor = {
    &Initialize,
    &SetServerCertificate,
    &CreateSession,
    &LoadSession,
    &UpdateSession,
    &CloseSession,
    &RemoveSession,
    &GetUsableKeyIds,
    &Decrypt,
    &InitializeAudioDecoder,
    &InitializeVideoDecoder,
    &DeinitializeDecoder,
    &ResetDecoder,
    &DecryptAndDecode};

template <> const char* interface_name<PPB_ContentDecryptor_Private>() {
  return PPB_CONTENTDECRYPTOR_PRIVATE_INTERFACE;
}

}  // namespace

ContentDecryptor_Private::ContentDecryptor_Private(Instance* instance)
    : associated_instance_(instance) {
  Module::Get()->AddPluginInterface(kPPPContentDecryptorInterface,
                                    &ppp_content_decryptor);
  instance->AddPerInstanceObject(kPPPContentDecryptorInterface, this);
}

ContentDecryptor_Private::~ContentDecryptor_Private() {
  Instance::RemovePerInstanceObject(associated_instance_,
                                    kPPPContentDecryptorInterface,
                                    this);
}

void ContentDecryptor_Private::PromiseResolved(uint32_t promise_id) {
  if (has_interface<PPB_ContentDecryptor_Private>()) {
    get_interface<PPB_ContentDecryptor_Private>()->PromiseResolved(
        associated_instance_.pp_instance(), promise_id);
  }
}

void ContentDecryptor_Private::PromiseResolvedWithSession(
    uint32_t promise_id,
    const std::string& web_session_id) {
  if (has_interface<PPB_ContentDecryptor_Private>()) {
    pp::Var web_session_id_var(web_session_id);
    get_interface<PPB_ContentDecryptor_Private>()->PromiseResolvedWithSession(
        associated_instance_.pp_instance(),
        promise_id,
        web_session_id_var.pp_var());
  }
}

void ContentDecryptor_Private::PromiseResolvedWithKeyIds(
    uint32_t promise_id,
    const std::vector<std::vector<uint8_t> >& key_ids) {
  if (has_interface<PPB_ContentDecryptor_Private>()) {
    pp::VarArray key_ids_array = pp::VarArray();
    key_ids_array.SetLength(key_ids.size());
    for (size_t i = 0; i < key_ids.size(); ++i) {
      const std::vector<uint8_t>& entry = key_ids[i];
      pp::VarArrayBuffer array_buffer(entry.size());
      memcpy(array_buffer.Map(), &entry[0], entry.size());
      key_ids_array.Set(i, array_buffer);
    }
    get_interface<PPB_ContentDecryptor_Private>()->PromiseResolvedWithKeyIds(
        associated_instance_.pp_instance(), promise_id, key_ids_array.pp_var());
  }
}

void ContentDecryptor_Private::PromiseRejected(
    uint32_t promise_id,
    PP_CdmExceptionCode exception_code,
    uint32_t system_code,
    const std::string& error_description) {
  if (has_interface<PPB_ContentDecryptor_Private>()) {
    pp::Var error_description_var(error_description);
    get_interface<PPB_ContentDecryptor_Private>()->PromiseRejected(
        associated_instance_.pp_instance(),
        promise_id,
        exception_code,
        system_code,
        error_description_var.pp_var());
  }
}

void ContentDecryptor_Private::SessionMessage(
    const std::string& web_session_id,
    pp::VarArrayBuffer message,
    const std::string& destination_url) {
  if (has_interface<PPB_ContentDecryptor_Private>()) {
    pp::Var web_session_id_var(web_session_id);
    pp::Var destination_url_var(destination_url);
    get_interface<PPB_ContentDecryptor_Private>()->SessionMessage(
        associated_instance_.pp_instance(),
        web_session_id_var.pp_var(),
        message.pp_var(),
        destination_url_var.pp_var());
  }
}

void ContentDecryptor_Private::SessionKeysChange(
    const std::string& web_session_id,
    bool has_additional_usable_key) {
  if (has_interface<PPB_ContentDecryptor_Private>()) {
    pp::Var web_session_id_var(web_session_id);
    get_interface<PPB_ContentDecryptor_Private>()->SessionKeysChange(
        associated_instance_.pp_instance(),
        web_session_id_var.pp_var(),
        PP_FromBool(has_additional_usable_key));
  }
}

void ContentDecryptor_Private::SessionExpirationChange(
    const std::string& web_session_id,
    PP_Time new_expiry_time) {
  if (has_interface<PPB_ContentDecryptor_Private>()) {
    pp::Var web_session_id_var(web_session_id);
    get_interface<PPB_ContentDecryptor_Private>()->SessionExpirationChange(
        associated_instance_.pp_instance(),
        web_session_id_var.pp_var(),
        new_expiry_time);
  }
}

void ContentDecryptor_Private::SessionReady(const std::string& web_session_id) {
  if (has_interface<PPB_ContentDecryptor_Private>()) {
    pp::Var web_session_id_var(web_session_id);
    get_interface<PPB_ContentDecryptor_Private>()->SessionReady(
        associated_instance_.pp_instance(), web_session_id_var.pp_var());
  }
}

void ContentDecryptor_Private::SessionClosed(
    const std::string& web_session_id) {
  if (has_interface<PPB_ContentDecryptor_Private>()) {
    pp::Var web_session_id_var(web_session_id);
    get_interface<PPB_ContentDecryptor_Private>()->SessionClosed(
        associated_instance_.pp_instance(), web_session_id_var.pp_var());
  }
}

void ContentDecryptor_Private::SessionError(
    const std::string& web_session_id,
    PP_CdmExceptionCode exception_code,
    uint32_t system_code,
    const std::string& error_description) {
  if (has_interface<PPB_ContentDecryptor_Private>()) {
    pp::Var web_session_id_var(web_session_id);
    pp::Var error_description_var(error_description);
    get_interface<PPB_ContentDecryptor_Private>()->SessionError(
        associated_instance_.pp_instance(),
        web_session_id_var.pp_var(),
        exception_code,
        system_code,
        error_description_var.pp_var());
  }
}

void ContentDecryptor_Private::DeliverBlock(
    pp::Buffer_Dev decrypted_block,
    const PP_DecryptedBlockInfo& decrypted_block_info) {
  if (has_interface<PPB_ContentDecryptor_Private>()) {
    get_interface<PPB_ContentDecryptor_Private>()->DeliverBlock(
        associated_instance_.pp_instance(),
        decrypted_block.pp_resource(),
        &decrypted_block_info);
  }
}

void ContentDecryptor_Private::DecoderInitializeDone(
    PP_DecryptorStreamType decoder_type,
    uint32_t request_id,
    bool success) {
  if (has_interface<PPB_ContentDecryptor_Private>()) {
    get_interface<PPB_ContentDecryptor_Private>()->DecoderInitializeDone(
        associated_instance_.pp_instance(),
        decoder_type,
        request_id,
        PP_FromBool(success));
  }
}

void ContentDecryptor_Private::DecoderDeinitializeDone(
    PP_DecryptorStreamType decoder_type,
    uint32_t request_id) {
  if (has_interface<PPB_ContentDecryptor_Private>()) {
    get_interface<PPB_ContentDecryptor_Private>()->DecoderDeinitializeDone(
        associated_instance_.pp_instance(),
        decoder_type,
        request_id);
  }
}

void ContentDecryptor_Private::DecoderResetDone(
    PP_DecryptorStreamType decoder_type,
    uint32_t request_id) {
  if (has_interface<PPB_ContentDecryptor_Private>()) {
    get_interface<PPB_ContentDecryptor_Private>()->DecoderResetDone(
        associated_instance_.pp_instance(),
        decoder_type,
        request_id);
  }
}

void ContentDecryptor_Private::DeliverFrame(
    pp::Buffer_Dev decrypted_frame,
    const PP_DecryptedFrameInfo& decrypted_frame_info) {
  if (has_interface<PPB_ContentDecryptor_Private>()) {
    get_interface<PPB_ContentDecryptor_Private>()->DeliverFrame(
        associated_instance_.pp_instance(),
        decrypted_frame.pp_resource(),
        &decrypted_frame_info);
  }
}

void ContentDecryptor_Private::DeliverSamples(
    pp::Buffer_Dev audio_frames,
    const PP_DecryptedSampleInfo& decrypted_sample_info) {
  if (has_interface<PPB_ContentDecryptor_Private>()) {
    get_interface<PPB_ContentDecryptor_Private>()->DeliverSamples(
        associated_instance_.pp_instance(),
        audio_frames.pp_resource(),
        &decrypted_sample_info);
  }
}

}  // namespace pp
