// Copyright 2014 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com

#ifndef FPDFSDK_JAVASCRIPT_JS_VALUE_H_
#define FPDFSDK_JAVASCRIPT_JS_VALUE_H_

#include <vector>

#include "fxjs/fxjs_v8.h"

class CJS_Array;
class CJS_Date;
class CJS_Document;
class CJS_Object;
class CJS_Runtime;

class CJS_Value {
 public:
  enum Type {
    VT_unknown,
    VT_string,
    VT_number,
    VT_boolean,
    VT_date,
    VT_object,
    VT_null,
    VT_undefined
  };

  static Type GetValueType(v8::Local<v8::Value> value);

  explicit CJS_Value(CJS_Runtime* pRuntime);
  CJS_Value(CJS_Runtime* pRuntime, v8::Local<v8::Value> pValue);
  CJS_Value(CJS_Runtime* pRuntime, int iValue);
  CJS_Value(CJS_Runtime* pRuntime, double dValue);
  CJS_Value(CJS_Runtime* pRuntime, float fValue);
  CJS_Value(CJS_Runtime* pRuntime, bool bValue);
  CJS_Value(CJS_Runtime* pRuntime, CJS_Object* pObj);
  CJS_Value(CJS_Runtime* pRuntime, const char* pStr);
  CJS_Value(CJS_Runtime* pRuntime, const wchar_t* pWstr);
  CJS_Value(CJS_Runtime* pRuntime, const CJS_Array& array);
  CJS_Value(CJS_Runtime* pRuntime, const CJS_Date& date);
  CJS_Value(CJS_Runtime* pRuntime, const CJS_Object* object);
  CJS_Value(const CJS_Value& other);

  ~CJS_Value();

  void SetNull(CJS_Runtime* pRuntime);
  void Set(v8::Local<v8::Value> pValue);

  Type GetType() const { return GetValueType(m_pValue); }

  int ToInt(CJS_Runtime* pRuntime) const;
  bool ToBool(CJS_Runtime* pRuntime) const;
  double ToDouble(CJS_Runtime* pRuntime) const;
  float ToFloat(CJS_Runtime* pRuntime) const;
  CJS_Object* ToObject(CJS_Runtime* pRuntime) const;
  CJS_Array ToArray(CJS_Runtime* pRuntime) const;
  CJS_Date ToDate(CJS_Runtime* pRuntime) const;
  WideString ToWideString(CJS_Runtime* pRuntime) const;
  ByteString ToByteString(CJS_Runtime* pRuntime) const;
  v8::Local<v8::Object> ToV8Object(CJS_Runtime* pRuntime) const;
  v8::Local<v8::Array> ToV8Array(CJS_Runtime* pRuntime) const;
  v8::Local<v8::Value> ToV8Value(CJS_Runtime* pRuntime) const;

  // Replace the current |m_pValue| with a v8::Number if possible
  // to make one from the current |m_pValue|.
  void MaybeCoerceToNumber(CJS_Runtime* pRuntime);

  bool IsArrayObject() const;
  bool IsDateObject() const;

 private:
  v8::Local<v8::Value> m_pValue;
};

class CJS_PropValue {
 public:
  explicit CJS_PropValue(CJS_Runtime* pRuntime);
  CJS_PropValue(CJS_Runtime* pRuntime, const CJS_Value&);
  ~CJS_PropValue();

  void StartSetting() { m_bIsSetting = true; }
  void StartGetting() { m_bIsSetting = false; }
  bool IsSetting() const { return m_bIsSetting; }
  bool IsGetting() const { return !m_bIsSetting; }
  CJS_Runtime* GetJSRuntime() const { return m_pJSRuntime.Get(); }
  CJS_Value* GetJSValue() { return &m_Value; }
  const CJS_Value* GetJSValue() const { return &m_Value; }

  // These calls may re-enter JS (and hence invalidate objects).
  void Set(int val);
  int ToInt() const;

  void Set(bool val);
  bool ToBool() const;

  void Set(double val);
  double ToDouble() const;

  void Set(CJS_Object* pObj);
  CJS_Object* ToObject() const;

  void Set(CJS_Document* pJsDoc);
  CJS_Document* ToDocument() const;

  void Set(const ByteString&);
  ByteString ToByteString() const;

  void Set(const WideString&);
  void Set(const wchar_t* c_string);
  WideString ToWideString() const;

  void Set(v8::Local<v8::Object>);
  v8::Local<v8::Object> ToV8Object() const;

  void Set(const CJS_Array& array);
  CJS_Array ToArray() const;

  void Set(const CJS_Date& date);
  CJS_Date ToDate() const;

 private:
  bool m_bIsSetting;
  CJS_Value m_Value;
  UnownedPtr<CJS_Runtime> const m_pJSRuntime;
};

class CJS_Array {
 public:
  CJS_Array();
  explicit CJS_Array(v8::Local<v8::Array> pArray);
  CJS_Array(const CJS_Array& other);
  virtual ~CJS_Array();

  int GetLength(CJS_Runtime* pRuntime) const;

  // These two calls may re-enter JS (and hence invalidate objects).
  CJS_Value GetElement(CJS_Runtime* pRuntime, unsigned index) const;
  void SetElement(CJS_Runtime* pRuntime,
                  unsigned index,
                  const CJS_Value& value);

  v8::Local<v8::Array> ToV8Array(CJS_Runtime* pRuntime) const;

 private:
  mutable v8::Local<v8::Array> m_pArray;
};

class CJS_Date {
 public:
  CJS_Date();
  explicit CJS_Date(v8::Local<v8::Date> pDate);
  CJS_Date(CJS_Runtime* pRuntime, double dMsec_time);
  CJS_Date(CJS_Runtime* pRuntime,
           int year,
           int mon,
           int day,
           int hour,
           int min,
           int sec);
  CJS_Date(const CJS_Date&);
  virtual ~CJS_Date();

  bool IsValidDate(CJS_Runtime* pRuntime) const;

  int GetYear(CJS_Runtime* pRuntime) const;
  int GetMonth(CJS_Runtime* pRuntime) const;
  int GetDay(CJS_Runtime* pRuntime) const;
  int GetHours(CJS_Runtime* pRuntime) const;
  int GetMinutes(CJS_Runtime* pRuntime) const;
  int GetSeconds(CJS_Runtime* pRuntime) const;

  v8::Local<v8::Date> ToV8Date(CJS_Runtime* pRuntime) const;
  WideString ToWideString(int style) const;

 protected:
  v8::Local<v8::Date> m_pDate;
};

double JS_GetDateTime();
int JS_GetYearFromTime(double dt);
int JS_GetMonthFromTime(double dt);
int JS_GetDayFromTime(double dt);
int JS_GetHourFromTime(double dt);
int JS_GetMinFromTime(double dt);
int JS_GetSecFromTime(double dt);
double JS_DateParse(const WideString& str);
double JS_MakeDay(int nYear, int nMonth, int nDay);
double JS_MakeTime(int nHour, int nMin, int nSec, int nMs);
double JS_MakeDate(double day, double time);

// Some JS methods have the bizarre convention that they may also be called
// with a single argument which is an object containing the actual arguments
// as its properties. The varying arguments to this method are the property
// names as wchar_t string literals corresponding to each positional argument.
// The result will always contain |nKeywords| value, with unspecified ones
// being set to type VT_unknown.
std::vector<CJS_Value> ExpandKeywordParams(
    CJS_Runtime* pRuntime,
    const std::vector<CJS_Value>& originals,
    size_t nKeywords,
    ...);

#endif  // FPDFSDK_JAVASCRIPT_JS_VALUE_H_