import {
  createPromiseCapability,
  RenderingCancelledException,
  setLayerDimensions,
  AbortException
} from "pdfjs-dist/legacy/build/pdf";
import {
  DEFAULT_SCALE,
  PromiseExt,
  RendererType,
  RenderingStates,
  TextLayerMode,
  maxCanvasPixels,
} from "./ui_utils.js";
import {
  PixelsPerInch,
} from '../constants'
import { PDFAnnotationLayer } from './pdf_annotation_layer.js'
import ComPDFAnnotationLayer from './annotation/layer.js';
import { ContentContainer } from "./editor/content_container.js";
import TextSelection from "./TextSelection";
import TextSearch from './TextSearch'
import CropPage from './crop_page'

import AnnotationManager from "./annotations";
import { v4 as uuidv4 } from 'uuid';
import { convertBase64ToBytes } from './fileHandler.ts'
/**
 * @typedef {Object} PDFPageViewOptions
 * @property {HTMLDivElement} [container] - The viewer element.
 * @property {EventBus} eventBus - The application event bus.
 * @property {number} id - The page unique ID (normally its number).
 * @property {number} [scale] - The page scale display.
 * @property {PageViewport} defaultViewport - The page viewport.
 * @property {Promise<OptionalContentConfig>} [optionalContentConfigPromise] -
 *   A promise that is resolved with an {@link OptionalContentConfig} instance.
 *   The default value is `null`.
 * @property {number} [textLayerMode] - Controls if the text layer used for
 *   selection and searching is created. The constants from {TextLayerMode}
 *   should be used. The default value is `TextLayerMode.ENABLE`.
 * @property {number} [annotationMode] - Controls if the annotation layer is
 *   created, and if interactive form elements or `AnnotationStorage`-data are
 *   being rendered. The constants from {@link AnnotationMode} should be used;
 *   see also {@link RenderParameters} and {@link GetOperatorListParameters}.
 *   The default value is `AnnotationMode.ENABLE_FORMS`.
 * @property {string} [imageResourcesPath] - Path for image resources, mainly
 *   forannotationLayer icons. Include trailing ./annotation/layer.jsslash.
 * @property {boolean} [useOnlyCssZoom] - Enables CSS only zooming. The default
 *   value is `false`.
 * @property {boolean} [isOffscreenCanvasSupported] - Allows to use an
 *   OffscreenCanvas if needed.
 * @property {number} [maxCanvasPixels] - The maximum supported canvas size in
 *   total pixels, i.e. width * height. Use -1 for no limit. The default value
 *   is 4096 * 4096 (16 mega-pixels).
 * @property {Object} [pageColors] - Overwrites background and foreground colors
 *   with user defined ones in order to improve readability in high contrast
 *   mode.
 * @property {IL10n} [l10n] - Localization service.
 * @property {function} [layerProperties] - The function that is used to lookup
 *   the necessary layer-properties.
 */

const MAX_CANVAS_PIXELS = maxCanvasPixels;

/**
 * @implements {IRenderableView}
 */
class PDFPageView {
  #previousRotation = null;

