// 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_FPDFAPI_FPDF_PAGE_PAGEINT_H_
#define CORE_FPDFAPI_FPDF_PAGE_PAGEINT_H_

#include <map>
#include <memory>
#include <unordered_map>
#include <vector>

#include "core/fpdfapi/fpdf_page/cpdf_contentmark.h"
#include "core/fpdfapi/fpdf_page/cpdf_countedobject.h"
#include "core/fpdfapi/fpdf_page/cpdf_parseoptions.h"
#include "core/fpdfapi/fpdf_page/include/cpdf_pageobjectholder.h"
#include "core/include/fxge/fx_ge.h"

class CPDF_AllStates;
class CPDF_ColorSpace;
class CPDF_Font;
class CPDF_FontEncoding;
class CPDF_Form;
class CPDF_IccProfile;
class CPDF_Image;
class CPDF_ImageObject;
class CPDF_Page;
class CPDF_ParseOptions;
class CPDF_Pattern;
class CPDF_StreamAcc;
class CPDF_TextObject;
class CPDF_Type3Char;

#define PARSE_STEP_LIMIT 100

class CPDF_StreamParser {
 public:
  enum SyntaxType { EndOfData, Number, Keyword, Name, Others };

  CPDF_StreamParser(const uint8_t* pData, uint32_t dwSize);
  ~CPDF_StreamParser();

  CPDF_Stream* ReadInlineStream(CPDF_Document* pDoc,
                                CPDF_Dictionary* pDict,
                                CPDF_Object* pCSObj,
                                FX_BOOL bDecode);
  SyntaxType ParseNextElement();
  uint8_t* GetWordBuf() { return m_WordBuffer; }
  uint32_t GetWordSize() const { return m_WordSize; }
  CPDF_Object* GetObject() {
    CPDF_Object* pObj = m_pLastObj;
    m_pLastObj = NULL;
    return pObj;
  }
  uint32_t GetPos() const { return m_Pos; }
  void SetPos(uint32_t pos) { m_Pos = pos; }
  CPDF_Object* ReadNextObject(FX_BOOL bAllowNestedArray = FALSE,
                              FX_BOOL bInArray = FALSE);
  void SkipPathObject();

 protected:
  friend class fpdf_page_parser_old_ReadHexString_Test;

  void GetNextWord(FX_BOOL& bIsNumber);
  CFX_ByteString ReadString();
  CFX_ByteString ReadHexString();
  const uint8_t* m_pBuf;

  // Length in bytes of m_pBuf.
  uint32_t m_Size;

  // Current byte position within m_pBuf.
  uint32_t m_Pos;

  uint8_t m_WordBuffer[256];
  uint32_t m_WordSize;
  CPDF_Object* m_pLastObj;

 private:
  bool PositionIsInBounds() const;
};

#define PARAM_BUF_SIZE 16
struct ContentParam {
  enum Type { OBJECT = 0, NUMBER, NAME };
  Type m_Type;
  union {
    struct {
      FX_BOOL m_bInteger;
      union {
        int m_Integer;
        FX_FLOAT m_Float;
      };
    } m_Number;
    CPDF_Object* m_pObject;
    struct {
      int m_Len;
      char m_Buffer[32];
    } m_Name;
  };
};
#define _FPDF_MAX_FORM_LEVEL_ 30
#define _FPDF_MAX_TYPE3_FORM_LEVEL_ 4
#define _FPDF_MAX_OBJECT_STACK_SIZE_ 512
class CPDF_StreamContentParser {
 public:
  CPDF_StreamContentParser(CPDF_Document* pDoc,
                           CPDF_Dictionary* pPageResources,
                           CPDF_Dictionary* pParentResources,
                           CFX_Matrix* pmtContentToUser,
                           CPDF_PageObjectHolder* pObjectHolder,
                           CPDF_Dictionary* pResources,
                           CFX_FloatRect* pBBox,
                           CPDF_ParseOptions* pOptions,
                           CPDF_AllStates* pAllStates,
                           int level);
  ~CPDF_StreamContentParser();

  CPDF_PageObjectHolder* GetPageObjectHolder() const { return m_pObjectHolder; }
  CPDF_AllStates* GetCurStates() const { return m_pCurStates.get(); }
  FX_BOOL IsColored() const { return m_bColored; }
  const FX_FLOAT* GetType3Data() const { return m_Type3Data; }

