#ifndef FS_OFD_PAGE_H_
#define FS_OFD_PAGE_H_

#include "common/fs_common.h"
#include "fs_ofdlayer.h"
#include "annots/fs_ofdannot.h"

/**
 * @brief Foxit 命名空间。
 *
 * @details Foxit PDF SDK 的所有定义和类都在此命名空间内定义。
 */
namespace foxit {
/**
 * @brief OFD 命名空间。
 */
namespace ofd {
namespace graphics {
class OFDBlockObject;
}
class Actions;
class TextSearchCharInfo;
class TextSearch;
class Annot;

/** 此类表示和OFD页面相关的方法。*/
class OFDPage : public Base {
 public:
  /**
   * @brief OFD页面区域类型枚举。
   *
   * @details 该枚举值应单独使用。
   */
  typedef enum _PageBoxType {
    /** @brief 出血框，超出物理设备限制的额外出血区域，默认为物理区域。 */
    e_BleedBox = 0,
    /** @brief 物理区域。 */
    e_PhysicalBox = 1,
    /** @brief 显示区域，页面内容实际显示或打印输出区域。 */
    e_ApplicationBox = 2,
    /** @brief 版心区域即文件正文区域。 */
    e_ContentBox = 3,
    /** @brief 裁剪区域。 */
    e_CropBox = 4
  } PageBoxType;
  

  /** @brief 析构函数。 */
  ~OFDPage();

  /**
   * @brief 构造函数，通过另一个OFD页面对象。
   *
   * @param[in] other  另一个OFD页面对象。
   */
  OFDPage(const OFDPage& other);

  /**
   * @brief 赋值操作符。
   *
   * @param[in] other  另一个OFD页面对象，其值将被赋给当前对象。
   *
   * @return 当前对象自身的引用。
   */
  OFDPage& operator = (const OFDPage& other);

  /**
   * @brief 相等操作符。
   *
   * @param[in] other  另一个OFD页面对象。此函数将检查当前对象是否等于这个对象。
   *
   * @return <b>true</b> 表示相等，<b>false</b> 表示不相等。
   */
  bool operator == (const OFDPage& other) const;

  /**
   * @brief 不相等操作符。
   *
   * @param[in] other  另一个OFD页面对象。此函数将检查当前对象是否不等于这个对象。
   *
   * @return <b>true</b> 表示不相等，<b>false</b> 表示相等。
   */
  bool operator != (const OFDPage& other) const;

  /**
   * @brief 检查当前对象是否为空。
   *
   * @details 当前对象为空时，表示当前对象是无用的。
   *
   * @return <b>true</b> 表示当前对象为空，<b>false</b> 表示不为空。
   */
  bool IsEmpty() const;

  /**
   * @brief 判断当前页面是否为模板页。
   *
   * @return <b>true</b> 表示当前页面为模板页，<b>false</b> 表示当前页面不是模板页面。
   */
  bool IsTemplatePage() const;

  /**
   * @brief 获取页索引。
   *
   * @return 返回页索引。
   */
  int GetIndex() const;

  /**
   * @brief 获取页ID。
   *
   * @return 返回页ID。
   */
  int GetID() const;

  /**
   * @brief 计算渲染矩阵。
   *
   * @param[in] left              设备区域左上角X坐标。
   * @param[in] top               设备区域左上角Y坐标。
   * @param[in] width             设备区域宽度。
   * @param[in] height            设备区域高度。
   * @param[in] rotate            旋转角度。
   *
   * @return 渲染矩阵。
   */
  Matrix GetDisplayMatrix(int left, int top, int width, int height, common::Rotation rotate) const;

  /**
   * @brief 设置页面区域。
   *
   * @param[in] box_type          要设置的页面区域类型（值为PageBoxType类型）。
   * @param[in] rect              页面区域值。坐标系为OFD坐标系。
   *
   * @return <b>true</b>代表设置成功, <b>false</b>代表设置失败。
   */
  bool SetPageBox(PageBoxType box_type, const RectF& rect);

  /**
   * @brief 设置页面旋转。
   *
   * @param[in] rotate             旋转角度。
   *
   * @return <b>true</b>代表设置成功, <b>false</b>代表设置失败。
   */
  bool SetRotation(common::Rotation rotate);

  /**
   * @brief 获取页面旋转角度。
   *
   * @return 页面旋转角度。
   */
  common::Rotation GetRotation() const;

  /**
   * @brief 获取页面区域。
   *
   * @param[in] box_type          要设置的页面区域类型（值为PageBoxType类型）。
   *
   * @return 页面区域。坐标系为OFD坐标系。
   */
  RectF GetPageBox(PageBoxType box_type) const;

  /**
   * @brief 获取页面动作数组。
   *
   * @return 页面动作数组句柄。
   */
  Actions GetActions() const;

