//========================================================================
//
// Page.h
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

//========================================================================
//
// Modified under the Poppler project - http://poppler.freedesktop.org
//
// All changes made under the Poppler project to this file are licensed
// under GPL version 2 or later
//
// Copyright (C) 2005 Kristian Høgsberg <krh@redhat.com>
// Copyright (C) 2005 Jeff Muizelaar <jeff@infidigm.net>
// Copyright (C) 2006 Pino Toscano <pino@kde.org>
// Copyright (C) 2006, 2011 Carlos Garcia Campos <carlosgc@gnome.org>
// Copyright (C) 2007 Julien Rebetez <julienr@svn.gnome.org>
// Copyright (C) 2008 Iñigo Martínez <inigomartinez@gmail.com>
// Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
// Copyright (C) 2012, 2017, 2018, 2020 Albert Astals Cid <aacid@kde.org>
// Copyright (C) 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
// Copyright (C) 2013, 2017 Adrian Johnson <ajohnson@redneon.com>
// Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de>
// Copyright (C) 2020 Oliver Sander <oliver.sander@tu-dresden.de>
// Copyright (C) 2020 Nelson Benítez León <nbenitezl@gmail.com>
//
// To see a description of the changes please see the Changelog file that
// came with your tarball or type make ChangeLog if you are building from git
//
//========================================================================

#ifndef PAGE_H
#define PAGE_H

#include <memory>
#include <mutex>

#include "poppler-config.h"
#include "Object.h"

class Dict;
class PDFDoc;
class XRef;
class OutputDev;
class Links;
class LinkAction;
class Annots;
class Annot;
class Gfx;
class FormPageWidgets;
class Form;
class FormField;

//------------------------------------------------------------------------

class PDFRectangle {
public:
  double x1, y1, x2, y2;

  PDFRectangle() { x1 = y1 = x2 = y2 = 0; }
  PDFRectangle(double x1A, double y1A, double x2A, double y2A)
    { x1 = x1A; y1 = y1A; x2 = x2A; y2 = y2A; }
  bool isValid() const { return x1 != 0 || y1 != 0 || x2 != 0 || y2 != 0; }
  bool contains(double x, double y) const { return x1 <= x && x <= x2 && y1 <= y && y <= y2; }
  void clipTo(PDFRectangle *rect);
  
  bool operator==(const PDFRectangle &rect) const { return x1 == rect.x1 && y1 == rect.y1 && x2 == rect.x2 && y2 == rect.y2; }
};

//------------------------------------------------------------------------
// PageAttrs
//------------------------------------------------------------------------

class PageAttrs {
public:

  // Construct a new PageAttrs object by merging a dictionary
  // (of type Pages or Page) into another PageAttrs object.  If
  // <attrs> is nullptr, uses defaults.
  PageAttrs(PageAttrs *attrs, Dict *dict);

  // Destructor.
  ~PageAttrs();

  // Accessors.
  const PDFRectangle *getMediaBox() const { return &mediaBox; }
  const PDFRectangle *getCropBox() const { return &cropBox; }
  bool isCropped() const { return haveCropBox; }
  const PDFRectangle *getBleedBox() const { return &bleedBox; }
  const PDFRectangle *getTrimBox() const { return &trimBox; }
  const PDFRectangle *getArtBox() const { return &artBox; }
  int getRotate() const { return rotate; }
  const GooString *getLastModified() const
    { return lastModified.isString()
	? lastModified.getString() : nullptr; }
  Dict *getBoxColorInfo()
    { return boxColorInfo.isDict() ? boxColorInfo.getDict() : nullptr; }
  Dict *getGroup()
    { return group.isDict() ? group.getDict() : nullptr; }
  Stream *getMetadata()
    { return metadata.isStream() ? metadata.getStream() : nullptr; }
  Dict *getPieceInfo()
    { return pieceInfo.isDict() ? pieceInfo.getDict() : nullptr; }
  Dict *getSeparationInfo()
    { return separationInfo.isDict()
	? separationInfo.getDict() : nullptr; }
  Dict *getResourceDict()
    { return resources.isDict() ? resources.getDict() : nullptr; }
  Object *getResourceDictObject()
    { return &resources; }
  void replaceResource(Object &&obj1)
  {  resources = std::move(obj1); }

  // Clip all other boxes to the MediaBox.
  void clipBoxes();

private:

  bool readBox(Dict *dict, const char *key, PDFRectangle *box);

  PDFRectangle mediaBox;
  PDFRectangle cropBox;
  bool haveCropBox;
  PDFRectangle bleedBox;
  PDFRectangle trimBox;
  PDFRectangle artBox;
  int rotate;
  Object lastModified;
  Object boxColorInfo;
  Object group;
  Object metadata;
  Object pieceInfo;
  Object separationInfo;
  Object resources;
};

//------------------------------------------------------------------------
// Page
//------------------------------------------------------------------------

class Page {
public:

  // Constructor.
  Page(PDFDoc *docA, int numA, Object &&pageDict, Ref pageRefA, PageAttrs *attrsA, Form *form);

  // Destructor.
  ~Page();

  Page(const Page &) = delete;
  Page& operator=(const Page &) = delete;

  // Is page valid?
  bool isOk() const { return ok; }