  void AddNumberParam(const FX_CHAR* str, int len);
  void AddObjectParam(CPDF_Object* pObj);
  void AddNameParam(const FX_CHAR* name, int size);
  int GetNextParamPos();
  void ClearAllParams();
  CPDF_Object* GetObject(uint32_t index);
  CFX_ByteString GetString(uint32_t index);
  FX_FLOAT GetNumber(uint32_t index);
  FX_FLOAT GetNumber16(uint32_t index);
  int GetInteger(uint32_t index) { return (int32_t)(GetNumber(index)); }
  void OnOperator(const FX_CHAR* op);
  void BigCaseCaller(int index);
  uint32_t GetParsePos() { return m_pSyntax->GetPos(); }
  void AddTextObject(CFX_ByteString* pText,
                     FX_FLOAT fInitKerning,
                     FX_FLOAT* pKerning,
                     int count);

  void ConvertUserSpace(FX_FLOAT& x, FX_FLOAT& y);
  void ConvertTextSpace(FX_FLOAT& x, FX_FLOAT& y);
  void OnChangeTextMatrix();
  uint32_t Parse(const uint8_t* pData, uint32_t dwSize, uint32_t max_cost);
  void ParsePathObject();
  void AddPathPoint(FX_FLOAT x, FX_FLOAT y, int flag);
  void AddPathRect(FX_FLOAT x, FX_FLOAT y, FX_FLOAT w, FX_FLOAT h);
  void AddPathObject(int FillType, FX_BOOL bStroke);
  CPDF_ImageObject* AddImage(CPDF_Stream* pStream,
                             CPDF_Image* pImage,
                             FX_BOOL bInline);
  void AddDuplicateImage();
  void AddForm(CPDF_Stream* pStream);
  void SetGraphicStates(CPDF_PageObject* pObj,
                        FX_BOOL bColor,
                        FX_BOOL bText,
                        FX_BOOL bGraph);
  void SaveStates(CPDF_AllStates* pState);
  void RestoreStates(CPDF_AllStates* pState);
  CPDF_Font* FindFont(const CFX_ByteString& name);
  CPDF_ColorSpace* FindColorSpace(const CFX_ByteString& name);
  CPDF_Pattern* FindPattern(const CFX_ByteString& name, FX_BOOL bShading);
  CPDF_Object* FindResourceObj(const CFX_ByteStringC& type,
                               const CFX_ByteString& name);

 protected:
  using OpCodes =
      std::unordered_map<uint32_t, void (CPDF_StreamContentParser::*)()>;
  static OpCodes InitializeOpCodes();

  void Handle_CloseFillStrokePath();
  void Handle_FillStrokePath();
  void Handle_CloseEOFillStrokePath();
  void Handle_EOFillStrokePath();
  void Handle_BeginMarkedContent_Dictionary();
  void Handle_BeginImage();
  void Handle_BeginMarkedContent();
  void Handle_BeginText();
  void Handle_CurveTo_123();
  void Handle_ConcatMatrix();
  void Handle_SetColorSpace_Fill();
  void Handle_SetColorSpace_Stroke();
  void Handle_SetDash();
  void Handle_SetCharWidth();
  void Handle_SetCachedDevice();
  void Handle_ExecuteXObject();
  void Handle_MarkPlace_Dictionary();
  void Handle_EndImage();
  void Handle_EndMarkedContent();
  void Handle_EndText();
  void Handle_FillPath();
  void Handle_FillPathOld();
  void Handle_EOFillPath();
  void Handle_SetGray_Fill();
  void Handle_SetGray_Stroke();
  void Handle_SetExtendGraphState();
  void Handle_ClosePath();
  void Handle_SetFlat();
  void Handle_BeginImageData();
  void Handle_SetLineJoin();
  void Handle_SetLineCap();
  void Handle_SetCMYKColor_Fill();
  void Handle_SetCMYKColor_Stroke();
  void Handle_LineTo();
  void Handle_MoveTo();
  void Handle_SetMiterLimit();
  void Handle_MarkPlace();
  void Handle_EndPath();
  void Handle_SaveGraphState();
  void Handle_RestoreGraphState();
  void Handle_Rectangle();
  void Handle_SetRGBColor_Fill();
  void Handle_SetRGBColor_Stroke();
  void Handle_SetRenderIntent();
  void Handle_CloseStrokePath();
  void Handle_StrokePath();
  void Handle_SetColor_Fill();
  void Handle_SetColor_Stroke();
  void Handle_SetColorPS_Fill();
  void Handle_SetColorPS_Stroke();
  void Handle_ShadeFill();
  void Handle_SetCharSpace();
  void Handle_MoveTextPoint();
  void Handle_MoveTextPoint_SetLeading();
  void Handle_SetFont();
  void Handle_ShowText();
  void Handle_ShowText_Positioning();
  void Handle_SetTextLeading();
  void Handle_SetTextMatrix();
  void Handle_SetTextRenderMode();
  void Handle_SetTextRise();
  void Handle_SetWordSpace();
  void Handle_SetHorzScale();
  void Handle_MoveToNextLine();
  void Handle_CurveTo_23();
  void Handle_SetLineWidth();
  void Handle_Clip();
  void Handle_EOClip();
  void Handle_CurveTo_13();
  void Handle_NextLineShowText();
  void Handle_NextLineShowText_Space();
  void Handle_Invalid();