  /**
   * @brief 设置模板页属性。
   *
   * @param[in] template_page       模板页句柄。
   * @param[in] order               图层类型。
   *
   * @return <b>true</b>代表设置成功, <b>false</b>代表设置失败。
   */
  bool SetTemplatePage(OFDPage template_page, foxit::ofd::OFDLayer::LayerType order = foxit::ofd::OFDLayer::e_LayerTypeBody);

  /**
   * @brief 根据指定ID，设置页面的模板属性和呈现顺序。
   *
   * @param[in] template_id         模板页ID。
   * @param[in] order               模板页的呈现顺序，0：背景层（默认）1：正文层 2：前景层。
   *
   * @return <b>true</b>代表设置成功, <b>false</b>代表设置失败。
   */
  bool SetTemplatePageByID(int template_id, foxit::ofd::OFDLayer::LayerType order = foxit::ofd::OFDLayer::e_LayerTypeBody);

  /**
   * @brief 获取页中模板页数。
   *
   * @return 返回页模板页的数量。
   */
  int GetTemplatePageCount() const;

  /**
   * @brief 新建一个层，并追加至页中。
   *
   * @return 层对象。
   */
  OFDLayer AddLayer();

  /**
   * @brief 新建一个层，并插入页中指定位置。
   *
   * @param[in] index 层索引号（由0开始编号）。
   *
   * @return 层对象。
   */
  OFDLayer InsertLayer(int index);

  /**
   * @brief 删除指定索引的层。
   *
   * @param[in] index 指定层的索引。有效范围：从0到(<i>count</i>-1)。
   *                  <i>count</i> 由函数 @link OFDPage::GetLayerCount @endlink 返回。
   *
   * @return <b>true</b>代表删除成功, <b>false</b>代表删除页面失败。
   */
  bool RemoveLayer(int index);

  /**
   * @brief 获取页中层数。
   *
   * @return 页中层数。
   */
  int GetLayerCount() const;

  /**
   * @brief 获取指定索引的层句柄。
   *
   * @param[in] index 指定层的索引。有效范围：从0到(<i>count</i>-1)。
   *                  <i>count</i> 由函数 @link OFDPage::GetLayerCount @endlink 返回。
   *
   * @return 层对象。
   */
  OFDLayer GetLayer(int index) const;

  /**
   * @brief 新建一个页面块。这个接口将创建一个Layer插入到页面，在Layer中添加页面块。
   *
   * @return 返回页面块对象。
   */
  foxit::ofd::graphics::OFDBlockObject AddPageBlock();

  /**
   * @brief 新建一个页面块。这个接口将创建一个Layer插入到页面，在Layer中添加页面块。
   *
   * @param[in] 指定层的索引。有效范围：从0到(<i>count</i>-1)。
   *                          <i>count</i> 由函数 @link OFDPage::GetLayerCount @endlink 返回。
   *
   * @return 返回页面块句柄。
   */
  foxit::ofd::graphics::OFDBlockObject InsertPageBlock(int index);

  /**
   * @brief 删除指定索引页面块。
   *
   * @param[in] index 指定层的索引。有效范围：从0到(<i>count</i>-1)。
   *                  <i>count</i> 由函数 @link OFDPage::GetLayerCount @endlink 返回。
   *
   * @return <b>true</b>代表删除页面块成功, <b>false</b>代表删除页面块失败。
   */
  bool RemovePageBlock(int index);

  /**
   * @brief 根据搜索结果，完成字符替换。
   *
   * @param[in] char_info      搜索文本字符信息对象。
   * @param[in] character      被屏蔽的信息替换并显示的字符，默认是 'X'。
   *
   * @return <b>true</b>代表替换成功, <b>false</b>代表替换失败。
   */
  bool ReplaceCharacterBySearch(TextSearchCharInfo char_info, foxit::uint8 character = 'X');

  /**
   * @brief 根据矩形区域，完成字符替换。
   *
   * @param[in] rect                矩形区域。坐标系为OFD坐标系。
   * @param[in] recover             被屏蔽的信息是否可恢复，<b>true</b>代表可恢复，<b>false</b>代表不可恢复。
   * @param[in] character           被屏蔽的信息替换并显示的字符，默认是 'X'。
   *
   * @return <b>true</b>代表替换成功, <b>false</b>代表替换失败。
   */
  bool ReplaceCharacterInRect(const RectF& rect, bool recover = true, foxit::uint8 character = 'X');

  /**
   * @brief 通过涂黑进行页面信息屏蔽。
   *
   * @param[in] rect                矩形区域。坐标系为OFD坐标系。
   * @param[in] shape               0 矩形，1 圆形。
   *
   * @return <b>true</b>代表遮盖成功, <b>false</b>代表遮盖失败。
   */
  bool MaskCharacterWithBlack(const RectF& rect, int shape);

