// Copyright (c) 2010 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 "webkit/plugins/npapi/npapi_extension_thunk.h"

#include "base/logging.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "third_party/npapi/bindings/npapi_extensions.h"
#include "webkit/plugins/npapi/plugin_instance.h"
#include "webkit/plugins/npapi/webplugin.h"
#include "webkit/plugins/npapi/webplugin_delegate.h"
#include "webkit/glue/webkit_glue.h"

namespace webkit {
namespace npapi {

// FindInstance()
// Finds a PluginInstance from an NPP.
// The caller must take a reference if needed.
static PluginInstance* FindInstance(NPP id) {
  if (id == NULL) {
    NOTREACHED();
    return NULL;
  }
  return static_cast<PluginInstance*>(id->ndata);
}

// 2D device API ---------------------------------------------------------------

static NPError Device2DQueryCapability(NPP id, int32_t capability,
                                       int32_t* value) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    plugin->webplugin()->delegate()->Device2DQueryCapability(capability, value);
    return NPERR_NO_ERROR;
  } else {
    return NPERR_GENERIC_ERROR;
  }
}

static NPError Device2DQueryConfig(NPP id,
                                   const NPDeviceConfig* request,
                                   NPDeviceConfig* obtain) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    return plugin->webplugin()->delegate()->Device2DQueryConfig(
        static_cast<const NPDeviceContext2DConfig*>(request),
        static_cast<NPDeviceContext2DConfig*>(obtain));
  }
  return NPERR_GENERIC_ERROR;
}

static NPError Device2DInitializeContext(NPP id,
                                         const NPDeviceConfig* config,
                                         NPDeviceContext* context) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    return plugin->webplugin()->delegate()->Device2DInitializeContext(
        static_cast<const NPDeviceContext2DConfig*>(config),
        static_cast<NPDeviceContext2D*>(context));
  }
  return NPERR_GENERIC_ERROR;
}

static NPError Device2DSetStateContext(NPP id,
                                       NPDeviceContext* context,
                                       int32_t state,
                                       intptr_t value) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    return plugin->webplugin()->delegate()->Device2DSetStateContext(
        static_cast<NPDeviceContext2D*>(context), state, value);
  }
  return NPERR_GENERIC_ERROR;
}

static NPError Device2DGetStateContext(NPP id,
                                       NPDeviceContext* context,
                                       int32_t state,
                                       intptr_t* value) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    return plugin->webplugin()->delegate()->Device2DGetStateContext(
        static_cast<NPDeviceContext2D*>(context), state, value);
  }
  return NPERR_GENERIC_ERROR;
}

static NPError Device2DFlushContext(NPP id,
                                    NPDeviceContext* context,
                                    NPDeviceFlushContextCallbackPtr callback,
                                    void* user_data) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    NPError err = plugin->webplugin()->delegate()->Device2DFlushContext(
        id, static_cast<NPDeviceContext2D*>(context), callback, user_data);

    // Invoke the callback to inform the caller the work was done.
    // TODO(brettw) this is probably not how we want this to work, this should
    // happen when the frame is painted so the plugin knows when it can draw
    // the next frame.
    if (callback != NULL)
      (*callback)(id, context, err, user_data);

    // Return any errors.
    return err;
  }
  return NPERR_GENERIC_ERROR;
}

static NPError Device2DDestroyContext(NPP id,
                                      NPDeviceContext* context) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    return plugin->webplugin()->delegate()->Device2DDestroyContext(
        static_cast<NPDeviceContext2D*>(context));
  }
  return NPERR_GENERIC_ERROR;
}

static NPError Device2DCreateBuffer(NPP id,
                                    NPDeviceContext* context,
                                    size_t size,
                                    int32_t* buffer_id) {
  return NPERR_GENERIC_ERROR;
}

static NPError Device2DDestroyBuffer(NPP id,
                                     NPDeviceContext* context,
                                     int32_t buffer_id) {
  return NPERR_GENERIC_ERROR;
}