  CPDF_Document* const m_pDocument;
  CPDF_Dictionary* m_pPageResources;
  CPDF_Dictionary* m_pParentResources;
  CPDF_Dictionary* m_pResources;
  CPDF_PageObjectHolder* m_pObjectHolder;
  int m_Level;
  CFX_Matrix m_mtContentToUser;
  CFX_FloatRect m_BBox;
  CPDF_ParseOptions m_Options;
  ContentParam m_ParamBuf[PARAM_BUF_SIZE];
  uint32_t m_ParamStartPos;
  uint32_t m_ParamCount;
  CPDF_StreamParser* m_pSyntax;
  std::unique_ptr<CPDF_AllStates> m_pCurStates;
  CPDF_ContentMark m_CurContentMark;
  CFX_ArrayTemplate<CPDF_TextObject*> m_ClipTextList;
  CPDF_TextObject* m_pLastTextObject;
  FX_FLOAT m_DefFontSize;
  FX_PATHPOINT* m_pPathPoints;
  int m_PathPointCount;
  int m_PathAllocSize;
  FX_FLOAT m_PathStartX;
  FX_FLOAT m_PathStartY;
  FX_FLOAT m_PathCurrentX;
  FX_FLOAT m_PathCurrentY;
  int m_PathClipType;
  CFX_ByteString m_LastImageName;
  CPDF_Image* m_pLastImage;
  CFX_BinaryBuf m_LastImageDict;
  CFX_BinaryBuf m_LastImageData;
  CPDF_Dictionary* m_pLastImageDict;
  CPDF_Dictionary* m_pLastCloneImageDict;
  FX_BOOL m_bReleaseLastDict;
  FX_BOOL m_bSameLastDict;
  FX_BOOL m_bColored;
  FX_FLOAT m_Type3Data[6];
  FX_BOOL m_bResourceMissing;
  std::vector<std::unique_ptr<CPDF_AllStates>> m_StateStack;
};
class CPDF_ContentParser {
 public:
  enum ParseStatus { Ready, ToBeContinued, Done };

  CPDF_ContentParser();
  ~CPDF_ContentParser();

  ParseStatus GetStatus() const { return m_Status; }
  void Start(CPDF_Page* pPage, CPDF_ParseOptions* pOptions);
  void Start(CPDF_Form* pForm,
             CPDF_AllStates* pGraphicStates,
             CFX_Matrix* pParentMatrix,
             CPDF_Type3Char* pType3Char,
             CPDF_ParseOptions* pOptions,
             int level);
  void Continue(IFX_Pause* pPause);

 private:
  enum InternalStage {
    STAGE_GETCONTENT = 1,
    STAGE_PARSE,
    STAGE_CHECKCLIP,
  };

  ParseStatus m_Status;
  InternalStage m_InternalStage;
  CPDF_PageObjectHolder* m_pObjectHolder;
  FX_BOOL m_bForm;
  CPDF_ParseOptions m_Options;
  CPDF_Type3Char* m_pType3Char;
  uint32_t m_nStreams;
  std::unique_ptr<CPDF_StreamAcc> m_pSingleStream;
  std::vector<std::unique_ptr<CPDF_StreamAcc>> m_StreamArray;
  uint8_t* m_pData;
  uint32_t m_Size;
  uint32_t m_CurrentOffset;
  std::unique_ptr<CPDF_StreamContentParser> m_pParser;
};

