// 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/cpp/dev/scriptable_object_deprecated.h"
#include "ppapi/c/dev/ppb_memory_dev.h"
#include "ppapi/c/dev/ppp_class_deprecated.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/var.h"

namespace pp {

namespace deprecated {

namespace {

// Allows converting an output param of a Var to an output param of a PP_Var
// for exceptions. The object is only copied if it is not void, which we
// take to mean an exception occurred.
class ExceptionConverter {
 public:
  ExceptionConverter(PP_Var* out) : out_(out) {
  }
  ~ExceptionConverter() {
    if (!exception_.is_undefined())
      *out_ = exception_.Detach();
  }

  Var* Get() { return &exception_; }

 private:
  PP_Var* out_;
  Var exception_;
};

// Used internally to convert a C-style array of PP_Var to a vector of Var.
void ArgListToVector(uint32_t argc, PP_Var* argv, std::vector<Var>* output) {
  output->reserve(argc);
  for (size_t i = 0; i < argc; i++)
    output->push_back(Var(Var::DontManage(), argv[i]));
}

bool HasProperty(void* object, PP_Var name, PP_Var* exception) {
  ExceptionConverter e(exception);
  return static_cast<ScriptableObject*>(object)->HasProperty(
      Var(Var::DontManage(), name), e.Get());
}

bool HasMethod(void* object, PP_Var name, PP_Var* exception) {
  ExceptionConverter e(exception);
  return static_cast<ScriptableObject*>(object)->HasMethod(
      Var(Var::DontManage(), name), e.Get());
}

PP_Var GetProperty(void* object,
                   PP_Var name,
                   PP_Var* exception) {
  ExceptionConverter e(exception);
  return static_cast<ScriptableObject*>(object)->GetProperty(
      Var(Var::DontManage(), name), e.Get()).Detach();
}

void GetAllPropertyNames(void* object,
                         uint32_t* property_count,
                         PP_Var** properties,
                         PP_Var* exception) {
  ExceptionConverter e(exception);
  std::vector<Var> props;
  static_cast<ScriptableObject*>(object)->GetAllPropertyNames(&props, e.Get());
  if (props.empty())
    return;
  *property_count = static_cast<uint32_t>(props.size());

  const PPB_Memory_Dev* memory_if = static_cast<const PPB_Memory_Dev*>(
      pp::Module::Get()->GetBrowserInterface(PPB_MEMORY_DEV_INTERFACE));
  *properties = static_cast<PP_Var*>(
      memory_if->MemAlloc(sizeof(PP_Var) * props.size()));

  for (size_t i = 0; i < props.size(); ++i)
    (*properties)[i] = props[i].Detach();
}

void SetProperty(void* object,
                 PP_Var name,
                 PP_Var value,
                 PP_Var* exception) {
  ExceptionConverter e(exception);
  static_cast<ScriptableObject*>(object)->SetProperty(
      Var(Var::DontManage(), name), Var(Var::DontManage(), value), e.Get());
}

void RemoveProperty(void* object,
                    PP_Var name,
                    PP_Var* exception) {
  ExceptionConverter e(exception);
  static_cast<ScriptableObject*>(object)->RemoveProperty(
      Var(Var::DontManage(), name), e.Get());
}

PP_Var Call(void* object,
            PP_Var method_name,
            uint32_t argc,
            PP_Var* argv,
            PP_Var* exception) {
  ExceptionConverter e(exception);

  std::vector<Var> args;
  ArgListToVector(argc, argv, &args);
  return static_cast<ScriptableObject*>(object)->Call(
      Var(Var::DontManage(), method_name), args, e.Get()).Detach();
}

PP_Var Construct(void* object,
                 uint32_t argc,
                 PP_Var* argv,
                 PP_Var* exception) {
  ExceptionConverter e(exception);

  std::vector<Var> args;
  ArgListToVector(argc, argv, &args);
  return static_cast<ScriptableObject*>(object)->Construct(
      args, e.Get()).Detach();
}

void Deallocate(void* object) {
  delete static_cast<ScriptableObject*>(object);
}

PPP_Class_Deprecated plugin_class = {
  &HasProperty,
  &HasMethod,
  &GetProperty,
  &GetAllPropertyNames,
  &SetProperty,
  &RemoveProperty,
  &Call,
  &Construct,
  &Deallocate
};

}  // namespace

bool ScriptableObject::HasProperty(const Var& /*name*/, Var* /*exception*/) {
  return false;
}

bool ScriptableObject::HasMethod(const Var& /*name*/, Var* /*exception*/) {
  return false;
}

Var ScriptableObject::GetProperty(const Var& /*name*/, Var* exception) {
  *exception = Var("Property does not exist on ScriptableObject");
  return Var();
}

void ScriptableObject::GetAllPropertyNames(std::vector<Var>* /*properties*/,
                                           Var* /*exception*/) {
}

void ScriptableObject::SetProperty(const Var& /*name*/,
                                   const Var& /*value*/,
                                   Var* exception) {
  *exception = Var("Property can not be set on ScriptableObject");
}

void ScriptableObject::RemoveProperty(const Var& /*name*/,
                                      Var* exception) {
  *exception = Var(
      "Property does does not exist to be removed in ScriptableObject");
}

Var ScriptableObject::Call(const Var& /*method_name*/,
                           const std::vector<Var>& /*args*/,
                           Var* exception) {
  *exception = Var("Method does not exist to call in ScriptableObject");
  return Var();
}

Var ScriptableObject::Construct(const std::vector<Var>& /*args*/,
                                Var* exception) {
  *exception = Var("Construct method does not exist in ScriptableObject");
  return Var();
}

// static
const PPP_Class_Deprecated* ScriptableObject::GetClass() {
  return &plugin_class;
}

}  // namespace deprecated

}  // namespace pp
