// 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_VAR_H_
#define PPAPI_CPP_VAR_H_

#include <string>
#include <vector>

#include "ppapi/c/pp_var.h"
#include "ppapi/cpp/pass_ref.h"
#include "ppapi/cpp/resource.h"

/// @file
/// This file defines the API for handling the passing of data types between
/// your module and the page.
namespace pp {

/// A generic type used for passing data types between the module and the page.
class Var {
 public:
  /// Special value passed to constructor to make <code>NULL</code>.
  struct Null {};

  /// Default constructor. Creates a <code>Var</code> of type
  /// <code>Undefined</code>.
  Var();

  /// A constructor used to create a <code>Var</code> of type <code>Null</code>.
  Var(Null);

  /// A constructor used to create a <code>Var</code> of type <code>Bool</code>.
  ///
  /// @param[in] b A boolean value.
  Var(bool b);

  /// A constructor used to create a 32 bit integer <code>Var</code>.
  ///
  /// @param[in] i A 32 bit integer value.
  Var(int32_t i);

  /// A constructor used to create a double value <code>Var</code>.
  ///
  /// @param[in] d A double value.
  Var(double d);

  /// A constructor used to create a UTF-8 character <code>Var</code>.
  Var(const char* utf8_str);  // Must be encoded in UTF-8.

  /// A constructor used to create a UTF-8 character <code>Var</code>.
  Var(const std::string& utf8_str);  // Must be encoded in UTF-8.

  /// A constructor used to create a resource <code>Var</code>.
  explicit Var(const pp::Resource& resource);

  /// A constructor used when you have received a <code>Var</code> as a return
  /// value that has had its reference count incremented for you.
  ///
  /// You will not normally need to use this constructor because
  /// the reference count will not normally be incremented for you.
  Var(PassRef, const PP_Var& var) {
    var_ = var;
    is_managed_ = true;
  }

  /// A constructor that increments the reference count.
  explicit Var(const PP_Var& var);

  struct DontManage {};

  /// This constructor is used when we've given a <code>PP_Var</code> as an
  /// input argument from somewhere and that reference is managing the
  /// reference count for us. The object will not have its reference count
  /// increased or decreased by this class instance.
  ///
  /// @param[in] var A <code>Var</code>.
  Var(DontManage, const PP_Var& var) {
    var_ = var;
    is_managed_ = false;
  }

  /// A constructor for copying a <code>Var</code>.
  Var(const Var& other);

  /// Destructor.
  virtual ~Var();

  /// This function assigns one <code>Var</code> to another <code>Var</code>.
  ///
  /// @param[in] other The <code>Var</code> to be assigned.
  ///
  /// @return A resulting <code>Var</code>.
  virtual Var& operator=(const Var& other);

  /// This function compares object identity (rather than value identity) for
  /// objects, dictionaries, and arrays
  ///
  /// @param[in] other The <code>Var</code> to be compared to this Var.
  ///
  /// @return true if the <code>other</code> <code>Var</code> is the same as
  /// this <code>Var</code>, otherwise false.
  bool operator==(const Var& other) const;

  /// This function determines if this <code>Var</code> is an undefined value.
  ///
  /// @return true if this <code>Var</code> is undefined, otherwise false.
  bool is_undefined() const { return var_.type == PP_VARTYPE_UNDEFINED; }

  /// This function determines if this <code>Var</code> is a null value.
  ///
  /// @return true if this <code>Var</code> is null, otherwise false.
  bool is_null() const { return var_.type == PP_VARTYPE_NULL; }

  /// This function determines if this <code>Var</code> is a bool value.
  ///
  /// @return true if this <code>Var</code> is a bool, otherwise false.
  bool is_bool() const { return var_.type == PP_VARTYPE_BOOL; }

  /// This function determines if this <code>Var</code> is a string value.
  ///
  /// @return true if this <code>Var</code> is a string, otherwise false.
  bool is_string() const { return var_.type == PP_VARTYPE_STRING; }

  /// This function determines if this <code>Var</code> is an object.
  ///
  /// @return true if this <code>Var</code> is an object, otherwise false.
  bool is_object() const { return var_.type == PP_VARTYPE_OBJECT; }

  /// This function determines if this <code>Var</code> is an array.
  ///
  /// @return true if this <code>Var</code> is an array, otherwise false.
  bool is_array() const { return var_.type == PP_VARTYPE_ARRAY; }

  /// This function determines if this <code>Var</code> is a dictionary.
  ///
  /// @return true if this <code>Var</code> is a dictionary, otherwise false.
  bool is_dictionary() const { return var_.type == PP_VARTYPE_DICTIONARY; }

  /// This function determines if this <code>Var</code> is a resource.
  ///
  /// @return true if this <code>Var</code> is a resource, otherwise false.
  bool is_resource() const { return var_.type == PP_VARTYPE_RESOURCE; }

  /// This function determines if this <code>Var</code> is an integer value.
  /// The <code>is_int</code> function returns the internal representation.
  /// The JavaScript runtime may convert between the two as needed, so the
  /// distinction may not be relevant in all cases (int is really an
  /// optimization inside the runtime). So most of the time, you will want
  /// to check is_number().
  ///
  /// @return true if this <code>Var</code> is an integer, otherwise false.
  bool is_int() const { return var_.type == PP_VARTYPE_INT32; }

  /// This function determines if this <code>Var</code> is a double value.
  /// The <code>is_double</code> function returns the internal representation.
  /// The JavaScript runtime may convert between the two as needed, so the
  /// distinction may not be relevant in all cases (int is really an
  /// optimization inside the runtime). So most of the time, you will want to
  /// check is_number().
  ///
  /// @return true if this <code>Var</code> is a double, otherwise false.
  bool is_double() const { return var_.type == PP_VARTYPE_DOUBLE; }