class CPDF_DocPageData {
 public:
  explicit CPDF_DocPageData(CPDF_Document* pPDFDoc);
  ~CPDF_DocPageData();

  void Clear(FX_BOOL bRelease = FALSE);
  CPDF_Font* GetFont(CPDF_Dictionary* pFontDict, FX_BOOL findOnly);
  CPDF_Font* GetStandardFont(const CFX_ByteStringC& fontName,
                             CPDF_FontEncoding* pEncoding);
  void ReleaseFont(CPDF_Dictionary* pFontDict);
  CPDF_ColorSpace* GetColorSpace(CPDF_Object* pCSObj,
                                 const CPDF_Dictionary* pResources);
  CPDF_ColorSpace* GetCopiedColorSpace(CPDF_Object* pCSObj);
  void ReleaseColorSpace(CPDF_Object* pColorSpace);
  CPDF_Pattern* GetPattern(CPDF_Object* pPatternObj,
                           FX_BOOL bShading,
                           const CFX_Matrix* matrix);
  void ReleasePattern(CPDF_Object* pPatternObj);
  CPDF_Image* GetImage(CPDF_Object* pImageStream);
  void ReleaseImage(CPDF_Object* pImageStream);
  CPDF_IccProfile* GetIccProfile(CPDF_Stream* pIccProfileStream);
  void ReleaseIccProfile(CPDF_IccProfile* pIccProfile);
  CPDF_StreamAcc* GetFontFileStreamAcc(CPDF_Stream* pFontStream);
  void ReleaseFontFileStreamAcc(CPDF_Stream* pFontStream,
                                FX_BOOL bForce = FALSE);
  FX_BOOL IsForceClear() const { return m_bForceClear; }
  CPDF_CountedColorSpace* FindColorSpacePtr(CPDF_Object* pCSObj) const;
  CPDF_CountedPattern* FindPatternPtr(CPDF_Object* pPatternObj) const;

 private:
  using CPDF_CountedFont = CPDF_CountedObject<CPDF_Font>;
  using CPDF_CountedIccProfile = CPDF_CountedObject<CPDF_IccProfile>;
  using CPDF_CountedImage = CPDF_CountedObject<CPDF_Image>;
  using CPDF_CountedStreamAcc = CPDF_CountedObject<CPDF_StreamAcc>;

  using CPDF_ColorSpaceMap = std::map<CPDF_Object*, CPDF_CountedColorSpace*>;
  using CPDF_FontFileMap = std::map<CPDF_Stream*, CPDF_CountedStreamAcc*>;
  using CPDF_FontMap = std::map<CPDF_Dictionary*, CPDF_CountedFont*>;
  using CPDF_IccProfileMap = std::map<CPDF_Stream*, CPDF_CountedIccProfile*>;
  using CPDF_ImageMap = std::map<uint32_t, CPDF_CountedImage*>;
  using CPDF_PatternMap = std::map<CPDF_Object*, CPDF_CountedPattern*>;

  CPDF_Document* const m_pPDFDoc;
  FX_BOOL m_bForceClear;
  std::map<CFX_ByteString, CPDF_Stream*> m_HashProfileMap;
  CPDF_ColorSpaceMap m_ColorSpaceMap;
  CPDF_FontFileMap m_FontFileMap;
  CPDF_FontMap m_FontMap;
  CPDF_IccProfileMap m_IccProfileMap;
  CPDF_ImageMap m_ImageMap;
  CPDF_PatternMap m_PatternMap;
};

class CPDF_Function {
 public:
  enum class Type {
    kType0Sampled,
    kType2ExpotentialInterpolation,
    kType3Stitching,
    kType4PostScript,
  };

  static CPDF_Function* Load(CPDF_Object* pFuncObj);
  virtual ~CPDF_Function();
  FX_BOOL Call(FX_FLOAT* inputs,
               int ninputs,
               FX_FLOAT* results,
               int& nresults) const;
  int CountInputs() const { return m_nInputs; }
  int CountOutputs() const { return m_nOutputs; }
  FX_FLOAT GetDomain(int i) const { return m_pDomains[i]; }
  Type GetType() const { return m_Type; }

