// 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 CORE_INCLUDE_FPDFAPI_FPDF_OBJECTS_H_
#define CORE_INCLUDE_FPDFAPI_FPDF_OBJECTS_H_

#include <map>
#include <set>

#include "core/include/fxcrt/fx_coordinates.h"
#include "core/include/fxcrt/fx_system.h"

class CPDF_Array;
class CPDF_Boolean;
class CPDF_CryptoHandler;
class CPDF_Dictionary;
class CPDF_Document;
class CPDF_IndirectObjectHolder;
class CPDF_Name;
class CPDF_Null;
class CPDF_Number;
class CPDF_Parser;
class CPDF_Reference;
class CPDF_Stream;
class CPDF_StreamAcc;
class CPDF_StreamFilter;
class CPDF_String;
class IFX_FileRead;

class CPDF_Object {
 public:
  enum Type {
    BOOLEAN = 1,
    NUMBER,
    STRING,
    NAME,
    ARRAY,
    DICTIONARY,
    STREAM,
    NULLOBJ,
    REFERENCE
  };

  virtual Type GetType() const = 0;
  FX_DWORD GetObjNum() const { return m_ObjNum; }
  FX_DWORD GetGenNum() const { return m_GenNum; }

  virtual CPDF_Object* Clone(FX_BOOL bDirect = FALSE) const = 0;
  virtual CPDF_Object* GetDirect() const {
    return const_cast<CPDF_Object*>(this);
  }

  FX_BOOL IsModified() const { return FALSE; }
  void Release();

  virtual CFX_ByteString GetString() const { return CFX_ByteString(); }
  virtual CFX_ByteStringC GetConstString() const { return CFX_ByteStringC(); }
  virtual CFX_WideString GetUnicodeText(CFX_CharMap* pCharMap = nullptr) const {
    return CFX_WideString();
  }
  virtual FX_FLOAT GetNumber() const { return 0; }
  virtual int GetInteger() const { return 0; }
  virtual CPDF_Dictionary* GetDict() const { return nullptr; }
  virtual CPDF_Array* GetArray() const { return nullptr; }

  virtual void SetString(const CFX_ByteString& str) { ASSERT(FALSE); }

  virtual bool IsArray() const { return false; }
  virtual bool IsBoolean() const { return false; }
  virtual bool IsDictionary() const { return false; }
  virtual bool IsName() const { return false; }
  virtual bool IsNumber() const { return false; }
  virtual bool IsReference() const { return false; }
  virtual bool IsStream() const { return false; }
  virtual bool IsString() const { return false; }

  virtual CPDF_Array* AsArray() { return nullptr; }
  virtual const CPDF_Array* AsArray() const { return nullptr; }
  virtual CPDF_Boolean* AsBoolean() { return nullptr; }
  virtual const CPDF_Boolean* AsBoolean() const { return nullptr; }
  virtual CPDF_Dictionary* AsDictionary() { return nullptr; }
  virtual const CPDF_Dictionary* AsDictionary() const { return nullptr; }
  virtual CPDF_Name* AsName() { return nullptr; }
  virtual const CPDF_Name* AsName() const { return nullptr; }
  virtual CPDF_Number* AsNumber() { return nullptr; }
  virtual const CPDF_Number* AsNumber() const { return nullptr; }
  virtual CPDF_Reference* AsReference() { return nullptr; }
  virtual const CPDF_Reference* AsReference() const { return nullptr; }
  virtual CPDF_Stream* AsStream() { return nullptr; }
  virtual const CPDF_Stream* AsStream() const { return nullptr; }
  virtual CPDF_String* AsString() { return nullptr; }
  virtual const CPDF_String* AsString() const { return nullptr; }

 protected:
  CPDF_Object() : m_ObjNum(0), m_GenNum(0) {}
  virtual ~CPDF_Object() {}
  void Destroy() { delete this; }

  FX_DWORD m_ObjNum;
  FX_DWORD m_GenNum;

  friend class CPDF_IndirectObjectHolder;
  friend class CPDF_Parser;
  friend class CPDF_SyntaxParser;

 private:
  CPDF_Object(const CPDF_Object& src) {}
};