  /**
   * @brief 通过模糊进行页面信息屏蔽。
   *
   * @param[in] rect                矩形区域。坐标系为OFD坐标系。
   * @param[in] shape               0 矩形，1 圆形。
   * @param[in] horz_blur           横向高斯模糊，一般取值为32。
   * @param[in] vert_blur           纵向高斯模糊，一般取值为32。
   *
   * @return <b>true</b>代表遮盖成功, <b>false</b>代表遮盖失败。
   */
  bool MaskCharacterWithBlur(const RectF& rect, int shape, int horz_blur, int vert_blur);

  /**
   * @brief 通过马赛克进行页面信息屏蔽。
   *
   * @param[in] rect                矩形区域。坐标系为OFD坐标系。
   * @param[in] shape               0 矩形，1 圆形。
   * @param[in] vector_length       马赛克向量长度，一般取值为4。
   *
   * @return <b>true</b>代表遮盖成功, <b>false</b>代表遮盖失败。
   */
  bool MaskCharacterWithMosaic(const RectF& rect, int shape, int vector_length);

  /**
   * @brief 页面信息预遮盖，可恢复。
   *
   * @param[in] rect      矩形区域。坐标系为OFD坐标系。
   * @param[in] color     矩形颜色, ARGB值。
   *
   * @return <b>true</b>代表预遮盖成功, <b>false</b>代表预遮盖失败。
   */
  bool PreMaskCharacterByRect(const RectF& rect, foxit::ARGB color);

  /**
   * @brief 将指定区域填充为指定颜色。
   *
   * @param[in] rect          矩形区域。坐标系为OFD坐标系。
   * @param[in] color         填充颜色, RGB值。
   *
   * @return <b>true</b>代表填充成功, <b>false</b>代表填充失败。
   */
  bool FillRect(const RectF& rect, foxit::RGB color);

  /**
   * @brief 获取页面文本行数。
   *
   * @return 页面文本行数。
   */
  int GetTextLineCount();

  /**
   * @brief 获取指定行的文本内容。
   *
   * @param[in] index     行索引（由0开始编号）。 有效范围：从0到(<i>count</i>-1)。
   *                      <i>count</i> 由函数 @link OFDPage::GetTextLineCount @endlink 返回。
   *
   * @return 指定行的文本内容。
   */
  WString GetTextByLineIndex(int index);

  /**
   * @brief 获取页中标注数量。
   *
   * @return 标注数量。
   */
  int GetAnnotCount();

  /**
   * @brief 按索引获取标注对象。
   *
   * @param[in] index         标注索引。有效范围：从0到(<i>count</i>-1)。
   *                      <i>count</i> 由函数 @link OFDPage::GetAnnotCount @endlink 返回。
   *
   * @return 标注对象。
   */
  Annot GetAnnot(int index);

  /**
   * @brief 按ID获取标注对象。
   *
   * @param[in] id     标注ID。
   *
   * @return 标注对象。
   */
  Annot GetAnnotByID(foxit::uint32 id);

  /**
   * @brief 删除指定索引的标注对象。
   *
   * @param[in] index         标注索引。有效范围：从0到(<i>count</i>-1)。
   *                      <i>count</i> 由函数 @link OFDPage::GetAnnotCount @endlink 返回。
   *
   * @return <b>true</b>代表删除成功, <b>false</b>代表删除失败。
   */
  bool RemoveAnnot(int index);

  /**
   * @brief 新建一个标注对象并添加到页中。
   *
   * @param[in] type        标注类型。
   *                    请参考从 @link foxit::ofd::Annot::AnnotType::e_AnnotTypeUnknown @endlink 开始的值。
   *
   * @return 标注对象。
   */
  Annot AddAnnot(Annot::AnnotType type);

  /**
   * @brief 判断页面是否为双层页面。
   *
   * @return <b>true</b>是双层页面，<b>false</b>不是双层页面。
   */
  bool IsDoubleLayer();

  explicit OFDPage(FS_HANDLE handle);
};

/** 此类表示和OFD模板页的方法。*/
class OFDTemplatePage FS_FINAL : public OFDPage {
 public:
   /** @brief 析构函数。 */
  ~OFDTemplatePage();

  /**
   * @brief 设置模板页的名称。
   *
   * @param[in] name 模板页名称。
   *
   * @return <b>true</b>代表设置成功, <b>false</b>代表设置失败。
   */
  bool SetName(const WString& name);

  explicit OFDTemplatePage(FS_HANDLE handle);
};

}  // namespace ofd
}  // namespace foxit

#endif  // FS_OFD_PAGE_H_