static NPError Device2DMapBuffer(NPP id,
                                 NPDeviceContext* context,
                                 int32_t buffer_id,
                                 NPDeviceBuffer* buffer) {
  return NPERR_GENERIC_ERROR;
}

// 3D device API ---------------------------------------------------------------

static NPError Device3DQueryCapability(NPP id, int32_t capability,
                                       int32_t* value) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    plugin->webplugin()->delegate()->Device3DQueryCapability(capability, value);
    return NPERR_NO_ERROR;
  } else {
    return NPERR_GENERIC_ERROR;
  }
}

static NPError Device3DQueryConfig(NPP id,
                                   const NPDeviceConfig* request,
                                   NPDeviceConfig* obtain) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    return plugin->webplugin()->delegate()->Device3DQueryConfig(
        static_cast<const NPDeviceContext3DConfig*>(request),
        static_cast<NPDeviceContext3DConfig*>(obtain));
  }
  return NPERR_GENERIC_ERROR;
}

static NPError Device3DInitializeContext(NPP id,
                                         const NPDeviceConfig* config,
                                         NPDeviceContext* context) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    return plugin->webplugin()->delegate()->Device3DInitializeContext(
        static_cast<const NPDeviceContext3DConfig*>(config),
        static_cast<NPDeviceContext3D*>(context));
  }
  return NPERR_GENERIC_ERROR;
}

static NPError Device3DSetStateContext(NPP id,
                                       NPDeviceContext* context,
                                       int32_t state,
                                       intptr_t value) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    return plugin->webplugin()->delegate()->Device3DSetStateContext(
        static_cast<NPDeviceContext3D*>(context), state, value);
  }
  return NPERR_GENERIC_ERROR;
}

static NPError Device3DGetStateContext(NPP id,
                                       NPDeviceContext* context,
                                       int32_t state,
                                       intptr_t* value) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    return plugin->webplugin()->delegate()->Device3DGetStateContext(
        static_cast<NPDeviceContext3D*>(context), state, value);
  }
  return NPERR_GENERIC_ERROR;
}

static NPError Device3DFlushContext(NPP id,
                                    NPDeviceContext* context,
                                    NPDeviceFlushContextCallbackPtr callback,
                                    void* user_data) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    return plugin->webplugin()->delegate()->Device3DFlushContext(
        id, static_cast<NPDeviceContext3D*>(context), callback, user_data);
  }
  return NPERR_GENERIC_ERROR;
}

static NPError Device3DDestroyContext(NPP id,
                                      NPDeviceContext* context) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    return plugin->webplugin()->delegate()->Device3DDestroyContext(
        static_cast<NPDeviceContext3D*>(context));
  }
  return NPERR_GENERIC_ERROR;
}

static NPError Device3DCreateBuffer(NPP id,
                                    NPDeviceContext* context,
                                    size_t size,
                                    int32_t* buffer_id) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    return plugin->webplugin()->delegate()->Device3DCreateBuffer(
        static_cast<NPDeviceContext3D*>(context), size, buffer_id);
  }
  return NPERR_GENERIC_ERROR;
}

static NPError Device3DDestroyBuffer(NPP id,
                                     NPDeviceContext* context,
                                     int32_t buffer_id) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    return plugin->webplugin()->delegate()->Device3DDestroyBuffer(
        static_cast<NPDeviceContext3D*>(context), buffer_id);
  }
  return NPERR_GENERIC_ERROR;
}

static NPError Device3DMapBuffer(NPP id,
                                 NPDeviceContext* context,
                                 int32_t buffer_id,
                                 NPDeviceBuffer* buffer) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    return plugin->webplugin()->delegate()->Device3DMapBuffer(
        static_cast<NPDeviceContext3D*>(context), buffer_id, buffer);
  }
  return NPERR_GENERIC_ERROR;
}

// Experimental 3D device API --------------------------------------------------

static NPError Device3DGetNumConfigs(NPP id, int32_t* num_configs) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    return plugin->webplugin()->delegate()->Device3DGetNumConfigs(num_configs);
  }
  return NPERR_GENERIC_ERROR;
}