class CPDF_Boolean : public CPDF_Object {
 public:
  CPDF_Boolean() : m_bValue(false) {}
  explicit CPDF_Boolean(FX_BOOL value) : m_bValue(value) {}

  // CPDF_Object.
  Type GetType() const override { return BOOLEAN; }
  CPDF_Object* Clone(FX_BOOL bDirect = FALSE) const override {
    return new CPDF_Boolean(m_bValue);
  }
  CFX_ByteString GetString() const override {
    return m_bValue ? "true" : "false";
  }
  int GetInteger() const override { return m_bValue; }
  void SetString(const CFX_ByteString& str) override {
    m_bValue = (str == "true");
  }
  bool IsBoolean() const override { return true; }
  CPDF_Boolean* AsBoolean() override { return this; }
  const CPDF_Boolean* AsBoolean() const override { return this; }

 protected:
  ~CPDF_Boolean() {}

  FX_BOOL m_bValue;
};

inline CPDF_Boolean* ToBoolean(CPDF_Object* obj) {
  return obj ? obj->AsBoolean() : nullptr;
}

inline const CPDF_Boolean* ToBoolean(const CPDF_Object* obj) {
  return obj ? obj->AsBoolean() : nullptr;
}

class CPDF_Number : public CPDF_Object {
 public:
  CPDF_Number() : m_bInteger(TRUE), m_Integer(0) {}
  explicit CPDF_Number(int value) : m_bInteger(TRUE), m_Integer(value) {}
  explicit CPDF_Number(FX_FLOAT value) : m_bInteger(FALSE), m_Float(value) {}
  explicit CPDF_Number(const CFX_ByteStringC& str);

  // CPDF_Object.
  Type GetType() const override { return NUMBER; }
  CPDF_Object* Clone(FX_BOOL bDirect = FALSE) const override {
    return m_bInteger ? new CPDF_Number(m_Integer) : new CPDF_Number(m_Float);
  }
  CFX_ByteString GetString() const override;
  FX_FLOAT GetNumber() const override {
    return m_bInteger ? static_cast<FX_FLOAT>(m_Integer) : m_Float;
  }
  int GetInteger() const override {
    return m_bInteger ? m_Integer : static_cast<int>(m_Float);
  }
  void SetString(const CFX_ByteString& str) override;
  bool IsNumber() const override { return true; }
  CPDF_Number* AsNumber() override { return this; }
  const CPDF_Number* AsNumber() const override { return this; }

  FX_BOOL IsInteger() { return m_bInteger; }

 protected:
  ~CPDF_Number() {}

  FX_BOOL m_bInteger;

  union {
    int m_Integer;

    FX_FLOAT m_Float;
  };
};

inline CPDF_Number* ToNumber(CPDF_Object* obj) {
  return obj ? obj->AsNumber() : nullptr;
}

inline const CPDF_Number* ToNumber(const CPDF_Object* obj) {
  return obj ? obj->AsNumber() : nullptr;
}

class CPDF_String : public CPDF_Object {
 public:
  CPDF_String() : m_bHex(FALSE) {}
  CPDF_String(const CFX_ByteString& str, FX_BOOL bHex)
      : m_String(str), m_bHex(bHex) {}
  explicit CPDF_String(const CFX_WideString& str);

  // CPDF_Object.
  Type GetType() const override { return STRING; }
  CPDF_Object* Clone(FX_BOOL bDirect = FALSE) const override {
    return new CPDF_String(m_String, m_bHex);
  }
  CFX_ByteString GetString() const override { return m_String; }
  CFX_ByteStringC GetConstString() const override {
    return CFX_ByteStringC(m_String);
  }
  CFX_WideString GetUnicodeText(CFX_CharMap* pCharMap = nullptr) const override;
  void SetString(const CFX_ByteString& str) override { m_String = str; }
  bool IsString() const override { return true; }
  CPDF_String* AsString() override { return this; }
  const CPDF_String* AsString() const override { return this; }

  FX_BOOL IsHex() const { return m_bHex; }

 protected:
  ~CPDF_String() {}

  CFX_ByteString m_String;
  FX_BOOL m_bHex;
};