 protected:
  CPDF_Function(Type type);
  FX_BOOL Init(CPDF_Object* pObj);
  virtual FX_BOOL v_Init(CPDF_Object* pObj) = 0;
  virtual FX_BOOL v_Call(FX_FLOAT* inputs, FX_FLOAT* results) const = 0;

  int m_nInputs;
  int m_nOutputs;
  FX_FLOAT* m_pDomains;
  FX_FLOAT* m_pRanges;
  Type m_Type;
};

class CPDF_ExpIntFunc : public CPDF_Function {
 public:
  CPDF_ExpIntFunc();
  ~CPDF_ExpIntFunc() override;

  // CPDF_Function
  FX_BOOL v_Init(CPDF_Object* pObj) override;
  FX_BOOL v_Call(FX_FLOAT* inputs, FX_FLOAT* results) const override;

  int m_nOrigOutputs;
  FX_FLOAT m_Exponent;
  FX_FLOAT* m_pBeginValues;
  FX_FLOAT* m_pEndValues;
};

class CPDF_StitchFunc : public CPDF_Function {
 public:
  CPDF_StitchFunc();
  ~CPDF_StitchFunc() override;

  // CPDF_Function
  FX_BOOL v_Init(CPDF_Object* pObj) override;
  FX_BOOL v_Call(FX_FLOAT* inputs, FX_FLOAT* results) const override;

  std::vector<CPDF_Function*> m_pSubFunctions;
  FX_FLOAT* m_pBounds;
  FX_FLOAT* m_pEncode;

  static const int kRequiredNumInputs = 1;
};

class CPDF_IccProfile {
 public:
  CPDF_IccProfile(const uint8_t* pData, uint32_t dwSize);
  ~CPDF_IccProfile();
  uint32_t GetComponents() const { return m_nSrcComponents; }
  FX_BOOL m_bsRGB;
  void* m_pTransform;

 private:
  uint32_t m_nSrcComponents;
};

class CPDF_DeviceCS : public CPDF_ColorSpace {
 public:
  CPDF_DeviceCS(CPDF_Document* pDoc, int family);

  FX_BOOL GetRGB(FX_FLOAT* pBuf,
                 FX_FLOAT& R,
                 FX_FLOAT& G,
                 FX_FLOAT& B) const override;
  FX_BOOL SetRGB(FX_FLOAT* pBuf,
                 FX_FLOAT R,
                 FX_FLOAT G,
                 FX_FLOAT B) const override;
  FX_BOOL v_GetCMYK(FX_FLOAT* pBuf,
                    FX_FLOAT& c,
                    FX_FLOAT& m,
                    FX_FLOAT& y,
                    FX_FLOAT& k) const override;
  FX_BOOL v_SetCMYK(FX_FLOAT* pBuf,
                    FX_FLOAT c,
                    FX_FLOAT m,
                    FX_FLOAT y,
                    FX_FLOAT k) const override;
  void TranslateImageLine(uint8_t* pDestBuf,
                          const uint8_t* pSrcBuf,
                          int pixels,
                          int image_width,
                          int image_height,
                          FX_BOOL bTransMask = FALSE) const override;
};

class CPDF_PatternCS : public CPDF_ColorSpace {
 public:
  explicit CPDF_PatternCS(CPDF_Document* pDoc);
  ~CPDF_PatternCS() override;
  FX_BOOL v_Load(CPDF_Document* pDoc, CPDF_Array* pArray) override;
  FX_BOOL GetRGB(FX_FLOAT* pBuf,
                 FX_FLOAT& R,
                 FX_FLOAT& G,
                 FX_FLOAT& B) const override;
  CPDF_ColorSpace* GetBaseCS() const override;

 private:
  CPDF_ColorSpace* m_pBaseCS;
  CPDF_CountedColorSpace* m_pCountedBaseCS;
};

#define MAX_PATTERN_COLORCOMPS 16
struct PatternValue {
  CPDF_Pattern* m_pPattern;
  CPDF_CountedPattern* m_pCountedPattern;
  int m_nComps;
  FX_FLOAT m_Comps[MAX_PATTERN_COLORCOMPS];
};

void PDF_ReplaceAbbr(CPDF_Object* pObj);
bool IsPathOperator(const uint8_t* buf, size_t len);

#endif  // CORE_FPDFAPI_FPDF_PAGE_PAGEINT_H_