// Copyright (c) 2011 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/proxy/ppb_flash_proxy.h"

#include "base/logging.h"
#include "base/message_loop.h"
#include "base/time.h"
#include "ppapi/c/dev/ppb_font_dev.h"
#include "ppapi/c/dev/ppb_var_deprecated.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/c/pp_resource.h"
#include "ppapi/c/private/ppb_flash.h"
#include "ppapi/proxy/host_dispatcher.h"
#include "ppapi/proxy/plugin_dispatcher.h"
#include "ppapi/proxy/plugin_resource.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/proxy/proxy_module.h"
#include "ppapi/proxy/serialized_var.h"

namespace pp {
namespace proxy {

namespace {

void SetInstanceAlwaysOnTop(PP_Instance pp_instance, PP_Bool on_top) {
  PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(pp_instance);
  if (dispatcher) {
    dispatcher->Send(new PpapiHostMsg_PPBFlash_SetInstanceAlwaysOnTop(
        INTERFACE_ID_PPB_FLASH, pp_instance, on_top));
  }
}

PP_Bool DrawGlyphs(PP_Instance instance,
                   PP_Resource pp_image_data,
                   const PP_FontDescription_Dev* font_desc,
                   uint32_t color,
                   PP_Point position,
                   PP_Rect clip,
                   const float transformation[3][3],
                   uint32_t glyph_count,
                   const uint16_t glyph_indices[],
                   const PP_Point glyph_advances[]) {
  PluginResource* image_data = PluginResourceTracker::GetInstance()->
      GetResourceObject(pp_image_data);
  if (!image_data)
    return PP_FALSE;
  // The instance parameter isn't strictly necessary but we check that it
  // matches anyway.
  if (image_data->instance() != instance)
    return PP_FALSE;

  PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(
      image_data->instance());
  if (!dispatcher)
    return PP_FALSE;

  PPBFlash_DrawGlyphs_Params params;
  params.image_data = image_data->host_resource();
  params.font_desc.SetFromPPFontDescription(dispatcher, *font_desc, true);
  params.color = color;
  params.position = position;
  params.clip = clip;
  for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++)
      params.transformation[i][j] = transformation[i][j];
  }

  params.glyph_indices.insert(params.glyph_indices.begin(),
                              &glyph_indices[0],
                              &glyph_indices[glyph_count]);
  params.glyph_advances.insert(params.glyph_advances.begin(),
                               &glyph_advances[0],
                               &glyph_advances[glyph_count]);

  PP_Bool result = PP_FALSE;
  dispatcher->Send(new PpapiHostMsg_PPBFlash_DrawGlyphs(
      INTERFACE_ID_PPB_FLASH, params, &result));
  return result;
}

PP_Var GetProxyForURL(PP_Instance instance, const char* url) {
  PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
  if (!dispatcher)
    return PP_MakeUndefined();

  ReceiveSerializedVarReturnValue result;
  dispatcher->Send(new PpapiHostMsg_PPBFlash_GetProxyForURL(
      INTERFACE_ID_PPB_FLASH, instance, url, &result));
  return result.Return(dispatcher);
}

int32_t Navigate(PP_Resource request_id,
                 const char* target,
                 bool from_user_action) {
  PluginResource* request_object =
      PluginResourceTracker::GetInstance()->GetResourceObject(request_id);
  if (!request_object)
    return PP_ERROR_BADRESOURCE;

  PluginDispatcher* dispatcher =
      PluginDispatcher::GetForInstance(request_object->instance());
  if (!dispatcher)
    return PP_ERROR_FAILED;

  int32_t result = PP_ERROR_FAILED;
  dispatcher->Send(new PpapiHostMsg_PPBFlash_Navigate(
      INTERFACE_ID_PPB_FLASH,
      request_object->host_resource(), target, from_user_action,
      &result));
  return result;
}

void RunMessageLoop(PP_Instance instance) {
  PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
  if (!dispatcher)
    return;
  IPC::SyncMessage* msg = new PpapiHostMsg_PPBFlash_RunMessageLoop(
      INTERFACE_ID_PPB_FLASH, instance);
  msg->EnableMessagePumping();
  dispatcher->Send(msg);
}

void QuitMessageLoop(PP_Instance instance) {
  PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
  if (!dispatcher)
    return;
  dispatcher->Send(new PpapiHostMsg_PPBFlash_QuitMessageLoop(
      INTERFACE_ID_PPB_FLASH, instance));
}

double GetLocalTimeZoneOffset(PP_Instance instance, PP_Time t) {
  PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
  if (!dispatcher)
    return 0.0;

  // TODO(brettw) on Windows it should be possible to do the time calculation
  // in-process since it doesn't need to read files on disk. This will improve
  // performance.
  //
  // On Linux, it would be better to go directly to the browser process for
  // this message rather than proxy it through some instance in a renderer.
  double result = 0;
  dispatcher->Send(new PpapiHostMsg_PPBFlash_GetLocalTimeZoneOffset(
      INTERFACE_ID_PPB_FLASH, instance, t, &result));
  return result;
}

PP_Var GetCommandLineArgs(PP_Module pp_module) {
  const PPB_Var_Deprecated* var_deprecated =
      static_cast<const PPB_Var_Deprecated*>(
          PluginDispatcher::GetInterfaceFromDispatcher(
              PPB_VAR_DEPRECATED_INTERFACE));
  std::string args =
      pp::proxy::ProxyModule::GetInstance()->GetFlashCommandLineArgs();
  return var_deprecated->VarFromUtf8(pp_module, args.data(), args.length());
}

const PPB_Flash flash_interface = {
  &SetInstanceAlwaysOnTop,
  &DrawGlyphs,
  &GetProxyForURL,
  &Navigate,
  &RunMessageLoop,
  &QuitMessageLoop,
  &GetLocalTimeZoneOffset,
  &GetCommandLineArgs
};

InterfaceProxy* CreateFlashProxy(Dispatcher* dispatcher,
                                 const void* target_interface) {
  return new PPB_Flash_Proxy(dispatcher, target_interface);
}

}  // namespace

