// 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.

// Note that the single accessor, Module::Get(), is not actually implemented
// in this file.  This is an intentional hook that allows users of ppapi's
// C++ wrapper objects to provide different semantics for how the singleton
// object is accessed.
//
// In general, users of ppapi will also link in ppp_entrypoints.cc, which
// provides a simple default implementation of Module::Get().
//
// A notable exception where the default ppp_entrypoints will not work is
// when implementing "internal plugins" that are statically linked into the
// browser. In this case, the process may actually have multiple Modules
// loaded at once making a traditional "singleton" unworkable.  To get around
// this, the users of ppapi need to get creative about how to properly
// implement the Module::Get() so that ppapi's C++ wrappers can find the
// right Module object.  One example solution is to use thread local storage
// to change the Module* returned based on which thread is invoking the
// function. Leaving Module::Get() unimplemented provides a hook for
// implementing such behavior.

#include "ppapi/cpp/module.h"

#include <string.h>

#include "ppapi/c/pp_instance.h"
#include "ppapi/c/pp_var.h"
#include "ppapi/c/ppp_input_event.h"
#include "ppapi/c/ppp_instance.h"
#include "ppapi/c/ppp_messaging.h"
#include "ppapi/cpp/input_event.h"
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/rect.h"
#include "ppapi/cpp/resource.h"
#include "ppapi/cpp/url_loader.h"
#include "ppapi/cpp/var.h"
#include "ppapi/cpp/view.h"

namespace pp {

// PPP_InputEvent implementation -----------------------------------------------

PP_Bool InputEvent_HandleEvent(PP_Instance pp_instance, PP_Resource resource) {
  Module* module_singleton = Module::Get();
  if (!module_singleton)
    return PP_FALSE;
  Instance* instance = module_singleton->InstanceForPPInstance(pp_instance);
  if (!instance)
    return PP_FALSE;

  return PP_FromBool(instance->HandleInputEvent(InputEvent(resource)));
}

const PPP_InputEvent input_event_interface = {
  &InputEvent_HandleEvent
};

// PPP_Instance implementation -------------------------------------------------

PP_Bool Instance_DidCreate(PP_Instance pp_instance,
                           uint32_t argc,
                           const char* argn[],
                           const char* argv[]) {
  Module* module_singleton = Module::Get();
  if (!module_singleton)
    return PP_FALSE;

  Instance* instance = module_singleton->CreateInstance(pp_instance);
  if (!instance)
    return PP_FALSE;
  module_singleton->current_instances_[pp_instance] = instance;
  return PP_FromBool(instance->Init(argc, argn, argv));
}

void Instance_DidDestroy(PP_Instance instance) {
  Module* module_singleton = Module::Get();
  if (!module_singleton)
    return;
  Module::InstanceMap::iterator found =
      module_singleton->current_instances_.find(instance);
  if (found == module_singleton->current_instances_.end())
    return;

  // Remove it from the map before deleting to try to catch reentrancy.
  Instance* obj = found->second;
  module_singleton->current_instances_.erase(found);
  delete obj;
}

void Instance_DidChangeView(PP_Instance pp_instance,
                            PP_Resource view_resource) {
  Module* module_singleton = Module::Get();
  if (!module_singleton)
    return;
  Instance* instance = module_singleton->InstanceForPPInstance(pp_instance);
  if (!instance)
    return;
  instance->DidChangeView(View(view_resource));
}

void Instance_DidChangeFocus(PP_Instance pp_instance, PP_Bool has_focus) {
  Module* module_singleton = Module::Get();
  if (!module_singleton)
    return;
  Instance* instance = module_singleton->InstanceForPPInstance(pp_instance);
  if (!instance)
    return;
  instance->DidChangeFocus(PP_ToBool(has_focus));
}

PP_Bool Instance_HandleDocumentLoad(PP_Instance pp_instance,
                                    PP_Resource pp_url_loader) {
  Module* module_singleton = Module::Get();
  if (!module_singleton)
    return PP_FALSE;
  Instance* instance = module_singleton->InstanceForPPInstance(pp_instance);
  if (!instance)
    return PP_FALSE;
  return PP_FromBool(instance->HandleDocumentLoad(URLLoader(pp_url_loader)));
}

static PPP_Instance instance_interface = {
  &Instance_DidCreate,
  &Instance_DidDestroy,
  &Instance_DidChangeView,
  &Instance_DidChangeFocus,
  &Instance_HandleDocumentLoad
};

// PPP_Messaging implementation ------------------------------------------------

void Messaging_HandleMessage(PP_Instance pp_instance, PP_Var var) {
  Module* module_singleton = Module::Get();
  if (!module_singleton)
    return;
  Instance* instance = module_singleton->InstanceForPPInstance(pp_instance);
  if (!instance)
    return;
  instance->HandleMessage(Var(PASS_REF, var));
}

static PPP_Messaging instance_messaging_interface = {
  &Messaging_HandleMessage
};

// Module ----------------------------------------------------------------------

Module::Module() : pp_module_(0), get_browser_interface_(NULL), core_(NULL) {
}

Module::~Module() {
  delete core_;
  core_ = NULL;
}

bool Module::Init() {
  return true;
}

const void* Module::GetPluginInterface(const char* interface_name) {
  if (strcmp(interface_name, PPP_INPUT_EVENT_INTERFACE) == 0)
    return &input_event_interface;
  if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0)
    return &instance_interface;
  if (strcmp(interface_name, PPP_MESSAGING_INTERFACE) == 0)
    return &instance_messaging_interface;

  // Now see if anything was dynamically registered.
  InterfaceMap::const_iterator found = additional_interfaces_.find(
      std::string(interface_name));
  if (found != additional_interfaces_.end())
    return found->second;

  return NULL;
}

const void* Module::GetBrowserInterface(const char* interface_name) {
  return get_browser_interface_(interface_name);
}

Instance* Module::InstanceForPPInstance(PP_Instance instance) {
  InstanceMap::iterator found = current_instances_.find(instance);
  if (found == current_instances_.end())
    return NULL;
  return found->second;
}

void Module::AddPluginInterface(const std::string& interface_name,
                                const void* vtable) {
  // Verify that we're not trying to register an interface that's already
  // handled, and if it is, that we're re-registering with the same vtable.
  // Calling GetPluginInterface rather than looking it up in the map allows
  // us to also catch "internal" ones in addition to just previously added ones.
  const void* existing_interface = GetPluginInterface(interface_name.c_str());
  if (existing_interface) {
    PP_DCHECK(vtable == existing_interface);
    return;
  }
  additional_interfaces_[interface_name] = vtable;
}

bool Module::InternalInit(PP_Module mod,
                          PPB_GetInterface get_browser_interface) {
  pp_module_ = mod;
  get_browser_interface_ = get_browser_interface;

  // Get the core interface which we require to run.
  const PPB_Core* core = reinterpret_cast<const PPB_Core*>(GetBrowserInterface(
      PPB_CORE_INTERFACE));
  if (!core)
    return false;
  core_ = new Core(core);

  return Init();
}

}  // namespace pp