  // Get page parameters.
  int getNum() const { return num; }
  const PDFRectangle *getMediaBox() const { return attrs->getMediaBox(); }
  const PDFRectangle *getCropBox() const { return attrs->getCropBox(); }
  bool isCropped() const { return attrs->isCropped(); }
  double getMediaWidth() const
    { return attrs->getMediaBox()->x2 - attrs->getMediaBox()->x1; }
  double getMediaHeight() const
    { return attrs->getMediaBox()->y2 - attrs->getMediaBox()->y1; }
  double getCropWidth() const
    { return attrs->getCropBox()->x2 - attrs->getCropBox()->x1; }
  double getCropHeight() const
    { return attrs->getCropBox()->y2 - attrs->getCropBox()->y1; }
  const PDFRectangle *getBleedBox() const { return attrs->getBleedBox(); }
  const PDFRectangle *getTrimBox() const { return attrs->getTrimBox(); }
  const PDFRectangle *getArtBox() const { return attrs->getArtBox(); }
  int getRotate() const { return attrs->getRotate(); }
  const GooString *getLastModified() const { return attrs->getLastModified(); }
  Dict *getBoxColorInfo() { return attrs->getBoxColorInfo(); }
  Dict *getGroup() { return attrs->getGroup(); }
  Stream *getMetadata() { return attrs->getMetadata(); }
  Dict *getPieceInfo() { return attrs->getPieceInfo(); }
  Dict *getSeparationInfo() { return attrs->getSeparationInfo(); }
  PDFDoc *getDoc() { return doc; }
  Ref getRef() { return pageRef; }

  // Get resource dictionary.
  Dict *getResourceDict();
  Object *getResourceDictObject();
  Dict *getResourceDictCopy(XRef *xrefA);

  // Get annotations array.
  Object getAnnotsObject(XRef *xrefA = nullptr) { return annotsObj.fetch(xrefA ? xrefA : xref); }
  // Add a new annotation to the page
  void addAnnot(Annot *annot);
  // Remove an existing annotation from the page
  void removeAnnot(Annot *annot);

  // Return a list of links.
  Links *getLinks();

  // Return a list of annots. It will be valid until the page is destroyed
  Annots *getAnnots(XRef *xrefA = nullptr);

  // Get contents.
  Object getContents() { return contents.fetch(xref); }

  // Get thumb.
  Object getThumb() { return thumb.fetch(xref); }
  bool loadThumb(unsigned char **data, int *width, int *height, int *rowstride);

  // Get transition.
  Object getTrans() { return trans.fetch(xref); }

  // Get form.
  FormPageWidgets *getFormWidgets();

  // Get duration, the maximum length of time, in seconds,
  // that the page is displayed before the presentation automatically
  // advances to the next page
  double getDuration() { return duration; }

  // Get actions
  Object getActions() { return actions.fetch(xref); }

  enum PageAdditionalActionsType {
    actionOpenPage,     ///< Performed when opening the page
    actionClosePage,    ///< Performed when closing the page
  };

  std::unique_ptr<LinkAction> getAdditionalAction(PageAdditionalActionsType type);

  Gfx *createGfx(OutputDev *out, double hDPI, double vDPI,
		 int rotate, bool useMediaBox, bool crop,
		 int sliceX, int sliceY, int sliceW, int sliceH,
		 bool printing,
		 bool (*abortCheckCbk)(void *data),
		 void *abortCheckCbkData, XRef *xrefA = nullptr);

  // Display a page.
  void display(OutputDev *out, double hDPI, double vDPI,
	       int rotate, bool useMediaBox, bool crop,
	       bool printing,
	       bool (*abortCheckCbk)(void *data) = nullptr,
	       void *abortCheckCbkData = nullptr,
               bool (*annotDisplayDecideCbk)(Annot *annot, void *user_data) = nullptr,
               void *annotDisplayDecideCbkData = nullptr,
               bool copyXRef = false);

  // Display part of a page.
  void displaySlice(OutputDev *out, double hDPI, double vDPI,
		    int rotate, bool useMediaBox, bool crop,
		    int sliceX, int sliceY, int sliceW, int sliceH,
		    bool printing,
		    bool (*abortCheckCbk)(void *data) = nullptr,
		    void *abortCheckCbkData = nullptr,
                    bool (*annotDisplayDecideCbk)(Annot *annot, void *user_data) = nullptr,
                    void *annotDisplayDecideCbkData = nullptr,
                    bool copyXRef = false);

  void display(Gfx *gfx);

  void makeBox(double hDPI, double vDPI, int rotate,
	       bool useMediaBox, bool upsideDown,
	       double sliceX, double sliceY, double sliceW, double sliceH,
	       PDFRectangle *box, bool *crop);

  void processLinks(OutputDev *out);

  // Get the page's default CTM.
  void getDefaultCTM(double *ctm, double hDPI, double vDPI,
		     int rotate, bool useMediaBox, bool upsideDown);

private:
  // replace xref
  void replaceXRef(XRef *xrefA);

  PDFDoc *doc;
  XRef *xref;			// the xref table for this PDF file
  Object pageObj;               // page dictionary
  Ref pageRef;                  // page reference
  int num;			// page number
  PageAttrs *attrs;		// page attributes
  Annots *annots;               // annotations
  Object annotsObj;		// annotations array
  Object contents;		// page contents
  Object thumb;			// page thumbnail
  Object trans;			// page transition
  Object actions;		// page additional actions
  double duration;              // page duration
  bool ok;			// true if page is valid
  mutable std::recursive_mutex mutex;
  // standalone widgets are special FormWidget's inside a Page that *are not*
  // referenced from the Catalog's Field array. That means they are standlone,
  // i.e. the PDF document does not have a FormField associated with them. We
  // create standalone FormFields to contain those special FormWidgets, as
  // they are 'de facto' being used to implement tooltips. See #34
  std::vector<FormField*> standaloneFields;
  void loadStandaloneFields(Annots *annotations, Form *form);
};

#endif