inline CPDF_String* ToString(CPDF_Object* obj) {
  return obj ? obj->AsString() : nullptr;
}

inline const CPDF_String* ToString(const CPDF_Object* obj) {
  return obj ? obj->AsString() : nullptr;
}

class CPDF_Name : public CPDF_Object {
 public:
  explicit CPDF_Name(const CFX_ByteString& str) : m_Name(str) {}
  explicit CPDF_Name(const CFX_ByteStringC& str) : m_Name(str) {}
  explicit CPDF_Name(const FX_CHAR* str) : m_Name(str) {}

  // CPDF_Object.
  Type GetType() const override { return NAME; }
  CPDF_Object* Clone(FX_BOOL bDirect = FALSE) const override {
    return new CPDF_Name(m_Name);
  }
  CFX_ByteString GetString() const override { return m_Name; }
  CFX_ByteStringC GetConstString() const override {
    return CFX_ByteStringC(m_Name);
  }
  CFX_WideString GetUnicodeText(CFX_CharMap* pCharMap = nullptr) const override;
  void SetString(const CFX_ByteString& str) override { m_Name = str; }
  bool IsName() const override { return true; }
  CPDF_Name* AsName() override { return this; }
  const CPDF_Name* AsName() const override { return this; }

 protected:
  ~CPDF_Name() {}

  CFX_ByteString m_Name;
};

inline CPDF_Name* ToName(CPDF_Object* obj) {
  return obj ? obj->AsName() : nullptr;
}

inline const CPDF_Name* ToName(const CPDF_Object* obj) {
  return obj ? obj->AsName() : nullptr;
}

class CPDF_Array : public CPDF_Object {
 public:
  CPDF_Array() {}

  // CPDF_Object.
  Type GetType() const override { return ARRAY; }
  CPDF_Object* Clone(FX_BOOL bDirect = FALSE) const override;
  CPDF_Array* GetArray() const override {
    // The method should be made non-const if we want to not be const.
    // See bug #234.
    return const_cast<CPDF_Array*>(this);
  }
  bool IsArray() const override { return true; }
  CPDF_Array* AsArray() override { return this; }
  const CPDF_Array* AsArray() const override { return this; }

  FX_DWORD GetCount() const { return m_Objects.GetSize(); }
  CPDF_Object* GetElement(FX_DWORD index) const;
  CPDF_Object* GetElementValue(FX_DWORD index) const;
  CFX_Matrix GetMatrix();
  CFX_FloatRect GetRect();
  CFX_ByteString GetStringAt(FX_DWORD index) const;
  CFX_ByteStringC GetConstStringAt(FX_DWORD index) const;
  int GetIntegerAt(FX_DWORD index) const;
  FX_FLOAT GetNumberAt(FX_DWORD index) const;
  CPDF_Dictionary* GetDictAt(FX_DWORD index) const;
  CPDF_Stream* GetStreamAt(FX_DWORD index) const;
  CPDF_Array* GetArrayAt(FX_DWORD index) const;
  FX_FLOAT GetFloatAt(FX_DWORD index) const { return GetNumberAt(index); }

  void SetAt(FX_DWORD index,
             CPDF_Object* pObj,
             CPDF_IndirectObjectHolder* pObjs = nullptr);
  void InsertAt(FX_DWORD index,
                CPDF_Object* pObj,
                CPDF_IndirectObjectHolder* pObjs = nullptr);
  void RemoveAt(FX_DWORD index, int nCount = 1);

  void Add(CPDF_Object* pObj, CPDF_IndirectObjectHolder* pObjs = nullptr);
  void AddNumber(FX_FLOAT f);
  void AddInteger(int i);
  void AddString(const CFX_ByteString& str);
  void AddName(const CFX_ByteString& str);
  void AddReference(CPDF_IndirectObjectHolder* pDoc, FX_DWORD objnum);
  void AddReference(CPDF_IndirectObjectHolder* pDoc, CPDF_Object* obj) {
    AddReference(pDoc, obj->GetObjNum());
  }
  void AddNumber16(FX_FLOAT value) { AddNumber(value); }

 protected:
  ~CPDF_Array();

  CFX_ArrayTemplate<CPDF_Object*> m_Objects;
};