PPB_Flash_Proxy::PPB_Flash_Proxy(Dispatcher* dispatcher,
                                 const void* target_interface)
    : InterfaceProxy(dispatcher, target_interface) {
}

PPB_Flash_Proxy::~PPB_Flash_Proxy() {
}

// static
const InterfaceProxy::Info* PPB_Flash_Proxy::GetInfo() {
  static const Info info = {
    &flash_interface,
    PPB_FLASH_INTERFACE,
    INTERFACE_ID_PPB_FLASH,
    true,
    &CreateFlashProxy,
  };
  return &info;
}

bool PPB_Flash_Proxy::OnMessageReceived(const IPC::Message& msg) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(PPB_Flash_Proxy, msg)
    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBFlash_SetInstanceAlwaysOnTop,
                        OnMsgSetInstanceAlwaysOnTop)
    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBFlash_DrawGlyphs,
                        OnMsgDrawGlyphs)
    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBFlash_GetProxyForURL,
                        OnMsgGetProxyForURL)
    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBFlash_Navigate, OnMsgNavigate)
    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBFlash_RunMessageLoop,
                        OnMsgRunMessageLoop)
    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBFlash_QuitMessageLoop,
                        OnMsgQuitMessageLoop)
    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBFlash_GetLocalTimeZoneOffset,
                        OnMsgGetLocalTimeZoneOffset)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  // TODO(brettw) handle bad messages!
  return handled;
}

void PPB_Flash_Proxy::OnMsgSetInstanceAlwaysOnTop(
    PP_Instance instance,
    PP_Bool on_top) {
  ppb_flash_target()->SetInstanceAlwaysOnTop(instance, on_top);
}

void PPB_Flash_Proxy::OnMsgDrawGlyphs(
    const pp::proxy::PPBFlash_DrawGlyphs_Params& params,
    PP_Bool* result) {
  *result = PP_FALSE;

  PP_FontDescription_Dev font_desc;
  params.font_desc.SetToPPFontDescription(dispatcher(), &font_desc, false);

  if (params.glyph_indices.size() != params.glyph_advances.size() ||
      params.glyph_indices.empty())
    return;

  *result = ppb_flash_target()->DrawGlyphs(
      0,  // Unused instance param.
      params.image_data.host_resource(), &font_desc,
      params.color, params.position, params.clip,
      const_cast<float(*)[3]>(params.transformation),
      static_cast<uint32_t>(params.glyph_indices.size()),
      const_cast<uint16_t*>(&params.glyph_indices[0]),
      const_cast<PP_Point*>(&params.glyph_advances[0]));
}

void PPB_Flash_Proxy::OnMsgGetProxyForURL(PP_Instance instance,
                                          const std::string& url,
                                          SerializedVarReturnValue result) {
  result.Return(dispatcher(), ppb_flash_target()->GetProxyForURL(
      instance, url.c_str()));
}

void PPB_Flash_Proxy::OnMsgNavigate(const HostResource& request_info,
                                    const std::string& target,
                                    bool from_user_action,
                                    int32_t* result) {
  DCHECK(!dispatcher()->IsPlugin());
  // We need to allow re-entrancy here, because this may call into Javascript
  // (e.g. with a "javascript:" URL), or do things like navigate away from the
  // page, either one of which will need to re-enter into the plugin.
  // It is safe, because it is essentially equivalent to NPN_GetURL, where Flash
  // would expect re-entrancy. When running in-process, it does re-enter here.
  static_cast<HostDispatcher*>(dispatcher())->set_allow_plugin_reentrancy();
  *result = ppb_flash_target()->Navigate(request_info.host_resource(),
                                         target.c_str(),
                                         from_user_action);
}

void PPB_Flash_Proxy::OnMsgRunMessageLoop(PP_Instance instance) {
  ppb_flash_target()->RunMessageLoop(instance);
}

void PPB_Flash_Proxy::OnMsgQuitMessageLoop(PP_Instance instance) {
  ppb_flash_target()->QuitMessageLoop(instance);
}

void PPB_Flash_Proxy::OnMsgGetLocalTimeZoneOffset(PP_Instance instance,
                                                  PP_Time t,
                                                  double* result) {
  *result = ppb_flash_target()->GetLocalTimeZoneOffset(instance, t);
}

}  // namespace proxy
}  // namespace pp