  #useThumbnailCanvas = {
    initialOptionalContent: true,
    regularAnnotations: true,
  };

  #activeSearch = null

  /**
   * @param {PDFPageViewOptions} options
   */
  constructor(options) {
    this.documentViewer = options.documentViewer
    this._loadingId = null;
    const container = options.container;
    const defaultViewport = options.defaultViewport;

    this.id = options.id;
    this.pageIndex = options.pageIndex
    this.renderingId = "page" + this.id;
    this.layerProperties = options.layerProperties;
    this.doc = options.doc;
    this.messageHandler = options.messageHandler;

    this.pagesPtr = options.pagesPtr
    this.pdfPage = null;
    this.pageLabel = null;
    this.rotation = 0;

    this.annotationStore = options.annotationStore

    this.tool = ''
    this.color = ''
    this.toolMode = ''

    this.enableReply = options.enableReply

    this.scale = options.scale || DEFAULT_SCALE;
    this.viewport = defaultViewport;
    this.pdfPageRotate = defaultViewport.rotation;
    this.hasRestrictedScaling = false;
    this.textLayerMode = options.textLayerMode ?? TextLayerMode.ENABLE;
    this.imageResourcesPath = options.imageResourcesPath || "";
    this.useOnlyCssZoom = options.useOnlyCssZoom || false;
    this.isOffscreenCanvasSupported = true;
    this.maxCanvasPixels = options.maxCanvasPixels || MAX_CANVAS_PIXELS;
    this.pageColors = options.pageColors || null;

    this.eventBus = options.eventBus;
    this._annotations = options.annotations
    this._annotationsAll = options.annotationsAll

    this.pagesPtr = options.pagesPtr
    if (
      typeof PDFJSDev === "undefined" ||
      PDFJSDev.test("!PRODUCTION || GENERIC")
    ) {
      this.renderer = options.renderer || RendererType.CANVAS;
    }
    this.l10n = options.l10n;

    this.paintTask = null;
    this.paintedViewportMap = new WeakMap();
    this._renderingState = RenderingStates.INITIAL
    this.resume = null;
    this._renderError = null;

    this._annotationCanvasMap = null;

    this.annotationLayer = null;
    this.compdfAnnotationLayer = null;
    this.annotationEditorLayer = null;
    this.textSelect = null
    this.zoomLayer = null;
    this.xfaLayer = null;
    this.structTreeLayer = null;
    this.contentContainer = null;
    this.textSelection = null
    this.textSearch = null
    this.searchResults = null
    this.cropPage = null

    const div = document.createElement("div");
    div.className = "page page-number" + this.id;
    div.setAttribute("data-page-number", this.id);
    div.setAttribute("role", "region");
    this.pagePressHandle = this.pagePressHandle.bind(this)
    div.addEventListener("click", this.pagePressHandle)

    this.div = div;

    this.#setDimensions();
    container?.append(div);

    this.selected = options.selected
    this.$t = options.$t

    this.eventBus._on('toolChanged', this.handleTool.bind(this))
    this.eventBus._on('toolModeChanged', this.handleToolMode.bind(this))
    this.eventBus._on('search', this.handleSearch.bind(this))

    this.mode = null
    this.contentEditHistoryManager = options.contentEditHistoryManager

    container?.style.setProperty(
      "--scale-factor",
      this.scale * PixelsPerInch.PDF_TO_CSS_UNITS
    )
  }

  async pagePressHandle(evt) {
    let clickDiv = evt.currentTarget
    let pageNum = this.id
    const pageIndex = this.pageIndex
    if (pageNum) {
      let signImg = document.getElementById("sign-image-save")
      if (signImg) {
        let imgData = signImg.getAttribute("src")

        let divBound = clickDiv.getBoundingClientRect()
        let imgBound = signImg.getBoundingClientRect()

        //当前显示坐标
        let actualLeft = imgBound.x - divBound.x
        let actualTop = imgBound.y - divBound.y
        let actualRight = actualLeft + signImg.width
        let actualBottom = actualTop + signImg.height

        //转到页面坐标
        let left = actualLeft / this.scale
        let top = actualTop / this.scale
        let right = actualRight / this.scale
        let bottom = actualBottom / this.scale

        let annotation = null
        if (signImg.inkPath) {
          let inkPointes = []
          for (let i = 0; i < signImg.inkPath.length; i++) {
            let inkPath = []
            for (let j = 0; j < signImg.inkPath[i].length; j++) {
              inkPath.push({
                PointX: signImg.inkPath[i][j].x / this.scale + left,
                PointY: top + signImg.inkPath[i][j].y / this.scale
              })
            }
            inkPointes.push(inkPath)
          }
          annotation = {
            operate: "add-annot",
            type: "ink",
            pageIndex,
            inkPointes,
            rect: {
              left,
              top,
              right,
              bottom
            },
            targetPage: pageNum,
            borderWidth: signImg.inkParam.width || 1,
            borderColor: signImg.inkParam.color || "#000",
            date: new Date(),
            name: uuidv4()
          }
        } else {
          annotation = {
            operate: "add-annot",
            type: "stamp",
            stampType: 'image',
            pageIndex,
            imageBase64: imgData,
            rect: {
              left,
              top,
              right,
              bottom
            },
            targetPage: pageNum,
            date: new Date(),
            name: uuidv4()
          }
        }
        if (annotation) {
          this.eventBus.dispatch('createSignature', annotation)
        }
        signImg.remove()
        let docDiv = document.querySelector('.content')
        docDiv.onmousemove = null
      }
    }
  }

  handleTool({
    tool,
    color
  }) {
    if (this.cropPage) {
      if (tool === 'cropPage') {
        this.cropPage.init()
      } else {
        this.cropPage.reset()
      }
    }

    if (this.compdfAnnotationLayer) return
    this.tool = tool
    this.color = color
  }

  async handleToolMode(mode) {
    this.mode = mode
    this.toolMode = mode
    if (this.contentContainer) {
      if (mode === 'editor') {
        await this.contentContainer.init()
      } else {
        this.contentContainer.destroy()
        // this.contentContainer = null
      }
    }

    if (this.compdfAnnotationLayer) return
  }

  handleSearch(searchResults) {
    this.searchResults = searchResults
    if (this.textSearch) {
      this.textSearch.searchResults = searchResults
    }
  }

  get annotations() {
    return this._annotations
  }

  set annotations(val) {
    this._annotations = val
    if (this.compdfAnnotationLayer) {
      this.compdfAnnotationLayer.annotations = this.annotations
    }

  }

  get annotationsAll() {
    return this._annotationsAll
  }

  set annotationsAll(val) {
    this._annotationsAll = val
    if (this.compdfAnnotationLayer) {
      this.compdfAnnotationLayer.annotationsAll = this.annotationsAll
    }

  }

  get renderingState() {
    return this._renderingState;
  }

  set renderingState(state) {
    if (state === this._renderingState) {
      return;
    }
    this._renderingState = state;

    if (this._loadingId) {
      clearTimeout(this._loadingId);
      this._loadingId = null;
    }

    switch (state) {
      case RenderingStates.PAUSED:
        this.div.classList.remove("loading");
        break;
      case RenderingStates.RUNNING:
        this.div.classList.add("loadingIcon");
        this._loadingId = setTimeout(() => {
          // Adding the loading class is slightly postponed in order to not have
          // it with loadingIcon.
          // If we don't do that the visibility of the background is changed but
          // the transition isn't triggered.
          this.div.classList.add("loading");
          this._loadingId = null;
        }, 0);
        break;
      case RenderingStates.INITIAL:
      case RenderingStates.FINISHED:
        this.div.classList.remove("loadingIcon", "loading");
        break;
    }
  }

  get selectedText() {
    if (!this.textSelection) return null
    const selection = this.textSelection._selection
    return selection ? selection.textContent : null
  }

  get selectedTextQuads() {
    const textSelection = this.textSelection
    if (!textSelection) return null
    const selection = textSelection._selection
    if (selection) {
      const textQuads = textSelection.rects
      const rect = textSelection.rect
      return {
        textQuads,
        rect,
        pageIndex: this.pageIndex
      }
    }
    return null
  }

  #setDimensions() {
    const { viewport } = this;
    if (this.pdfPage) {
      if (this.#previousRotation === viewport.rotation) {
        return;
      }
      this.#previousRotation = viewport.rotation;
    }

    setLayerDimensions(
      this.div,
      viewport,
      /* mustFlip = */ true,
      /* mustRotate = */ false
    );
  }

  setPdfPage(pdfPage) {
    this.pdfPage = pdfPage;
    this.pdfPageRotate = pdfPage.rotate;

    const totalRotation = (this.rotation + this.pdfPageRotate) % 360;
    this.viewport = pdfPage.getViewport({
      scale: this.scale * PixelsPerInch.PDF_TO_CSS_UNITS,
      rotation: totalRotation,
    });
    this.#setDimensions();
    this.reset();
  }

  destroy() {
    this.reset();
    this.pdfPage?.cleanup();
  }

  clearSearchResults() {
    this.searchResults = null
    if (this.textSearch) {
      this.textSearch.destroy()
    }
  }

  /**
   * @private
   */
  async _renderAnnotationLayer() {
    let error = null;
    try {
      await this.annotationLayer.render(this.viewport, "display");
    } catch (ex) {
      console.error(`_renderAnnotationLayer: "${ex}".`);
      error = ex;
    } finally {
      this.eventBus.dispatch("annotationlayerrendered", {
        source: this,
        pageNumber: this.id,
        error,
      });
    }
  }

  emptyAnnotations() {
    if (this.compdfAnnotationLayer) {
      this.compdfAnnotationLayer.emptyAnnotations()
    }
  }

  /**
   * @private
   */
  _renderAnnotationEditorLayer() {
    this.annotationEditorLayer.render(this.viewport, "display");
  }

  selectAnnotation(annotation) {
    annotation.name && (this.annotationStore.toSelectAnnotationName = annotation.name)
    const compdfAnnotationLayer = this.compdfAnnotationLayer
    if (!compdfAnnotationLayer) return
    const annotationsArray = compdfAnnotationLayer.annotationsArray
    const annotationIndex = annotationsArray.findIndex(item => item.annotation.name === this.annotationStore.toSelectAnnotationName)
    if (compdfAnnotationLayer.selectedElementName) {
      const selectedAnnotationIndex = annotationsArray.findIndex(item => item.annotation.name === compdfAnnotationLayer.selectedElementName)
      selectedAnnotationIndex > -1 && compdfAnnotationLayer.annotationsArray[selectedAnnotationIndex].handleOutside()
    }
    if (annotationIndex > -1) {
      compdfAnnotationLayer.annotationsArray[annotationIndex].selectAnnotation()
      this.annotationStore.toSelectAnnotationName = null
    }
  }

  renderPage() {
    if (this.renderingState !== RenderingStates.INITIAL) {
      console.error("Must be in new state before drawing");
      this.reset(); // Ensure that we reset all state to prevent issues.
    }
    const { div, viewport, pdfPage, annotations, annotationsAll } = this;

    if (!pdfPage) {
      this.renderingState = RenderingStates.FINISHED;
      throw new Error("pdfPage is not loaded");
    }

    this.renderingState = RenderingStates.RUNNING;

    const canvasWrapper = document.createElement("div");
    canvasWrapper.classList.add("canvasWrapper");
    div.append(canvasWrapper)

    const canvas = document.createElement("canvas");
    canvas.setAttribute("role", "presentation");

    canvas.hidden = true;
    let isCanvasHidden = true;
    const showCanvas = function () {
      if (isCanvasHidden) {
        canvas.hidden = false;
        isCanvasHidden = false;
      }
    }

    canvasWrapper.append(canvas);
    this.canvas = canvas

    const radio = window.devicePixelRatio || 1
    this.radio = radio
    const { width, height, scale } = viewport

    const canvasWidth = Math.round(width * radio)
    const canvasHeight = Math.round(height * radio)

    canvas.width = canvasWidth
    canvas.height = canvasHeight
    const { style } = canvas;
    style.width = Math.round(width) + "px"
    style.height = Math.round(height) + "px"

    this.paintedViewportMap.set(canvas, viewport)

    const renderTask = (this.renderTask = this.internalRenderTask({
      width,
      height,
      scale: scale * radio
    }))

    const resultPromise = renderTask.promise.then(
      async () => {
        showCanvas();
        await this.finishRenderTask();

        this.#renderTextLayer();

          if (this.annotationLayer) {
            await this._renderAnnotationLayer();
          }

          if (!this.annotationEditorLayer) {
            const { annotationEditorUIManager, annotationStorage } = this.layerProperties();

            if (!annotationEditorUIManager) {
              return;
            }
            this.annotationEditorLayer = new PDFAnnotationLayer({
              uiManager: annotationEditorUIManager,
              annotationStorage,
              pageIndex: this.pageIndex,
              pageDiv: div,
              viewport: this.viewport,
              scale: this.scale,
              annotations,
              accessibilityManager: this._accessibilityManager,
            });
          }
          this._renderAnnotationEditorLayer();

          if (!this.compdfAnnotationLayer) {
            this.compdfAnnotationLayer = new ComPDFAnnotationLayer({
              documentViewer: this.documentViewer,
              annotationStore: this.annotationStore,
              messageHandler: this.messageHandler,
              pageViewer: this,
              annotations,
              annotationsAll,
              viewport: this.viewport,
              scale: this.scale,
              pageIndex: this.pageIndex,
              pageDiv: div,
              eventBus: this.eventBus,
              selected: this.selected,
              enableReply: this.enableReply,
              $t: this.$t
            })
          }
          this.compdfAnnotationLayer.render(this.viewport)

          // this.AnnotationManager = new AnnotationManager({
          //   annotations,
          //   container: div,
          //   viewport: this.viewport,
          // })

          if (!this.contentContainer) {
            this.contentContainer = new ContentContainer({
              annotationStore: this.annotationStore,
              pageViewer: this,
              annotations,
              annotationsAll,
              viewport: this.viewport,
              scale: this.scale,
              pageIndex: this.pageIndex,
              pageDiv: div,
              eventBus: this.eventBus,
              selected: this.selected,
              pagePtr: this.pagesPtr[this.pageIndex].pagePtr,
              messageHandler: this.messageHandler,
              $t: this.$t,
              contentEditHistoryManager: this.contentEditHistoryManager
            })
            this.mode === 'editor' && this.contentContainer.init()
          } else if (this.mode === 'editor') {
            await this.contentContainer.render()
          }

          if (!this.textSelection) {
            this.textSelection = new TextSelection({
              viewport: this.viewport,
              scale: this.scale,
              pageIndex: this.pageIndex,
              container: div,
              eventBus: this.eventBus,
              selected: this.selected,
              pagePtr: this.pagesPtr[this.pageIndex],
              messageHandler: this.messageHandler,
              tool: this.tool,
              toolMode: this.toolMode,
              color: this.color,
              pageViewer: this
            })
          }

          if (!this.textSearch) {
            this.textSearch = new TextSearch({
              viewport: this.viewport,
              scale: this.scale,
              pageIndex: this.pageIndex,
              container: div,
              results: this.searchResults,
            })
            if (this.#activeSearch) {
              this.textSearch.setActiveSearchResult(this.#activeSearch)
            }
          }

          if (this.cropPage?.cropping) {
            this.cropPage.draw()
          }
          if (!this.cropPage) {
            this.cropPage = new CropPage({
              viewport: this.viewport,
              scale: this.scale,
              pageIndex: this.pageIndex,
              container: div,
              eventBus: this.eventBus,
              selected: this.selected,
              pagePtr: this.pagesPtr[this.pageIndex],
              messageHandler: this.messageHandler,
              tool: this.tool,
              toolMode: this.toolMode,
              color: this.color,
              pageViewer: this,
              annotationStore: this.annotationStore
            })
          } else {
            this.cropPage.updateInitialProps()
          }
          if (this.tool === 'cropPage') {
            this.cropPage.init()
          }
      },
      error => {
        console.log(error)
        return this.finishRenderTask(error);
      }
    )

    div.setAttribute("data-loaded", true);

    this.eventBus.dispatch("pagerender", {
      source: this,
      pageNumber: this.id,
    });
    return resultPromise
  }

  internalRenderTask({
    width,
    height,
    scale
  }) {
    let renderTask = this.renderTask

    if (renderTask) {
      renderTask.cancel()
    }

    renderTask = this.render({
      width,
      height,
      scale
    })

    return renderTask
  }

  render({
    width,
    height,
    scale
  }) {
    const renderCapability = new PromiseExt()
    renderCapability.cancel = function () {
      renderCapability.reject('Cancel')
    }
    const { canvas, pagesPtr, pageIndex } = this

    this.messageHandler.sendWithPromise('PushRenderTask', {
      pagePtr: pagesPtr[pageIndex].pagePtr,
      left: 0,
      top: 0,
      right: Math.round(width),
      bottom: Math.round(height),
      scale
    })
      .then((result) => {
        const ctx = canvas.getContext("2d", { alpha: false });
        if (result && result.code) {
          console.log(result.message)
          alert(result.message)
          return
        }

        const { imageArray } = result
        ctx.clearRect(0, 0, width, height)
        let imageData = ctx.createImageData(width, height);
        imageData.data.set(imageArray)
        ctx.putImageData(imageData, 0, 0)
        this.baseImageData = imageData

        renderCapability.resolve()
      })
      .catch((err) => {
        console.log(err)
      })
    return renderCapability
  }

  finishRenderTask (error = null) {
    this.renderTask = null

    this._renderError = error;

    this.renderingState = RenderingStates.FINISHED;
    this._resetZoomLayer(/* removeFromDOM = */ true);

    this.eventBus.dispatch("pagerendered", {
      source: this,
      pageNumber: this.id,
      cssTransform: false,
      timestamp: performance.now(),
      error: this._renderError,
    });

    if (error) {
      throw error;
    }
  }

  async #renderTextLayer() {
    const { pdfPage, textLayer, viewport } = this;
    if (!textLayer) {
      return;
    }

    let error = null;
    try {
      if (!textLayer.renderingDone) {
        const readableStream = pdfPage.streamTextContent({
          includeMarkedContent: true,
        });
        textLayer.setTextContentSource(readableStream);
      }
      await textLayer.render(viewport);
    } catch (ex) {
      if (ex instanceof AbortException) {
        return;
      }
      console.error(`#renderTextLayer: "${ex}".`);
      error = ex;
    }

    this.eventBus.dispatch("textlayerrendered", {
      source: this,
      pageNumber: this.id,
      numTextDivs: textLayer.numTextDivs,
      error,
    });
  }

  /**
   * @private
   */
  _resetZoomLayer(removeFromDOM = false) {
    if (!this.zoomLayer) {
      return;
    }
    const zoomLayerCanvas = this.zoomLayer.firstChild;
    this.paintedViewportMap.delete(zoomLayerCanvas);
    // Zeroing the width and height causes Firefox to release graphics
    // resources immediately, which can greatly reduce memory consumption.
    zoomLayerCanvas.width = 0;
    zoomLayerCanvas.height = 0;

    if (removeFromDOM) {
      // Note: `ChildNode.remove` doesn't throw if the parent node is undefined.
      this.zoomLayer.remove();
    }
    this.zoomLayer = null;
  }

  reset({
    keepZoomLayer = false,
    keepAnnotationLayer = false,
    keepAnnotationEditorLayer = false,
    keepXfaLayer = false,
    keepTextLayer = false,
  } = {}) {
    this.cancelRendering({
      keepAnnotationLayer,
      keepAnnotationEditorLayer,
      keepXfaLayer,
      keepTextLayer,
    });
    this.renderingState = RenderingStates.INITIAL;

    const div = this.div;
    const childNodes = div.childNodes,
      zoomLayerNode = (keepZoomLayer && this.zoomLayer) || null,
      annotationLayerNode =
        (keepAnnotationLayer && this.annotationLayer?.div) || null,
      annotationEditorLayerNode =
        (keepAnnotationEditorLayer && this.annotationEditorLayer?.div) || null,
      textLayerNode = (keepTextLayer && this.textLayer?.div) || null

    for (let i = childNodes.length - 1; i >= 0; i--) {
      const node = childNodes[i];
      switch (node) {
        case zoomLayerNode:
        case textLayerNode:
          continue;
        case annotationLayerNode:
        case annotationEditorLayerNode:
          continue;
      }
      node.remove();
    }
    div.removeAttribute("data-loaded");

    if (annotationLayerNode) {
      // Hide the annotation layer until all elements are resized
      // so they are not displayed on the already resized page.
      this.annotationLayer.hide();
    }

    if (annotationEditorLayerNode) {
      this.annotationEditorLayer.hide();
    } else {
      this.annotationEditorLayer?.destroy();
    }
    if (textLayerNode) {
      this.textLayer.hide();
    }
    this.structTreeLayer?.hide();

    if (!zoomLayerNode) {
      if (this.canvas) {
        this.paintedViewportMap.delete(this.canvas);
        // Zeroing the width and height causes Firefox to release graphics
        // resources immediately, which can greatly reduce memory consumption.
        this.canvas.width = 0;
        this.canvas.height = 0;
        delete this.canvas;
      }
      this._resetZoomLayer();
    }
  }

  update({
    scale = 0,
    rotation = null,
    drawingDelay = -1,
  }) {
    this.scale = scale || this.scale;
    if (typeof rotation === "number") {
      this.rotation = rotation; // The rotation may be zero.
    }

    const totalRotation = (this.rotation + this.pdfPageRotate) % 360;
    this.viewport = this.viewport.clone({
      scale: this.scale * PixelsPerInch.PDF_TO_CSS_UNITS,
      rotation: totalRotation,
    });
    this.#setDimensions();

    this.div.parentNode?.style.setProperty(
      "--scale-factor",
      this.viewport.scale
    )
    const postponeDrawing = drawingDelay >= 0 && drawingDelay < 1000;

    if (this.canvas) {
      if (
        postponeDrawing ||
        this.useOnlyCssZoom ||
        (this.hasRestrictedScaling && isScalingRestricted)
      ) {
        if (
          postponeDrawing &&
          this.renderingState !== RenderingStates.FINISHED
        ) {
          this.cancelRendering({
            keepZoomLayer: true,
            keepAnnotationLayer: true,
            keepAnnotationEditorLayer: true,
            keepXfaLayer: true,
            keepTextLayer: true,
            cancelExtraDelay: drawingDelay,
          });
          // It isn't really finished, but once we have finished
          // to postpone, we'll call this.reset(...) which will set
          // the rendering state to INITIAL, hence the next call to
          // PDFViewer.update() will trigger a redraw (if it's mandatory).
          this.renderingState = RenderingStates.FINISHED;
        }

        this.cssTransform({
          target: this.canvas,
          redrawAnnotationLayer: true,
          redrawAnnotationEditorLayer: true,
          redrawXfaLayer: true,
          redrawTextLayer: !postponeDrawing,
          hideTextLayer: postponeDrawing,
        });

        this.eventBus.dispatch("pagerendered", {
          source: this,
          pageNumber: this.id,
          cssTransform: true,
          timestamp: performance.now(),
          error: this._renderError,
        });

        this.reset({
          keepZoomLayer: false,
          keepAnnotationLayer: true,
          keepAnnotationEditorLayer: true,
          keepXfaLayer: true,
          keepTextLayer: true,
        });
        return;
      }
      if (!this.zoomLayer && !this.canvas.hidden) {
        this.zoomLayer = this.canvas.parentNode;
        this.zoomLayer.style.position = "absolute";
      }
    }
    if (this.zoomLayer) {
      this.cssTransform({ target: this.zoomLayer.firstChild });
    }
    this.reset({
      keepZoomLayer: true,
      keepAnnotationLayer: true,
      keepAnnotationEditorLayer: true,
      keepXfaLayer: true,
      keepTextLayer: true,
    });
  }

  /**
   * PLEASE NOTE: Most likely you want to use the `this.reset()` method,
   *              rather than calling this one directly.
   */
  cancelRendering({
    keepAnnotationLayer = false,
    keepAnnotationEditorLayer = false,
    cancelExtraDelay = 0,
  } = {}) {
    if (this.paintTask) {
      this.paintTask.cancel(cancelExtraDelay)
      this.paintTask = null;
    }
    this.resume = null;

    if (this.structTreeLayer && !this.textLayer) {
      this.structTreeLayer = null;
    }
    if (
      this.annotationLayer &&
      (!keepAnnotationLayer || !this.annotationLayer.div)
    ) {
      this.annotationLayer.cancel();
      this.annotationLayer = null;
      this._annotationCanvasMap = null;
    }
    if (this.compdfAnnotationLayer) {
      this.compdfAnnotationLayer.cancel();
      this.compdfAnnotationLayer = null;
    }
    if (
      this.annotationEditorLayer &&
      (!keepAnnotationEditorLayer || !this.annotationEditorLayer.div)
    ) {
      this.annotationEditorLayer.cancel();
      this.annotationEditorLayer = null;
    }
    if (this.contentContainer) {
      this.contentContainer.cancel()
    }

    if (this.textSelection) {
      this.textSelection.destroy()
      this.textSelection = null
    }

    if (this.textSearch) {
      this.textSearch.destroy()
      this.textSearch = null
    }
  }

  cssTransform({
    target,
    redrawAnnotationLayer = false,
    redrawAnnotationEditorLayer = false,
    redrawXfaLayer = false,
    redrawTextLayer = false,
    hideTextLayer = false,
  }) {
    // Scale target (canvas or svg), its wrapper and page container.

    if (target instanceof HTMLCanvasElement) {
      if (!target.hasAttribute("zooming")) {
        target.setAttribute("zooming", true);
        const { style } = target;
        style.width = style.height = "";
      }
    } else {
      const div = this.div;
      const { width, height } = this.viewport;

      target.style.width =
        target.parentNode.style.width =
        div.style.width =
        Math.floor(width) + "px";
      target.style.height =
        target.parentNode.style.height =
        div.style.height =
        Math.floor(height) + "px";
    }

    const originalViewport = this.paintedViewportMap.get(target);
    if (this.viewport !== originalViewport) {
      // The canvas may have been originally rotated; rotate relative to that.
      const relativeRotation =
        this.viewport.rotation - originalViewport.rotation;
      const absRotation = Math.abs(relativeRotation);
      let scaleX = 1,
        scaleY = 1;
      if (absRotation === 90 || absRotation === 270) {
        const { width, height } = this.viewport;
        // Scale x and y because of the rotation.
        scaleX = height / width;
        scaleY = width / height;
      }

      if (absRotation !== 0) {
        target.style.transform = `rotate(${relativeRotation}deg) scale(${scaleX}, ${scaleY})`;
      }
    }

    if (redrawAnnotationLayer && this.annotationLayer) {
      this._renderAnnotationLayer();
    }

    if (redrawAnnotationEditorLayer && this.annotationEditorLayer) {
      this._renderAnnotationEditorLayer();
    }

    if (this.textLayer) {
      if (hideTextLayer) {
        this.textLayer.hide();
        this.structTreeLayer?.hide();
      } else if (redrawTextLayer) {
        this.#renderTextLayer();
      }
    }
  }

  get width() {
    return this.viewport.width;
  }

  get height() {
    return this.viewport.height;
  }

  getPagePoint(x, y) {
    return this.viewport.convertToPdfPoint(x, y);
  }

  async addTextEditor(data) {
    if (!this.contentContainer) {
      const { div, annotationStore, annotations, annotationsAll } = this
      this.contentContainer = new ContentContainer({
        annotationStore: annotationStore,
        pageViewer: this,
        annotations,
        annotationsAll,
        viewport: this.viewport,
        scale: this.scale,
        pageIndex: this.pageIndex,
        pageDiv: div,
        eventBus: this.eventBus,
        selected: this.selected,
        pagePtr: this.pagesPtr[this.pageIndex].pagePtr,
        messageHandler: this.messageHandler,
        $t: this.$t,
        contentEditHistoryManager: this.contentEditHistoryManager
      })
      await this.contentContainer.init()
    }

    this.contentContainer.addTextEditor(data)
  }

  async getEditAnnotation() {
    if (!this.contentContainer) {
      const { div, annotationStore, annotations, annotationsAll } = this
      this.contentContainer = new ContentContainer({
        annotationStore: annotationStore,
        pageViewer: this,
        annotations,
        annotationsAll,
        viewport: this.viewport,
        scale: this.scale,
        pageIndex: this.pageIndex,
        pageDiv: div,
        eventBus: this.eventBus,
        selected: this.selected,
        pagePtr: this.pagesPtr[this.pageIndex].pagePtr,
        messageHandler: this.messageHandler,
        $t: this.$t,
        contentEditHistoryManager: this.contentEditHistoryManager
      })
      await this.contentContainer.init()
    }

    return this.contentContainer.frameEditorList
  }

  draw() {
    if (this.renderingState !== RenderingStates.INITIAL) {
      console.error("Must be in new state before drawing");
      this.reset(); // Ensure that we reset all state to prevent issues.
    }
    const { div, pdfPage, annotations, annotationsAll } = this;

    if (!pdfPage) {
      this.renderingState = RenderingStates.FINISHED;
      return Promise.reject(new Error("pdfPage is not loaded"));
    }

    this.renderingState = RenderingStates.RUNNING;

    // Wrap the canvas so that if it has a CSS transform for high DPI the
    // overflow will be hidden in Firefox.
    const canvasWrapper = document.createElement("div");
    canvasWrapper.classList.add("canvasWrapper");
    div.append(canvasWrapper);

    let renderContinueCallback = null;

    const finishPaintTask = async (error = null) => {
      // The paintTask may have been replaced by a new one, so only remove
      // the reference to the paintTask if it matches the one that is
      // triggering this callback.
      if (paintTask === this.paintTask) {
        this.paintTask = null;
      }

      if (error instanceof RenderingCancelledException) {
        this._renderError = null;
        return;
      }
      this._renderError = error;

      this.renderingState = RenderingStates.FINISHED;
      this._resetZoomLayer(/* removeFromDOM = */ true);

      // Ensure that the thumbnails won't become partially (or fully) blank,
      // for documents that contain interactive form elements.
      this.#useThumbnailCanvas.regularAnnotations = !paintTask.separateAnnots;

      this.eventBus.dispatch("pagerendered", {
        source: this,
        pageNumber: this.id,
        cssTransform: false,
        timestamp: performance.now(),
        error: this._renderError,
      });

      if (error) {
        throw error;
      }
    };

    const paintTask = this.paintOnCanvas(canvasWrapper)
    paintTask.onRenderContinue = renderContinueCallback;
    this.paintTask = paintTask;

    const resultPromise = paintTask.promise.then(
      () => {
        return finishPaintTask(null).then(async () => {
          this.#renderTextLayer();

          if (this.annotationLayer) {
            await this._renderAnnotationLayer();
          }

          if (!this.annotationEditorLayer) {
            const { annotationEditorUIManager, annotationStorage } = this.layerProperties();

            if (!annotationEditorUIManager) {
              return;
            }
            this.annotationEditorLayer = new PDFAnnotationLayer({
              uiManager: annotationEditorUIManager,
              annotationStorage,
              pageIndex: this.pageIndex,
              pageDiv: div,
              viewport: this.viewport,
              scale: this.scale,
              annotations,
              accessibilityManager: this._accessibilityManager,
            });
          }
          this._renderAnnotationEditorLayer();

          if (!this.compdfAnnotationLayer) {
            this.compdfAnnotationLayer = new ComPDFAnnotationLayer({
              documentViewer: this.documentViewer,
              annotationStore: this.annotationStore,
              messageHandler: this.messageHandler,
              pageViewer: this,
              annotations,
              annotationsAll,
              viewport: this.viewport,
              scale: this.scale,
              pageIndex: this.pageIndex,
              pageDiv: div,
              eventBus: this.eventBus,
              selected: this.selected,
              enableReply: this.enableReply,
              $t: this.$t
            })
          } else {
            this.compdfAnnotationLayer.destroy()
          }
          this.compdfAnnotationLayer.render(this.viewport)

          // this.AnnotationManager = new AnnotationManager({
          //   annotations,
          //   container: div,
          //   viewport: this.viewport,
          // })

          if (!this.contentContainer) {
            this.contentContainer = new ContentContainer({
              annotationStore: this.annotationStore,
              pageViewer: this,
              annotations,
              annotationsAll,
              viewport: this.viewport,
              scale: this.scale,
              pageIndex: this.pageIndex,
              pageDiv: div,
              eventBus: this.eventBus,
              selected: this.selected,
              pagePtr: this.pagesPtr[this.pageIndex].pagePtr,
              messageHandler: this.messageHandler,
              $t: this.$t,
              contentEditHistoryManager: this.contentEditHistoryManager
            })
            this.mode === 'editor' && this.contentContainer.init()
          } else if (this.mode === 'editor') {
            await this.contentContainer.render()
          }
          if (this.contentContainer && this.contentContainer.pagePtr !== this.pagesPtr[this.pageIndex].pagePtr) {
            this.contentContainer.pagePtr = this.pagesPtr[this.pageIndex].pagePtr
            await this.contentContainer.update()
          }

          if (!this.textSelection) {
            this.textSelection = new TextSelection({
              viewport: this.viewport,
              scale: this.scale,
              pageIndex: this.pageIndex,
              container: div,
              eventBus: this.eventBus,
              selected: this.selected,
              pagePtr: this.pagesPtr[this.pageIndex],
              messageHandler: this.messageHandler,
              tool: this.tool,
              toolMode: this.toolMode,
              color: this.color,
              pageViewer: this
            })
          }

          if (!this.textSearch) {
            this.textSearch = new TextSearch({
              viewport: this.viewport,
              scale: this.scale,
              pageIndex: this.pageIndex,
              container: div,
              results: this.searchResults,
            })
            if (this.#activeSearch) {
              this.textSearch.setActiveSearchResult(this.#activeSearch)
            }
          }

          if (this.cropPage?.cropping) {
            this.cropPage.draw()
          }
          if (!this.cropPage) {
            this.cropPage = new CropPage({
              viewport: this.viewport,
              scale: this.scale,
              pageIndex: this.pageIndex,
              container: div,
              eventBus: this.eventBus,
              selected: this.selected,
              pagePtr: this.pagesPtr[this.pageIndex],
              messageHandler: this.messageHandler,
              tool: this.tool,
              toolMode: this.toolMode,
              color: this.color,
              pageViewer: this,
              annotationStore: this.annotationStore
            })
          } else {
            this.cropPage.updateInitialProps()
          }
          if (this.tool === 'cropPage') {
            this.cropPage.init()
          }
        });
      },
      function (reason) {
        return finishPaintTask(reason);
      }
    )

    div.setAttribute("data-loaded", true);

    return resultPromise;
  }

  paintOnCanvas(canvasWrapper) {
    const renderCapability = createPromiseCapability();
    const result = {
      promise: renderCapability.promise,
      onRenderContinue(cont) {
        cont();
      },
      cancel(extraDelay = 0) {
        renderTask.cancel(extraDelay);
      },
      get separateAnnots() {
        return renderTask.separateAnnots;
      },
    };

    const canvas = document.createElement("canvas");
    canvas.setAttribute("role", "presentation");

    // Keep the canvas hidden until the first draw callback, or until drawing
    // is complete when `!this.renderingQueue`, to prevent black flickering.
    canvas.hidden = true;
    let isCanvasHidden = true;
    const showCanvas = function () {
      if (isCanvasHidden) {
        canvas.hidden = false;
        isCanvasHidden = false;
      }
    };

    canvasWrapper.append(canvas);
    this.canvas = canvas;

    const ctx = canvas.getContext("2d", { alpha: false });

    const radio = window.devicePixelRatio || 1
    this.radio = radio

    const viewport = this.viewport
    const { width, height, scale } = viewport

    const canvasWidth = Math.round(width * radio)
    const canvasHeight = Math.round(height * radio)

    canvas.width = canvasWidth
    canvas.height = canvasHeight
    const { style } = canvas;
    style.width = Math.round(width) + "px"
    style.height = Math.round(height) + "px"

    // Add the viewport so it's known what it was originally drawn with.
    this.paintedViewportMap.set(canvas, viewport);

    const renderTask = new PromiseExt()

    renderTask.cancel = function() {
    }

    renderTask.resolve()
    renderTask.promise.then(
      async () => {
        const result = await this.messageHandler.sendWithPromise('PushRenderTask', {
          pagePtr: this.pagesPtr[this.pageIndex].pagePtr,
          left: 0,
          top: 0,
          right: Math.round(canvasWidth),
          bottom: Math.round(canvasHeight),
          scale: scale * radio
        })
        if (result && result.code) {
          console.log(result.message)
          alert(result.message)
          return
        }

        const { imageArray } = result
        ctx.clearRect(0, 0, canvasWidth, canvasHeight)
        let imageData = ctx.createImageData(canvasWidth, canvasHeight);
        imageData.data.set(imageArray)
        ctx.putImageData(imageData, 0, 0)
        this.baseImageData = imageData

        showCanvas();
        renderCapability.resolve();
      },
      function (error) {
        console.log(error)
        // When zooming with a `drawingDelay` set, avoid temporarily showing
        // a black canvas if rendering was cancelled before the `onContinue`-
        // callback had been invoked at least once.
        if (!(error instanceof RenderingCancelledException)) {
          showCanvas();
        }
        renderCapability.reject(error);
      }
    );
    return result;
  }

  setActiveSearchResult(search) {
    this.#activeSearch = search
    if (this.textSearch) {
      this.textSearch.setActiveSearchResult(search)
    }
  }

  clearActiveSearchResult(searchResult) {
    this.#activeSearch = null
    if (this.textSearch) {
      this.textSearch.clearActiveSearchResult(searchResult)
    }
  }

  /**
   * @param {string|null} label
   */
  setPageLabel(label) {
    this.pageLabel = typeof label === "string" ? label : null;

    if (this.pageLabel !== null) {
      this.div.setAttribute("data-page-label", this.pageLabel);
    } else {
      this.div.removeAttribute("data-page-label");
    }
  }

  /**
   * For use by the `PDFThumbnailView.setImage`-method.
   * @ignore
   */
  get thumbnailCanvas() {
    return this.canvas
  }

  get fontFile() {
    return this._fontFile;
  }

  set fontFile(file) {
    this._fontFile = file;
  }
}

export { PDFPageView };