inline CPDF_Array* ToArray(CPDF_Object* obj) {
  return obj ? obj->AsArray() : nullptr;
}

inline const CPDF_Array* ToArray(const CPDF_Object* obj) {
  return obj ? obj->AsArray() : nullptr;
}

class CPDF_Dictionary : public CPDF_Object {
 public:
  using iterator = std::map<CFX_ByteString, CPDF_Object*>::iterator;
  using const_iterator = std::map<CFX_ByteString, CPDF_Object*>::const_iterator;

  CPDF_Dictionary() {}

  // CPDF_Object.
  Type GetType() const override { return DICTIONARY; }
  CPDF_Object* Clone(FX_BOOL bDirect = FALSE) const override;
  CPDF_Dictionary* GetDict() const override {
    // The method should be made non-const if we want to not be const.
    // See bug #234.
    return const_cast<CPDF_Dictionary*>(this);
  }
  bool IsDictionary() const override { return true; }
  CPDF_Dictionary* AsDictionary() override { return this; }
  const CPDF_Dictionary* AsDictionary() const override { return this; }

  size_t GetCount() const { return m_Map.size(); }
  CPDF_Object* GetElement(const CFX_ByteStringC& key) const;
  CPDF_Object* GetElementValue(const CFX_ByteStringC& key) const;
  CFX_ByteString GetStringBy(const CFX_ByteStringC& key) const;
  CFX_ByteStringC GetConstStringBy(const CFX_ByteStringC& key) const;
  CFX_ByteString GetStringBy(const CFX_ByteStringC& key,
                             const CFX_ByteStringC& default_str) const;
  CFX_ByteStringC GetConstStringBy(const CFX_ByteStringC& key,
                                   const CFX_ByteStringC& default_str) const;
  CFX_WideString GetUnicodeTextBy(const CFX_ByteStringC& key,
                                  CFX_CharMap* pCharMap = NULL) const;
  int GetIntegerBy(const CFX_ByteStringC& key) const;
  int GetIntegerBy(const CFX_ByteStringC& key, int default_int) const;
  FX_BOOL GetBooleanBy(const CFX_ByteStringC& key,
                       FX_BOOL bDefault = FALSE) const;
  FX_FLOAT GetNumberBy(const CFX_ByteStringC& key) const;
  CPDF_Dictionary* GetDictBy(const CFX_ByteStringC& key) const;
  CPDF_Stream* GetStreamBy(const CFX_ByteStringC& key) const;
  CPDF_Array* GetArrayBy(const CFX_ByteStringC& key) const;
  CFX_FloatRect GetRectBy(const CFX_ByteStringC& key) const;
  CFX_Matrix GetMatrixBy(const CFX_ByteStringC& key) const;
  FX_FLOAT GetFloatBy(const CFX_ByteStringC& key) const {
    return GetNumberBy(key);
  }

  FX_BOOL KeyExist(const CFX_ByteStringC& key) const;

  // Set* functions invalidate iterators for the element with the key |key|.
  void SetAt(const CFX_ByteStringC& key, CPDF_Object* pObj);
  void SetAtName(const CFX_ByteStringC& key, const CFX_ByteString& name);
  void SetAtString(const CFX_ByteStringC& key, const CFX_ByteString& string);
  void SetAtInteger(const CFX_ByteStringC& key, int i);
  void SetAtNumber(const CFX_ByteStringC& key, FX_FLOAT f);
  void SetAtReference(const CFX_ByteStringC& key,
                      CPDF_IndirectObjectHolder* pDoc,
                      FX_DWORD objnum);
  void SetAtReference(const CFX_ByteStringC& key,
                      CPDF_IndirectObjectHolder* pDoc,
                      CPDF_Object* obj) {
    SetAtReference(key, pDoc, obj->GetObjNum());
  }
  void SetAtRect(const CFX_ByteStringC& key, const CFX_FloatRect& rect);
  void SetAtMatrix(const CFX_ByteStringC& key, const CFX_Matrix& matrix);
  void SetAtBoolean(const CFX_ByteStringC& key, FX_BOOL bValue);

