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

#ifndef PPAPI_CPP_MODULE_H_
#define PPAPI_CPP_MODULE_H_

#include <map>
#include <string>

#include "ppapi/c/pp_instance.h"
#include "ppapi/c/pp_module.h"
#include "ppapi/c/pp_stdint.h"
#include "ppapi/c/ppb.h"
#include "ppapi/c/ppb_core.h"
#include "ppapi/cpp/core.h"


/// @file
/// This file defines a Module class.
namespace pp {

class Instance;

/// The Module class.  The browser calls CreateInstance() to create
/// an instance of your module on the web page.  The browser creates a new
/// instance for each <code>\<embed></code> tag with
/// <code>type="application/x-nacl"</code>
class Module {
 public:
  typedef std::map<PP_Instance, Instance*> InstanceMap;

  // You may not call any other PP functions from the constructor, put them
  // in Init instead. Various things will not be set up until the constructor
  // completes.
  Module();
  virtual ~Module();

  /// Get() returns the global instance of this module object, or NULL if the
  /// module is not initialized yet.
  ///
  /// @return The global instance of the module object.
  static Module* Get();

  /// Init() is automatically called after the object is created. This is where
  /// you can put functions that rely on other parts of the API, now that the
  /// module has been created.
  ///
  /// @return true if successful, otherwise false.
  virtual bool Init();

  /// The pp_module() function returns the internal module handle.
  ///
  /// @return A <code>PP_Module</code> internal module handle.
  PP_Module pp_module() const { return pp_module_; }

  /// The get_browser_interface() function returns the internal
  /// <code>get_browser_interface</code> pointer.
  ///
  /// @return A <code>PPB_GetInterface</code> internal pointer.
  // TODO(sehr): This should be removed once the NaCl browser plugin no longer
  // needs it.
  PPB_GetInterface get_browser_interface() const {
    return get_browser_interface_;
  }

  /// The core() function returns the core interface for doing basic
  /// global operations. The return value is guaranteed to be non-NULL once the
  /// module has successfully initialized and during the Init() call.
  ///
  /// It will be NULL before Init() has been called.
  ///
  /// @return The core interface for doing basic global operations.
  Core* core() { return core_; }

  /// GetPluginInterface() implements <code>GetInterface</code> for the browser
  /// to get module interfaces. If you need to provide your own implementations
  /// of new interfaces, use AddPluginInterface() which this function will use.
  ///
  /// @param[in] interface_name The module interface for the browser to get.
  const void* GetPluginInterface(const char* interface_name);

  /// GetBrowserInterface() returns interfaces which the browser implements
  /// (i.e. PPB interfaces).
  /// @param[in] interface_name The browser interface for the module to get.
  const void* GetBrowserInterface(const char* interface_name);

  /// InstanceForPPInstance() returns the object associated with this
  /// <code>PP_Instance</code>, or NULL if one is not found. This should only
  /// be called from the main thread! This instance object may be destroyed at
  /// any time on the main thread, so using it on other threads may cause a
  /// crash.
  ///
  /// @param[in] instance This <code>PP_Instance</code>.
  ///
  /// @return The object associated with this <code>PP_Instance</code>,
  /// or NULL if one is not found.
  Instance* InstanceForPPInstance(PP_Instance instance);

  /// AddPluginInterface() adds a handler for a provided interface name. When
  /// the browser requests that interface name, the provided
  /// <code>vtable</code> will be returned.
  ///
  /// In general, modules will not need to call this directly. Instead, the
  /// C++ wrappers for each interface will register themselves with this
  /// function.
  ///
  /// This function may be called more than once with the same interface name
  /// and vtable with no effect. However, it may not be used to register a
  /// different vtable for an already-registered interface. It will assert for
  /// a different registration for an already-registered interface in debug
  /// mode, and just ignore the registration in release mode.
  ///
  /// @param[in] interface_name The interface name that will receive a handler.
  /// @param[in,out] vtable The vtable to return for
  /// <code>interface_name</code>.
  void AddPluginInterface(const std::string& interface_name,
                          const void* vtable);

  // InternalInit() sets the browser interface and calls the regular Init()
  /// function that can be overridden by the base classes.
  ///
  /// @param[in] mod A <code>PP_Module</code>.
  /// @param[in] get_browser_interface The browser interface to set.
  ///
  /// @return true if successful, otherwise false.
  // TODO(brettw) make this private when I can figure out how to make the
  // initialize function a friend.
  bool InternalInit(PP_Module mod,
                    PPB_GetInterface get_browser_interface);

  /// The current_instances() function allows iteration over the
  /// current instances in the module.
  ///
  /// @return An <code>InstanceMap</code> of all instances in the module.
  const InstanceMap& current_instances() const { return current_instances_; }

 protected:
  /// CreateInstance() should be overridden to create your own module type.
  ///
  /// @param[in] instance A <code>PP_Instance</code>.
  ///
  /// @return The resulting instance.
  virtual Instance* CreateInstance(PP_Instance instance) = 0;

 private:
  friend PP_Bool Instance_DidCreate(PP_Instance pp_instance,
                                    uint32_t argc,
                                    const char* argn[],
                                    const char* argv[]);
  friend void Instance_DidDestroy(PP_Instance instance);

  // Unimplemented (disallow copy and assign).
  Module(const Module&);
  Module& operator=(const Module&);

  // Instance tracking.
  InstanceMap current_instances_;

  PP_Module pp_module_;
  PPB_GetInterface get_browser_interface_;

  Core* core_;

  // All additional interfaces this plugin can handle as registered by
  // AddPluginInterface.
  typedef std::map<std::string, const void*> InterfaceMap;
  InterfaceMap additional_interfaces_;
};

}  // namespace pp

#endif  // PPAPI_CPP_MODULE_H_