  /// This function determines if this <code>Var</code> is a number.
  ///
  /// @return true if this <code>Var</code> is an int32 or double number,
  /// otherwise false.
  bool is_number() const {
    return var_.type == PP_VARTYPE_INT32 ||
           var_.type == PP_VARTYPE_DOUBLE;
  }

  /// This function determines if this <code>Var</code> is an ArrayBuffer.
  bool is_array_buffer() const { return var_.type == PP_VARTYPE_ARRAY_BUFFER; }

  /// AsBool() converts this <code>Var</code> to a bool. Assumes the
  /// internal representation is_bool(). If it's not, it will assert in debug
  /// mode, and return false.
  ///
  /// @return A bool version of this <code>Var</code>.
  bool AsBool() const;

  /// AsInt() converts this <code>Var</code> to an int32_t. This function
  /// is required because JavaScript doesn't have a concept of ints and doubles,
  /// only numbers. The distinction between the two is an optimization inside
  /// the compiler. Since converting from a double to an int may be lossy, if
  /// you care about the distinction, either always work in doubles, or check
  /// !is_double() before calling AsInt().
  ///
  /// These functions will assert in debug mode and return 0 if the internal
  /// representation is not is_number().
  ///
  /// @return An int32_t version of this <code>Var</code>.
  int32_t AsInt() const;

  /// AsDouble() converts this <code>Var</code> to a double. This function is
  /// necessary because JavaScript doesn't have a concept of ints and doubles,
  /// only numbers. The distinction between the two is an optimization inside
  /// the compiler. Since converting from a double to an int may be lossy, if
  /// you care about the distinction, either always work in doubles, or check
  /// !is_double() before calling AsInt().
  ///
  /// These functions will assert in debug mode and return 0 if the internal
  /// representation is not is_number().
  ///
  /// @return An double version of this <code>Var</code>.
  double AsDouble() const;

  /// AsString() converts this <code>Var</code> to a string. If this object is
  /// not a string, it will assert in debug mode, and return an empty string.
  ///
  /// @return A string version of this <code>Var</code>.
  std::string AsString() const;

  /// Gets the resource contained in the var. If this object is not a resource,
  /// it will assert in debug mode, and return a null resource.
  ///
  /// @return The <code>pp::Resource</code> that is contained in the var.
  pp::Resource AsResource() const;

  /// This function returns the internal <code>PP_Var</code>
  /// managed by this <code>Var</code> object.
  ///
  /// @return A const reference to a <code>PP_Var</code>.
  const PP_Var& pp_var() const {
    return var_;
  }

  /// Detach() detaches from the internal <code>PP_Var</code> of this
  /// object, keeping the reference count the same. This is used when returning
  /// a <code>PP_Var</code> from an API function where the caller expects the
  /// return value to have the reference count incremented for it.
  ///
  /// @return A detached version of this object without affecting the reference
  /// count.
  PP_Var Detach() {
    PP_Var ret = var_;
    var_ = PP_MakeUndefined();
    is_managed_ = true;
    return ret;
  }

  /// DebugString() returns a short description "Var<X>" that can be used for
  /// logging, where "X" is the underlying scalar or "UNDEFINED" or "OBJ" as
  /// it does not call into the browser to get the object description.
  ///
  /// @return A string displaying the value of this <code>Var</code>. This
  /// function is used for debugging.
  std::string DebugString() const;

  /// This class is used when calling the raw C PPAPI when using the C++
  /// <code>Var</code> as a possible NULL exception. This class will handle
  /// getting the address of the internal value out if it's non-NULL and
  /// fixing up the reference count.
  ///
  /// <strong>Warning:</strong> this will only work for things with exception
  /// semantics, i.e. that the value will not be changed if it's a
  /// non-undefined exception. Otherwise, this class will mess up the
  /// refcounting.
  ///
  /// This is a bit subtle:
  /// - If NULL is passed, we return NULL from get() and do nothing.
  ///
  /// - If a undefined value is passed, we return the address of a undefined
  ///   var from get and have the output value take ownership of that var.
  ///
  /// - If a non-undefined value is passed, we return the address of that var
  ///   from get, and nothing else should change.
  ///
  /// Example:
  ///   void FooBar(a, b, Var* exception = NULL) {
  ///     foo_interface->Bar(a, b, Var::OutException(exception).get());
  ///   }
  class OutException {
   public:
    /// A constructor.
    OutException(Var* v)
        : output_(v),
          originally_had_exception_(v && !v->is_undefined()) {
      if (output_) {
        temp_ = output_->var_;
      } else {
        temp_.padding = 0;
        temp_.type = PP_VARTYPE_UNDEFINED;
      }
    }

    /// Destructor.
    ~OutException() {
      if (output_ && !originally_had_exception_)
        *output_ = Var(PASS_REF, temp_);
    }

    PP_Var* get() {
      if (output_)
        return &temp_;
      return NULL;
    }

   private:
    Var* output_;
    bool originally_had_exception_;
    PP_Var temp_;
  };

 protected:
  PP_Var var_;

  // |is_managed_| indicates if the instance manages |var_|.
  // You need to check if |var_| is refcounted to call Release().
  bool is_managed_;

 private:
  // Prevent an arbitrary pointer argument from being implicitly converted to
  // a bool at Var construction. If somebody makes such a mistake, (s)he will
  // get a compilation error.
  Var(void* non_scriptable_object_pointer);
};

}  // namespace pp

#endif  // PPAPI_CPP_VAR_H_