static NPError Device3DGetConfigAttribs(NPP id,
                                        int32_t config,
                                        int32_t* attrib_list) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    return plugin->webplugin()->delegate()->Device3DGetConfigAttribs(
        config,
        attrib_list);
  }
  return NPERR_GENERIC_ERROR;
}

static NPError Device3DCreateContext(NPP id,
                                     int32_t config,
                                     const int32_t* attrib_list,
                                     NPDeviceContext** context) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    return plugin->webplugin()->delegate()->Device3DCreateContext(
        config,
        attrib_list,
        reinterpret_cast<NPDeviceContext3D**>(context));
  }
  return NPERR_GENERIC_ERROR;
}

static NPError Device3DSynchronizeContext(
    NPP id,
    NPDeviceContext* context,
    NPDeviceSynchronizationMode mode,
    const int32_t* input_attrib_list,
    int32_t* output_attrib_list,
    NPDeviceSynchronizeContextCallbackPtr callback,
    void* callback_data) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    return plugin->webplugin()->delegate()->Device3DSynchronizeContext(
        id,
        static_cast<NPDeviceContext3D*>(context),
        mode,
        input_attrib_list,
        output_attrib_list,
        callback,
        callback_data);
  }
  return NPERR_GENERIC_ERROR;
}

static NPError Device3DRegisterCallback(
    NPP id,
    NPDeviceContext* context,
    int32_t callback_type,
    NPDeviceGenericCallbackPtr callback,
    void* callback_data) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    return plugin->webplugin()->delegate()->Device3DRegisterCallback(
        id,
        static_cast<NPDeviceContext3D*>(context),
        callback_type,
        callback,
        callback_data);
  }
  return NPERR_GENERIC_ERROR;
}

// Audio device API ------------------------------------------------------------

static NPError DeviceAudioQueryCapability(NPP id, int32_t capability,
                                          int32_t* value) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    plugin->webplugin()->delegate()->DeviceAudioQueryCapability(capability,
                                                                value);
    return NPERR_NO_ERROR;
  } else {
    return NPERR_GENERIC_ERROR;
  }
}

static NPError DeviceAudioQueryConfig(NPP id,
                                      const NPDeviceConfig* request,
                                      NPDeviceConfig* obtain) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    return plugin->webplugin()->delegate()->DeviceAudioQueryConfig(
        static_cast<const NPDeviceContextAudioConfig*>(request),
        static_cast<NPDeviceContextAudioConfig*>(obtain));
  }
  return NPERR_GENERIC_ERROR;
}

static NPError DeviceAudioInitializeContext(NPP id,
                                            const NPDeviceConfig* config,
                                            NPDeviceContext* context) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    return plugin->webplugin()->delegate()->DeviceAudioInitializeContext(
        static_cast<const NPDeviceContextAudioConfig*>(config),
        static_cast<NPDeviceContextAudio*>(context));
  }
  return NPERR_GENERIC_ERROR;
}

static NPError DeviceAudioSetStateContext(NPP id,
                                          NPDeviceContext* context,
                                          int32_t state,
                                          intptr_t value) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    return plugin->webplugin()->delegate()->DeviceAudioSetStateContext(
        static_cast<NPDeviceContextAudio*>(context), state, value);
  }
  return NPERR_GENERIC_ERROR;
}

static NPError DeviceAudioGetStateContext(NPP id,
                                          NPDeviceContext* context,
                                          int32_t state,
                                          intptr_t* value) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  return plugin->webplugin()->delegate()->DeviceAudioGetStateContext(
      static_cast<NPDeviceContextAudio*>(context), state, value);
}

static NPError DeviceAudioFlushContext(NPP id,
                                       NPDeviceContext* context,
                                       NPDeviceFlushContextCallbackPtr callback,
                                       void* user_data) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  return plugin->webplugin()->delegate()->DeviceAudioFlushContext(
      id, static_cast<NPDeviceContextAudio*>(context), callback, user_data);
}

static NPError DeviceAudioDestroyContext(NPP id,
                                         NPDeviceContext* context) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  return plugin->webplugin()->delegate()->DeviceAudioDestroyContext(
      static_cast<NPDeviceContextAudio*>(context));
}
// -----------------------------------------------------------------------------