  void AddReference(const CFX_ByteStringC& key,
                    CPDF_IndirectObjectHolder* pDoc,
                    FX_DWORD objnum);

  // Invalidates iterators for the element with the key |key|.
  void RemoveAt(const CFX_ByteStringC& key);

  // Invalidates iterators for the element with the key |oldkey|.
  void ReplaceKey(const CFX_ByteStringC& oldkey, const CFX_ByteStringC& newkey);

  iterator begin() { return m_Map.begin(); }
  iterator end() { return m_Map.end(); }
  const_iterator begin() const { return m_Map.begin(); }
  const_iterator end() const { return m_Map.end(); }

 protected:
  ~CPDF_Dictionary();

  std::map<CFX_ByteString, CPDF_Object*> m_Map;
};

inline CPDF_Dictionary* ToDictionary(CPDF_Object* obj) {
  return obj ? obj->AsDictionary() : nullptr;
}

inline const CPDF_Dictionary* ToDictionary(const CPDF_Object* obj) {
  return obj ? obj->AsDictionary() : nullptr;
}

class CPDF_Stream : public CPDF_Object {
 public:
  CPDF_Stream(uint8_t* pData, FX_DWORD size, CPDF_Dictionary* pDict);

  // CPDF_Object.
  Type GetType() const override { return STREAM; }
  CPDF_Object* Clone(FX_BOOL bDirect = FALSE) const override;
  CPDF_Dictionary* GetDict() const override { return m_pDict; }
  CFX_WideString GetUnicodeText(CFX_CharMap* pCharMap = nullptr) const override;
  bool IsStream() const override { return true; }
  CPDF_Stream* AsStream() override { return this; }
  const CPDF_Stream* AsStream() const override { return this; }

  FX_DWORD GetRawSize() const { return m_dwSize; }
  uint8_t* GetRawData() const { return m_pDataBuf; }

  void SetData(const uint8_t* pData,
               FX_DWORD size,
               FX_BOOL bCompressed,
               FX_BOOL bKeepBuf);

  void InitStream(uint8_t* pData, FX_DWORD size, CPDF_Dictionary* pDict);
  void InitStreamFromFile(IFX_FileRead* pFile, CPDF_Dictionary* pDict);

  FX_BOOL ReadRawData(FX_FILESIZE start_pos,
                      uint8_t* pBuf,
                      FX_DWORD buf_size) const;

  FX_BOOL IsMemoryBased() const { return m_GenNum == kMemoryBasedGenNum; }

 protected:
  static const FX_DWORD kMemoryBasedGenNum = (FX_DWORD)-1;

  ~CPDF_Stream();

  void InitStreamInternal(CPDF_Dictionary* pDict);

  CPDF_Dictionary* m_pDict;
  FX_DWORD m_dwSize;
  FX_DWORD m_GenNum;

  union {
    uint8_t* m_pDataBuf;
    IFX_FileRead* m_pFile;
  };
};

inline CPDF_Stream* ToStream(CPDF_Object* obj) {
  return obj ? obj->AsStream() : nullptr;
}

inline const CPDF_Stream* ToStream(const CPDF_Object* obj) {
  return obj ? obj->AsStream() : nullptr;
}

class CPDF_StreamAcc {
 public:
  CPDF_StreamAcc();
  ~CPDF_StreamAcc();

  void LoadAllData(const CPDF_Stream* pStream,
                   FX_BOOL bRawAccess = FALSE,
                   FX_DWORD estimated_size = 0,
                   FX_BOOL bImageAcc = FALSE);

  const CPDF_Stream* GetStream() const { return m_pStream; }
  CPDF_Dictionary* GetDict() const {
    return m_pStream ? m_pStream->GetDict() : nullptr;
  }
  const uint8_t* GetData() const;
  FX_DWORD GetSize() const;
  const CFX_ByteString& GetImageDecoder() const { return m_ImageDecoder; }
  const CPDF_Dictionary* GetImageParam() const { return m_pImageParam; }

  uint8_t* DetachData();

 protected:
  uint8_t* m_pData;
  FX_DWORD m_dwSize;
  FX_BOOL m_bNewBuf;
  CFX_ByteString m_ImageDecoder;
  CPDF_Dictionary* m_pImageParam;
  const CPDF_Stream* m_pStream;
  uint8_t* m_pSrcData;
};

