// 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/instance.h"

#include "ppapi/c/pp_errors.h"
#include "ppapi/c/ppb_console.h"
#include "ppapi/c/ppb_input_event.h"
#include "ppapi/c/ppb_instance.h"
#include "ppapi/c/ppb_messaging.h"
#include "ppapi/c/ppp_message_handler.h"
#include "ppapi/cpp/compositor.h"
#include "ppapi/cpp/graphics_2d.h"
#include "ppapi/cpp/graphics_3d.h"
#include "ppapi/cpp/image_data.h"
#include "ppapi/cpp/instance_handle.h"
#include "ppapi/cpp/logging.h"
#include "ppapi/cpp/message_handler.h"
#include "ppapi/cpp/message_loop.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/module_impl.h"
#include "ppapi/cpp/point.h"
#include "ppapi/cpp/resource.h"
#include "ppapi/cpp/var.h"
#include "ppapi/cpp/view.h"

namespace pp {

namespace {

template <> const char* interface_name<PPB_Console_1_0>() {
  return PPB_CONSOLE_INTERFACE_1_0;
}

template <> const char* interface_name<PPB_InputEvent_1_0>() {
  return PPB_INPUT_EVENT_INTERFACE_1_0;
}

template <> const char* interface_name<PPB_Instance_1_0>() {
  return PPB_INSTANCE_INTERFACE_1_0;
}

template <> const char* interface_name<PPB_Messaging_1_0>() {
  return PPB_MESSAGING_INTERFACE_1_0;
}

template <> const char* interface_name<PPB_Messaging_1_2>() {
  return PPB_MESSAGING_INTERFACE_1_2;
}

// PPP_MessageHandler implementation -------------------------------------------
void HandleMessage(PP_Instance pp_instance,
                   void* user_data,
                   const PP_Var* var) {
  MessageHandler* message_handler = static_cast<MessageHandler*>(user_data);
  message_handler->HandleMessage(InstanceHandle(pp_instance), Var(*var));
}

void HandleBlockingMessage(PP_Instance pp_instance,
                           void* user_data,
                           const PP_Var* var,
                           PP_Var* result) {
  MessageHandler* message_handler = static_cast<MessageHandler*>(user_data);
  pp::Var result_var =
      message_handler->HandleBlockingMessage(InstanceHandle(pp_instance),
                                             Var(*var));
  *result = result_var.Detach();
}

void Destroy(PP_Instance pp_instance, void* user_data) {
  MessageHandler* message_handler = static_cast<MessageHandler*>(user_data);
  message_handler->WasUnregistered(InstanceHandle(pp_instance));
}

static PPP_MessageHandler_0_2 message_handler_if = {
  &HandleMessage, &HandleBlockingMessage, &Destroy
};

}  // namespace

Instance::Instance(PP_Instance instance) : pp_instance_(instance) {
}

Instance::~Instance() {
}

bool Instance::Init(uint32_t /*argc*/, const char* /*argn*/[],
                    const char* /*argv*/[]) {
  return true;
}

void Instance::DidChangeView(const View& view) {
  // Call the deprecated version for source backwards-compat.
  DidChangeView(view.GetRect(), view.GetClipRect());
}

void Instance::DidChangeView(const pp::Rect& /*position*/,
                             const pp::Rect& /*clip*/) {
}

void Instance::DidChangeFocus(bool /*has_focus*/) {
}


bool Instance::HandleDocumentLoad(const URLLoader& /*url_loader*/) {
  return false;
}

bool Instance::HandleInputEvent(const InputEvent& /*event*/) {
  return false;
}

void Instance::HandleMessage(const Var& /*message*/) {
  return;
}

bool Instance::BindGraphics(const Graphics2D& graphics) {
  if (!has_interface<PPB_Instance_1_0>())
    return false;
  return PP_ToBool(get_interface<PPB_Instance_1_0>()->BindGraphics(
      pp_instance(), graphics.pp_resource()));
}

bool Instance::BindGraphics(const Graphics3D& graphics) {
  if (!has_interface<PPB_Instance_1_0>())
    return false;
  return PP_ToBool(get_interface<PPB_Instance_1_0>()->BindGraphics(
      pp_instance(), graphics.pp_resource()));
}

bool Instance::BindGraphics(const Compositor& compositor) {
  if (!has_interface<PPB_Instance_1_0>())
    return false;
  return PP_ToBool(get_interface<PPB_Instance_1_0>()->BindGraphics(
      pp_instance(), compositor.pp_resource()));
}

bool Instance::IsFullFrame() {
  if (!has_interface<PPB_Instance_1_0>())
    return false;
  return PP_ToBool(get_interface<PPB_Instance_1_0>()->IsFullFrame(
      pp_instance()));
}

int32_t Instance::RequestInputEvents(uint32_t event_classes) {
  if (!has_interface<PPB_InputEvent_1_0>())
    return PP_ERROR_NOINTERFACE;
  return get_interface<PPB_InputEvent_1_0>()->RequestInputEvents(pp_instance(),
                                                                 event_classes);
}

int32_t Instance::RequestFilteringInputEvents(uint32_t event_classes) {
  if (!has_interface<PPB_InputEvent_1_0>())
    return PP_ERROR_NOINTERFACE;
  return get_interface<PPB_InputEvent_1_0>()->RequestFilteringInputEvents(
      pp_instance(), event_classes);
}

void Instance::ClearInputEventRequest(uint32_t event_classes) {
  if (!has_interface<PPB_InputEvent_1_0>())
    return;
  get_interface<PPB_InputEvent_1_0>()->ClearInputEventRequest(pp_instance(),
                                                          event_classes);
}

void Instance::PostMessage(const Var& message) {
  if (has_interface<PPB_Messaging_1_2>()) {
    get_interface<PPB_Messaging_1_2>()->PostMessage(pp_instance(),
                                                    message.pp_var());
  } else if (has_interface<PPB_Messaging_1_0>()) {
    get_interface<PPB_Messaging_1_0>()->PostMessage(pp_instance(),
                                                    message.pp_var());
  }
}

int32_t Instance::RegisterMessageHandler(MessageHandler* message_handler,
                                         const MessageLoop& message_loop) {
  if (!has_interface<PPB_Messaging_1_2>())
    return PP_ERROR_NOTSUPPORTED;
  return get_interface<PPB_Messaging_1_2>()->RegisterMessageHandler(
      pp_instance(),
      message_handler,
      &message_handler_if,
      message_loop.pp_resource());
}

void Instance::UnregisterMessageHandler() {
  if (!has_interface<PPB_Messaging_1_2>())
    return;
  get_interface<PPB_Messaging_1_2>()->UnregisterMessageHandler(pp_instance());
}

void Instance::LogToConsole(PP_LogLevel level, const Var& value) {
  if (!has_interface<PPB_Console_1_0>())
    return;
  get_interface<PPB_Console_1_0>()->Log(
      pp_instance(), level, value.pp_var());
}

void Instance::LogToConsoleWithSource(PP_LogLevel level,
                                      const Var& source,
                                      const Var& value) {
  if (!has_interface<PPB_Console_1_0>())
    return;
  get_interface<PPB_Console_1_0>()->LogWithSource(
      pp_instance(), level, source.pp_var(), value.pp_var());
}

void Instance::AddPerInstanceObject(const std::string& interface_name,
                                    void* object) {
  // Ensure we're not trying to register more than one object per interface
  // type. Otherwise, we'll get confused in GetPerInstanceObject.
  PP_DCHECK(interface_name_to_objects_.find(interface_name) ==
            interface_name_to_objects_.end());
  interface_name_to_objects_[interface_name] = object;
}

void Instance::RemovePerInstanceObject(const std::string& interface_name,
                                       void* object) {
  InterfaceNameToObjectMap::iterator found = interface_name_to_objects_.find(
      interface_name);
  if (found == interface_name_to_objects_.end()) {
    // Attempting to unregister an object that doesn't exist or was already
    // unregistered.
    PP_DCHECK(false);
    return;
  }

  // Validate that we're removing the object we thing we are.
  PP_DCHECK(found->second == object);
  (void)object;  // Prevent warning in release mode.

  interface_name_to_objects_.erase(found);
}

// static
void Instance::RemovePerInstanceObject(const InstanceHandle& instance,
                                       const std::string& interface_name,
                                       void* object) {
  // TODO(brettw) assert we're on the main thread.
  Instance* that = Module::Get()->InstanceForPPInstance(instance.pp_instance());
  if (!that)
    return;
  that->RemovePerInstanceObject(interface_name, object);
}

// static
void* Instance::GetPerInstanceObject(PP_Instance instance,
                                     const std::string& interface_name) {
  Instance* that = Module::Get()->InstanceForPPInstance(instance);
  if (!that)
    return NULL;
  InterfaceNameToObjectMap::iterator found =
      that->interface_name_to_objects_.find(interface_name);
  if (found == that->interface_name_to_objects_.end())
    return NULL;
  return found->second;
}

}  // namespace pp