static NPDevice* AcquireDevice(NPP id, NPDeviceID device_id) {
  static NPDevice device_2d = {
    Device2DQueryCapability,
    Device2DQueryConfig,
    Device2DInitializeContext,
    Device2DSetStateContext,
    Device2DGetStateContext,
    Device2DFlushContext,
    Device2DDestroyContext,
    Device2DCreateBuffer,
    Device2DDestroyBuffer,
    Device2DMapBuffer,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
  };
  static NPDevice device_3d = {
    Device3DQueryCapability,
    Device3DQueryConfig,
    Device3DInitializeContext,
    Device3DSetStateContext,
    Device3DGetStateContext,
    Device3DFlushContext,
    Device3DDestroyContext,
    Device3DCreateBuffer,
    Device3DDestroyBuffer,
    Device3DMapBuffer,
    Device3DGetNumConfigs,
    Device3DGetConfigAttribs,
    Device3DCreateContext,
    Device3DRegisterCallback,
    Device3DSynchronizeContext,
  };
  static NPDevice device_audio = {
    DeviceAudioQueryCapability,
    DeviceAudioQueryConfig,
    DeviceAudioInitializeContext,
    DeviceAudioSetStateContext,
    DeviceAudioGetStateContext,
    DeviceAudioFlushContext,
    DeviceAudioDestroyContext,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
  };

  switch (device_id) {
    case NPPepper2DDevice:
      return const_cast<NPDevice*>(&device_2d);
    case NPPepper3DDevice:
      return const_cast<NPDevice*>(&device_3d);
    case NPPepperAudioDevice:
      return const_cast<NPDevice*>(&device_audio);
    default:
      return NULL;
  }
}

static NPError ChooseFile(NPP id,
                          const char* mime_types,
                          NPChooseFileMode mode,
                          NPChooseFileCallback callback,
                          void* user_data) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (!plugin)
    return NPERR_GENERIC_ERROR;

  if (!plugin->webplugin()->delegate()->ChooseFile(mime_types,
                                                   static_cast<int>(mode),
                                                   callback, user_data))
    return NPERR_GENERIC_ERROR;

  return NPERR_NO_ERROR;
}

static void NumberOfFindResultsChanged(NPP id, int total, bool final_result) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin) {
    plugin->webplugin()->delegate()->NumberOfFindResultsChanged(
        total, final_result);
  }
}

static void SelectedFindResultChanged(NPP id, int index) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (plugin)
    plugin->webplugin()->delegate()->SelectedFindResultChanged(index);
}

static NPWidgetExtensions* GetWidgetExtensions(NPP id) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (!plugin)
    return NULL;

  return plugin->webplugin()->delegate()->GetWidgetExtensions();
}

static NPError NPSetCursor(NPP id, NPCursorType type) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (!plugin)
    return NPERR_GENERIC_ERROR;

  return plugin->webplugin()->delegate()->SetCursor(type) ?
      NPERR_NO_ERROR : NPERR_GENERIC_ERROR;
}

static NPFontExtensions* GetFontExtensions(NPP id) {
  scoped_refptr<PluginInstance> plugin(FindInstance(id));
  if (!plugin)
    return NULL;

  return plugin->webplugin()->delegate()->GetFontExtensions();
}

NPError GetPepperExtensionsFunctions(void* value) {
  static const NPNExtensions kExtensions = {
    &AcquireDevice,
    &NumberOfFindResultsChanged,
    &SelectedFindResultChanged,
    &ChooseFile,
    &GetWidgetExtensions,
    &NPSetCursor,
    &GetFontExtensions,
  };

  // Return a pointer to the canonical function table.
  NPNExtensions* extensions = const_cast<NPNExtensions*>(&kExtensions);
  NPNExtensions** exts = reinterpret_cast<NPNExtensions**>(value);
  *exts = extensions;
  return NPERR_NO_ERROR;
}

}  // namespace npapi
}  // namespace webkit