class CPDF_Null : public CPDF_Object {
 public:
  CPDF_Null() {}

  // CPDF_Object.
  Type GetType() const override { return NULLOBJ; }
  CPDF_Object* Clone(FX_BOOL bDirect = FALSE) const override {
    return new CPDF_Null;
  }
};

class CPDF_Reference : public CPDF_Object {
 public:
  CPDF_Reference(CPDF_IndirectObjectHolder* pDoc, int objnum)
      : m_pObjList(pDoc), m_RefObjNum(objnum) {}

  // CPDF_Object.
  Type GetType() const override { return REFERENCE; }
  CPDF_Object* Clone(FX_BOOL bDirect = FALSE) const override;
  CPDF_Object* GetDirect() const override;
  CFX_ByteString GetString() const override {
    CPDF_Object* obj = SafeGetDirect();
    return obj ? obj->GetString() : CFX_ByteString();
  }
  CFX_ByteStringC GetConstString() const override {
    CPDF_Object* obj = SafeGetDirect();
    return obj ? obj->GetConstString() : CFX_ByteStringC();
  }
  FX_FLOAT GetNumber() const override {
    CPDF_Object* obj = SafeGetDirect();
    return obj ? obj->GetNumber() : 0;
  }
  int GetInteger() const override {
    CPDF_Object* obj = SafeGetDirect();
    return obj ? obj->GetInteger() : 0;
  }
  CPDF_Dictionary* GetDict() const override {
    CPDF_Object* obj = SafeGetDirect();
    return obj ? obj->GetDict() : nullptr;
  }
  // TODO(weili): check whether GetUnicodeText() and GetArray() are needed.
  bool IsReference() const override { return true; }
  CPDF_Reference* AsReference() override { return this; }
  const CPDF_Reference* AsReference() const override { return this; }

  CPDF_IndirectObjectHolder* GetObjList() const { return m_pObjList; }
  FX_DWORD GetRefObjNum() const { return m_RefObjNum; }

  void SetRef(CPDF_IndirectObjectHolder* pDoc, FX_DWORD objnum);

 protected:
  ~CPDF_Reference() {}
  CPDF_Object* SafeGetDirect() const {
    CPDF_Object* obj = GetDirect();
    if (!obj || obj->IsReference())
      return nullptr;
    return obj;
  }

  CPDF_IndirectObjectHolder* m_pObjList;
  FX_DWORD m_RefObjNum;
};

inline CPDF_Reference* ToReference(CPDF_Object* obj) {
  return obj ? obj->AsReference() : nullptr;
}

inline const CPDF_Reference* ToReference(const CPDF_Object* obj) {
  return obj ? obj->AsReference() : nullptr;
}

class CPDF_IndirectObjectHolder {
 public:
  using iterator = std::map<FX_DWORD, CPDF_Object*>::iterator;
  using const_iterator = std::map<FX_DWORD, CPDF_Object*>::const_iterator;

  explicit CPDF_IndirectObjectHolder(CPDF_Parser* pParser);
  ~CPDF_IndirectObjectHolder();

  CPDF_Object* GetIndirectObject(FX_DWORD objnum);
  FX_DWORD AddIndirectObject(CPDF_Object* pObj);
  void ReleaseIndirectObject(FX_DWORD objnum);

  // Takes ownership of |pObj|.
  FX_BOOL InsertIndirectObject(FX_DWORD objnum, CPDF_Object* pObj);

  FX_DWORD GetLastObjNum() const { return m_LastObjNum; }
  iterator begin() { return m_IndirectObjs.begin(); }
  const_iterator begin() const { return m_IndirectObjs.begin(); }
  iterator end() { return m_IndirectObjs.end(); }
  const_iterator end() const { return m_IndirectObjs.end(); }

 protected:
  CPDF_Parser* m_pParser;
  FX_DWORD m_LastObjNum;
  std::map<FX_DWORD, CPDF_Object*> m_IndirectObjs;
};

#endif  // CORE_INCLUDE_FPDFAPI_FPDF_OBJECTS_H_