import { getActualPoint, getClickPoint, createSvg, createElement } from '../annotation/utils';
import { onClickOutsideUp, setCss, isMobileDevice } from '../ui_utils';
import copy from 'copy-to-clipboard';

export class TextEditor {
  #editList = []
  constructor({
    eventBus,
    contentContainer,
    container,
    pagePtr,
    editPagePtr,
    editAreaPtr,
    editAreaIndex,
    viewport,
    scale,
    messageHandler,
    newAdd,
    pageViewer,
    hidden,
    isUpdate = false
  }) {
    this.eventBus = eventBus
    this.contentContainer = contentContainer
    this.container = container
    this.pagePtr = pagePtr
    this.editPagePtr = editPagePtr
    this.editAreaPtr = editAreaPtr
    this.editAreaIndex = editAreaIndex
    this.viewport = viewport
    this.scale = scale
    this.messageHandler = messageHandler
    this.newAdd = newAdd
    this.pageViewer = pageViewer
    this.hidden = hidden
    this.isUpdate = isUpdate

    this.type = 'text'
    this.frame = null
    this.canvas = null
    this.borderWidth = 2
    this.pointWidth = 6
    this.pointHeight = 6
    this.activeCharPlace = {
      SectionIndex: 0,
      LineIndex: 0,
      RunIndex: 0,
      CharIndex: 0
    }
    this.selectedRects = null
    this.selectedCharRange = null
    this.entireCharRange = null
    this.state = 0 // 0 未选中；1 已选中；2 编辑状态
    this.mouseDown = false
    this.moving = false
    this.ratio = window.devicePixelRatio || 1
    this.removed = false

    this.start = null
    this.end = null
    this.newStart = null
    this.newEnd = null
    this.offsetX = 0
    this.offsetY = 0
    this.startPoint = null
    this.endPoint = null
    this.composing = false // 组合输入状态
    this.needUndoRec = false // 执行操作后，需要添加撤销记录

    this.isFirefox = navigator.userAgent.indexOf('Firefox') > -1
    this.mousedown = isMobileDevice ? 'touchstart' : 'mousedown'
    this.mouseup = isMobileDevice ? 'touchend' : 'mouseup'
    this.mousemove = isMobileDevice ? 'touchmove' : 'mousemove'
    this.textInput = isMobileDevice || this.isFirefox ? 'input' : 'textInput'

    this.onClick = this.handleClick.bind(this)
    this.onMousedown = this.handleMouseDown.bind(this)
    this.onMouseup = this.handleMouseUp.bind(this)
    this.onMousemove = this.handleMouseMove.bind(this)
    this.onBlur = this.handleBlur.bind(this)
    this.onKeydown = this.handleKeyDown.bind(this)
    this.onTextInput = this.handleTextInput.bind(this)
    this.onHandlePropertyPanelChanged = this.handlePropertyPanelChanged.bind(this)
    this.onCompositionstart = this.handleCompositionstart.bind(this)
    this.onCompositionend = this.handleCompositionend.bind(this)
    this.onShowContentEditorType = this.handleShow.bind(this)
    this.onHandlePopup = this.handlePopup.bind(this)

    this.render()
  }

  async render () {
    this.eventBus._on('textPropertyChanged', this.onHandlePropertyPanelChanged)
    this.eventBus._on('showContentEditorType', this.onShowContentEditorType)
    this.eventBus._on('contentEditorPopupClicked', this.onHandlePopup)

    await this.getRect()
    this.canvas = document.createElement('canvas')

    let frameContainer = createElement(
      'div',
      {
        position: 'absolute',
        left: this.rect.left + 'px',
        top: this.rect.top + 'px',
        width: this.rect.width + 'px',
        height: this.rect.height + 'px',
        display: this.hidden ? 'none' : 'block'
      },
      {
        class: 'frame-container',
        index: this.editAreaIndex
      }
    )
    this.frameContainer = frameContainer
    this.container?.append(this.frameContainer)
    this.frameContainer.appendChild(this.canvas)

    let textContainer = createElement(
      'div',
      {
        position: 'absolute',
        left: 0,
        top: 0,
        width: '100%',
        height: '100%',
        borderWidth: this.borderWidth + 'px',
        borderColor: '#1460F3'
      },
      {
        class: 'text-container',
      }
    )
    this.textContainer = textContainer

    let textarea = createElement(
      'textarea',
      {
        position: 'absolute',
        left: 0,
        top: 0,
        width: '100%',
        height: '100%',
        resize: 'none',
        opacity: 0,
        touchAction: 'none'
      }
    )
    textarea.name = 'text-editor'
    this.textContainer.append(textarea)
    this.textarea = textarea
    let text = await this.getText()
    this.textarea.value = text

    this.frameContainer?.append(this.textContainer)

    // this.textContainer.addEventListener('click', this.onClick)
    this.textContainer.addEventListener(this.mousedown, this.onMousedown)
    this.textContainer.addEventListener(this.mouseup, this.onMouseup)
    isMobileDevice && this.textarea.addEventListener('contextmenu', (event) => event.preventDefault())

    this.outerLineContainer = document.createElement('div')
    this.outerLineContainer.className = 'outline-container'
    this.outerLineContainer.style.position = 'absolute'

    const outerLine = createSvg('svg', {
      class: 'outerline'
    }, {
      position: 'absolute',
      left: this.rect.left - this.borderWidth + 'px',
      top: this.rect.top - this.borderWidth + 'px',
      width: this.rect.width + this.borderWidth + this.pointWidth / 2 + 'px',
      height: this.rect.height + this.borderWidth + this.pointHeight / 2 + 'px',
      zIndex: 2
    })

    this.outerLine = outerLine

    this.moveRect = createSvg(
      "rect",
      {
        class: "move",
        'data-id': "move",
        stroke: "#1460F3",
        'stroke-width': this.borderWidth,
        'fill-opacity': 0,
        width: this.rect.width - this.borderWidth,
        height: this.rect.height - this.borderWidth,
        x: this.pointWidth / 2,
        y: this.pointHeight / 2
      }
    );

    this.topLeftRect = createSvg(
      "rect",
      {
        class: "nw-resize",
        'data-id': "nw-resize",
        fill: "#1460F3",
        stroke: "none",
        width: this.pointWidth,
        height: this.pointHeight,
        x: 0,
        y: 0
      }
    );

    this.LeftRect = createSvg(
      "rect",
      {
        class: "w-resize",
        'data-id': "w-resize",
        fill: "#1460F3",
        stroke: "none",
        width: this.pointWidth,
        height: this.pointHeight,
        x: 0,
        y: (this.rect.height - this.pointHeight + this.borderWidth) / 2
      }
    );

    this.bottomLeftRect = createSvg(
      "rect",
      {
        class: "sw-resize",
        'data-id': "sw-resize",
        fill: "#1460F3",
        stroke: "none",
        width: this.pointWidth,
        height: this.pointHeight,
        x: 0,
        y: this.rect.height - this.pointHeight / 2 + this.borderWidth / 2
      }
    );

    this.bottomRect = createSvg(
      "rect",
      {
        class: "s-resize",
        'data-id': "s-resize",
        fill: "#1460F3",
        stroke: "none",
        width: this.pointWidth,
        height: this.pointHeight,
        x: (this.rect.width - this.pointWidth + this.borderWidth) / 2,
        y: this.rect.height - this.pointHeight / 2 + this.borderWidth / 2
      }
    );
    
    this.bottomRightRect = createSvg(
      "rect",
      {
        class: "se-resize",
        'data-id': "se-resize",
        fill: "#1460F3",
        stroke: "none",
        width: this.pointWidth,
        height: this.pointHeight,
        x: this.rect.width - this.pointWidth / 2 + this.borderWidth / 2,
        y: this.rect.height - this.pointHeight / 2 + this.borderWidth / 2
      }
    );

    this.rightRect = createSvg(
      "rect",
      {
        class: "e-resize",
        'data-id': "e-resize",
        fill: "#1460F3",
        stroke: "none",
        width: this.pointWidth,
        height: this.pointHeight,
        x: this.rect.width - this.pointWidth / 2 + this.borderWidth / 2,
        y: (this.rect.height - this.pointHeight + this.borderWidth) / 2
      }
    );

    this.topRightRect = createSvg(
      "rect",
      {
        class: "ne-resize",
        'data-id': "ne-resize",
        fill: "#1460F3",
        stroke: "none",
        width: this.pointWidth,
        height: this.pointHeight,
        x: this.rect.width - this.pointWidth / 2 + this.borderWidth / 2,
        y: 0
      }
    );

    this.topRect = createSvg(
      "rect",
      {
        class: "n-resize",
        'data-id': "n-resize",
        fill: "#1460F3",
        stroke: "none",
        width: this.pointWidth,
        height: this.pointHeight,
        x: (this.rect.width - this.pointWidth + this.borderWidth) / 2,
        y: 0
      }
    );

    this.outerLine.append(this.moveRect)
    this.outerLine.append(this.topLeftRect)
    this.outerLine.append(this.LeftRect)
    this.outerLine.append(this.bottomLeftRect)
    this.outerLine.append(this.bottomRect)
    this.outerLine.append(this.bottomRightRect)
    this.outerLine.append(this.rightRect)
    this.outerLine.append(this.topRightRect)
    this.outerLine.append(this.topRect)
    this.outerLineContainer.append(this.outerLine)

    this.outerLine.addEventListener(this.mousedown, this.onMousedown)
    this.outerLine.addEventListener(this.mouseup, this.onMouseup)

    if (this.newAdd) {
      this.goEditing()
      this.addUndoHistory()
      this.newAdd = false
    }
    
    this.isUpdate && await this.updateCanvas()
  }

  getActualRect (viewport, s, frame) {
    const { left: x1, top: y1, right: x2, bottom: y2 } = frame
    
    const start = getActualPoint(
      {
        x: x1,
        y: y1
      },
      viewport,
      s
    )
    const end = getActualPoint(
      {
        x: x2,
        y: y2
      },
      viewport,
      s
    )
    return {
      start,
      end
    }
  }

  rectCalc (start, end, padding = 5) {
    return {
      top: (start.y < end.y ? start.y : end.y) - padding,
      left: (start.x < end.x ? start.x : end.x) - padding,
      width: Math.abs(end.x - start.x) + padding * 2,
      height: Math.abs(end.y - start.y) + padding * 2,
    }
  }
  
  getInitialPoint (start, end) {
    const s = this.scale
    const startX = start.x
    const startY = start.y
    const endX = end.x
    const endY = end.y

    const { width, height, rotation } = this.viewport;
    let x1, y1, x2, y2;
    if (rotation === 0) {
      x1 = startX / s;
      y1 = startY / s; 
      x2 = endX / s;
      y2 = endY / s;
    } else if (rotation === 90) {
      x1 = startY / s;
      y1 = startX / s;
      x2 = endY / s;
      y2 = endX / s;
    } else if (rotation === 180) {
      x1 =( width - startX) / s;
      y1 = startY / s;
      x2 = (width - endX) / s;
      y2 = endY / s;
    } else {
      x1 = (height - startY) / s
      y1 = (width - startX) / s
      x2 = (height - endY) / s
      y2 = (width - endX)  / s
    }
    return {
      start: {
        x: x1,
        y: y1,
      },
      end: {
        x: x2,
        y: y2,
      },
    };
  }

  handleMouseDown (e) {
    isMobileDevice && e.preventDefault()
    if (e.button === 2 || this.hidden) return // 右键点击不执行

    if (this.state === 1) {
      const operatorId = e.target.getAttribute('data-id')
    
      const { pageX, pageY } = getClickPoint(e)
      this.startState = {
        operator: operatorId,
        clickX: pageX,
        clickY: pageY,
      }

      document.addEventListener(this.mousemove, this.onMousemove)
      
    } else if (this.state === 2) {
      if (isMobileDevice) {
        const offsetX = e.changedTouches[0].clientX - this.pageViewer.div.getBoundingClientRect().left
        const offsetY = e.changedTouches[0].clientY - this.pageViewer.div.getBoundingClientRect().top

        this.startPoint = {
          x: offsetX / this.scale,
          y: offsetY / this.scale
        }
      } else {
        const offsetX = e.offsetX
        const offsetY = e.offsetY

        this.startPoint = {
          x: (offsetX + this.rect.left) / this.scale,
          y: (offsetY + this.rect.top) / this.scale
        }
      }

      this.textContainer.addEventListener(this.mousemove, this.onMousemove)
    }

    document.addEventListener(this.mouseup, this.onMouseup)

    this.selectedCharRange = null
    this.mouseMoved = false
    this.mouseDown = true
  }

  async handleMouseUp (e) {
    if (e && !this.textContainer.contains(e.target) && this.state === 0) return
    if (this.contentContainer.selectedFrameIndex !== -1 && this.contentContainer.selectedFrameIndex !== this.editAreaIndex && this.state === 0) return

    document.removeEventListener(this.mousemove, this.onMousemove)
    document.removeEventListener(this.mouseup, this.onMouseup)

    this.textContainer.removeEventListener(this.mousemove, this.onMousemove)

    if (e && e.target !== this.textarea && e.target !== this.textContainer) {
      let offsetX, offsetY
      if (isMobileDevice) {
        offsetX = e.changedTouches[0].clientX - this.pageViewer.div.getBoundingClientRect().left
        offsetY = e.changedTouches[0].clientY - this.pageViewer.div.getBoundingClientRect().top
      } else {
        offsetX = e.offsetX
        offsetY = e.offsetY
      }
      const newEvent = new MouseEvent(this.mouseup, {
        offsetX,
        offsetY,
        type: e.type,
        target: this.textarea
      })
      this.textarea.dispatchEvent(newEvent)
    }

    if (!this.mouseDown) return
    this.mouseDown = false
    this.moving = false
    this.textContainer.removeEventListener(this.mousemove, this.onMousemove)

    if (e.type === 'touchend') {
      document.querySelector('.document-container').style.overflow = 'auto'
    }

    let flag = true
    if (this.state === 0) {
      this.state = 1
      flag = false

      this.getTextStyle()

      this.contentContainer.selectedFrameIndex = this.editAreaIndex
      this.eventBus.dispatch('changeRightPanelBtnDisabled', false)
      this.eventBus.dispatch('contentBoxSelected', {
        type: 'text',
        pageNumber: this.pageViewer.pageIndex + 1,
      })
    }

    if (this.state === 1) {
      this.frameContainer.classList.remove('editing')

      const oldPoint = {
        start: this.start,
        end: this.end
      }
      const newPoint = {
        start: this.newStart,
        end: this.newEnd
      }

      const { pageX, pageY } = getClickPoint(e)
      if (!(pageX === this.startState?.clickX && pageY === this.startState?.clickY) && this.newStart && this.newEnd) {
        this.start = this.newStart
        this.end = this.newEnd
      }
      
      this.container.append(this.outerLineContainer)

      if (this.mouseMoved) {
        const { start, end } = this.getInitialPoint(this.start, this.end)
        const rect = {
          left: start.x,
          top: start.y,
          right: end.x,
          bottom: end.y
        }

        await this.messageHandler.sendWithPromise('SetFrame', {
          pagePtr: this.pagePtr,
          editAreaPtr: this.editAreaPtr,
          rect
        })
        this.needUndoRec = true
        await this.updateCanvas({oldPoint, newPoint})

      } else if (!this.mouseMoved && flag) {
        this.state = 2
        if (!isMobileDevice) {
          this.eventBus.dispatch('contentPropertyChange', { type: 'text', isOpen: true })
        }
      }

      this.showPopup()
    }
    
    if (this.state === 2) {
      this.outerLineContainer.remove()
      this.frameContainer.classList.add('editing')

      let endPoint
      if (isMobileDevice) {
        this.textarea.focus()
        const offsetX = e.changedTouches[0].clientX - this.pageViewer.div.getBoundingClientRect().left
        const offsetY = e.changedTouches[0].clientY - this.pageViewer.div.getBoundingClientRect().top

        endPoint = {
          x: offsetX / this.scale,
          y: offsetY / this.scale
        }
      } else {
        const offsetX = e.offsetX
        const offsetY = e.offsetY

        endPoint = {
          x: (offsetX + this.rect.left) / this.scale,
          y: (offsetY + this.rect.top) / this.scale
        }
      }
      
      if (this.mouseMoved) {
        endPoint = this.endPoint
      }
      if (!this.startPoint) {
        this.startPoint = endPoint
      }
      const { start, end } = await this.getCharsRange(this.startPoint, endPoint)
      this.activeCharPlace = end
      this.getTextStyle()

      if (!this.cursor) {
        const cursor = createSvg(
          'svg',
          {
            class: 'cursor-animation'
          },
          {
            position: 'absolute',
            left: 0,
            top: 0,
            width: '100%',
            height: '100%',
          }
        )
        this.cursor = cursor
        this.textContainer.append(this.cursor)
      } else {
        this.cursor.style.display = 'block'
      }
      this.updateCursorLine()

      if ((this.selectedRects && !this.mouseMoved) ||
        (start.SectionIndex === end.SectionIndex &&
        start.LineIndex === end.LineIndex &&
        start.RunIndex === end.RunIndex &&
        start.CharIndex === end.CharIndex)
      ) {
        this.clearSelectText()
      }

      this.textarea.focus()
      this.textarea.addEventListener('blur', this.onBlur)
      this.textarea.addEventListener('keydown', this.onKeydown)
      this.textarea.addEventListener(this.textInput, this.onTextInput)
      if (this.isFirefox || isMobileDevice) {
        this.textarea.addEventListener('compositionstart', this.onCompositionstart)
        this.textarea.addEventListener('compositionend', this.onCompositionend)
      }
      onClickOutsideUp([this.textContainer], this.handleTextOutside.bind(this))

      this.hidePopup()
    }

    this.frameContainer.classList.add('selected')

    if (this.state === 1 && !this.mouseMoved) {
      const elements = [this.textContainer, this.outerLine]

      const editorPanel = document.querySelector('.editor-panel')
      editorPanel && elements.push(editorPanel)
      const propertyPanelButton = document.getElementById('propertyPanelButton')
      propertyPanelButton && elements.push(propertyPanelButton)
      const undo = document.getElementById('undo')
      undo && elements.push(undo)
      const redo = document.getElementById('redo')
      redo && elements.push(redo)
      const contentEditorPopup = document.querySelector('.content-editor-popup')
      contentEditorPopup && elements.push(contentEditorPopup)
      
      onClickOutsideUp(elements, this.handleOutside.bind(this))
    }
  }

  async handleMouseMove (e) {
    if (this.contentContainer.selectedFrameIndex !== -1 && this.contentContainer.selectedFrameIndex !== this.editAreaIndex && this.state === 0) return

    this.moving = true
    this.hidePopup()
    
    if (e.type === 'touchmove') {
      document.querySelector('.document-container').addEventListener('scroll', event => event.preventDefault())
    }

    if (this.state === 1) {
      const { pageX, pageY } = getClickPoint(e)
      const start = this.start
      const end = this.end
      const startState = this.startState
      if (!startState) return
      if (startState.clickX === pageX && startState.clickY === pageY) return

      const { width, height } = this.viewport
      if (startState.operator === 'nw-resize') {
        let x1 = pageX - (startState.clickX - this.start.x);
        let y1 = pageY - (startState.clickY - this.start.y);
        x1 = Math.min(end.x - 5, x1)
        x1 = Math.max(0, x1)
        y1 = Math.min(end.y - 5, y1)
        y1 = Math.max(0, y1)

        this.updateOutline({
          start: { x: x1, y: y1 },
          end,
        });
      } else if (startState.operator === 'w-resize') {
        let x1 = pageX - (startState.clickX - this.start.x);
        x1 = Math.min(end.x - 5, x1)
        x1 = Math.max(0, x1)

        this.updateOutline({
          start: { x: x1, y: start.y },
          end,
        });
      } else if (startState.operator === 'sw-resize') {
        let x1 = pageX - (startState.clickX - this.start.x);
        let y1 = pageY - (startState.clickY - this.end.y);
        x1 = Math.min(end.x - 5, x1)
        x1 = Math.max(0, x1)
        y1 = Math.min(height, y1)
        y1 = Math.max(start.y + 5, y1)

        this.updateOutline({
          start: { x: x1, y: start.y },
          end: { x: end.x, y: y1 },
        });
      } else if (startState.operator === 's-resize') {
        let y1 = pageY - (startState.clickY - this.end.y);
        y1 = Math.min(height, y1)
        y1 = Math.max(start.y + 5, y1)
        this.updateOutline({
          start,
          end: { x: end.x, y: y1 },
        });
      } else if (startState.operator === 'se-resize') {
        let x = pageX - (startState.clickX - this.end.x);
        let y = pageY - (startState.clickY - this.end.y);
        x = Math.min(width, x)
        x = Math.max(start.x + 5, x)
        y = Math.min(height, y)
        y = Math.max(start.y + 5, y)
        this.updateOutline({
          start,
          end: { x, y },
        });
      } else if (startState.operator === 'e-resize') {
        let x = pageX - (startState.clickX - this.end.x);
        x = Math.min(width, x)
        x = Math.max(start.x + 5, x)
        this.updateOutline({
          start,
          end: { x, y: end.y },
        });
      } else if (startState.operator === 'ne-resize') {
        let x = pageX - (startState.clickX - this.end.x);
        let y = pageY - (startState.clickY - this.start.y);
        x = Math.min(width, x)
        x = Math.max(start.x + 5, x)
        y = Math.min(end.y - 5, y)
        y = Math.max(0, y)
        this.updateOutline({
          start: { x: start.x, y },
          end: { x, y: end.y },
        });
      } else if (startState.operator === 'n-resize') {
        let y = pageY - (startState.clickY - this.start.y);
        y = Math.min(end.y - 5, y)
        y = Math.max(0, y)
        this.updateOutline({
          start: { x: start.x, y },
          end,
        });
      } else if (startState.operator === 'move') {
        let x1 = pageX - (startState.clickX - this.start.x);
        let y1 = pageY - (startState.clickY - this.start.y);
        let x2 = pageX - (startState.clickX - this.end.x);
        let y2 = pageY - (startState.clickY - this.end.y);

        const rect = {
          width: Math.abs(start.x -end.x),
          height: Math.abs(start.y -end.y)
        }
        if (x1 < x2) {
          x1 = Math.max(0, x1)
          x1 = Math.min(width - rect.width, x1)
          x2 = Math.max(rect.width, x2)
          x2 = Math.min(width, x2)
        } else {
          x2 = Math.max(0, x2)
          x2 = Math.min(width - rect.width, x2)
          x1 = Math.max(rect.width, x1)
          x1 = Math.min(width, x1)
        }

        if (y1 < y2) {
          y1 = Math.max(0, y1)
          y1 = Math.min(height - rect.height, y1)
          y2 = Math.max(rect.height, y2)
          y2 = Math.min(height, y2)
        } else {
          y2 = Math.max(0, y2)
          y2 = Math.min(height - rect.height, y2)
          y1 = Math.max(rect.height, y1)
          y1 = Math.min(height, y1)
        }

        this.updateOutline({
          start: { x: x1, y: y1 },
          end: { x: x2, y: y2 },
        });
      }
    } else {
      if (!this.startPoint) return

      let endPoint
      if (isMobileDevice) {
        const offsetX = e.changedTouches[0].clientX - this.pageViewer.div.getBoundingClientRect().left
        const offsetY = e.changedTouches[0].clientY - this.pageViewer.div.getBoundingClientRect().top

        endPoint = {
          x: offsetX / this.scale,
          y: offsetY / this.scale
        }
      } else {
        const offsetX = e.offsetX
        const offsetY = e.offsetY

        endPoint = {
          x: (offsetX + this.rect.left) / this.scale,
          y: (offsetY + this.rect.top) / this.scale
        }
      }
      this.endPoint = endPoint
      const { start, end } = await this.getCharsRange(this.startPoint, endPoint)
      this.activeCharPlace = end
      this.selectedCharRange = { start, end }

      this.cursor.style.display = 'none'
      this.updateSelectedRect(true)
    }

    this.mouseMoved = true
  }

  handleClick () {
    // console.log('click', this.state)
  }

  handleBlur () {
    // console.log('blur')
  }

  handleTextOutside () {
    this.cursor && (this.cursor.style.display = 'none')
  }

  async handleOutside () {
    if (this.moving) {
      const elements = [this.textContainer, this.outerLine]

      const editorPanel = document.querySelector('.editor-panel')
      editorPanel && elements.push(editorPanel)
      const propertyPanelButton = document.getElementById('propertyPanelButton')
      propertyPanelButton && elements.push(propertyPanelButton)
      const undo = document.getElementById('undo')
      undo && elements.push(undo)
      const redo = document.getElementById('redo')
      redo && elements.push(redo)
      const contentEditorPopup = document.querySelector('.content-editor-popup')
      contentEditorPopup && elements.push(contentEditorPopup)
      
      onClickOutsideUp(elements, this.handleOutside.bind(this))
      return
    }
    
    this.state = this.state === 2 ? 1 : this.state === 1 ? 0 : this.state

    this.frameContainer?.classList.remove('editing')

    this.clearSelectText()
    this.cursor?.remove()
    this.cursor = null

    if (this.state === 0) {
      this.frameContainer?.classList.remove('selected')
      this.outerLineContainer?.remove()

      if (!this.removed) this.contentContainer.selectedFrameIndex = -1

      setTimeout(() => {
        const hasItem = document.querySelector('.frame-container.selected')
        if (!hasItem) {
          this.eventBus.dispatch('contentPropertyChange', { type: 'text', isOpen: false })
          this.eventBus.dispatch('changeRightPanelBtnDisabled', true)

          this.eventBus.dispatch('contentBoxDeselected', {
            type: 'text',
            pageNumber: this.pageViewer.pageIndex + 1,
          })
        }
      }, 1)
      
      this.hidePopup()
    }

    if (this.state === 1) {
      const text = await this.getText()
      if (text.length === 0) {
        this.remove()
        return
      }

      this.textContainer.removeEventListener(this.mousemove, this.onMousemove)

      if (!this.hidden) {
        this.container.append(this.outerLineContainer)

        const elements = [this.textContainer, this.outerLine]

        const editorPanel = document.querySelector('.editor-panel')
        editorPanel && elements.push(editorPanel)
        const propertyPanelButton = document.getElementById('propertyPanelButton')
        propertyPanelButton && elements.push(propertyPanelButton)
        const undo = document.getElementById('undo')
        undo && elements.push(undo)
        const redo = document.getElementById('redo')
        redo && elements.push(redo)
        const contentEditorPopup = document.querySelector('.content-editor-popup')
        contentEditorPopup && elements.push(contentEditorPopup)
        
        onClickOutsideUp(elements, this.handleOutside.bind(this))

        this.showPopup()
      }
    }

    this.textarea.removeEventListener('blur', this.onBlur)
    this.textarea.removeEventListener('keydown', this.onKeydown)
    this.textarea.removeEventListener(this.textInput, this.onTextInput)
    if (this.isFirefox || isMobileDevice) {
      this.textarea.removeEventListener('compositionstart', this.onCompositionstart)
      this.textarea.removeEventListener('compositionend', this.onCompositionend)
    }
  }

  async handleKeyDown (e) {
    let keyCode = e.keyCode || e.which

    const toolKey = [9, 16, 17, 18, 20, 27, 37, 38, 39, 40, 91, 93]
    let isToolKey = e.ctrlKey || e.altKey || e.metaKey || toolKey.includes(keyCode)

    this.isToolKey = isToolKey

    if (keyCode === 8 || keyCode === 46) { // 8 backspace键，46 delete键
      if (this.activeCharPlace.SectionIndex === 0
        && this.activeCharPlace.LineIndex === 0
        && this.activeCharPlace.RunIndex === 0
        && this.activeCharPlace.CharIndex === -1
        && !this.selectedCharRange
      ) return

      let newChar = {}
      if (this.selectedCharRange) {
        newChar = await this.getCharPlace('DeleteChars')
        this.selectedCharRange = null
      } else if (keyCode === 8) {
        newChar = await this.getCharPlace('BackSpaceChar')
      } else {
        newChar = await this.getCharPlace('DeleteChar')
      }
      this.activeCharPlace = newChar
      this.needUndoRec = true
      await this.updateCanvas(null, true)
      this.clearSelectText()
      this.updateCursorLine()
      return
    }

    if (this.selectedCharRange && !isToolKey) {
      const newChar = await this.getCharPlace('DeleteChars')
      this.activeCharPlace = newChar
      this.selectedCharRange = null
      this.needUndoRec = true
      await this.updateCanvas(null, true)
      this.updateCursorLine()
    }

    this.isPasteKey = (e.metaKey || e.ctrlKey) && (e.key === 'v' || e.key === 'V')

    if (keyCode === 37) { // 左键
      const newChar = await this.getCharPlace('GetPrevCharPlace')
      this.activeCharPlace = newChar.start
    } else if (keyCode === 38) { // 上键
      const newChar = await this.getCharPlace('GetUpCharPlace')
      this.activeCharPlace = newChar.start
    } else if (keyCode === 39) { // 右键
      const newChar = await this.getCharPlace('GetNextCharPlace')
      this.activeCharPlace = newChar.start
    } else if (keyCode === 40) { // 下键
      const newChar = await this.getCharPlace('GetDownCharPlace')
      this.activeCharPlace = newChar.start
    } else if ((e.metaKey || e.ctrlKey) && (e.key === 'c' || e.key === 'C') && this.selectedCharRange) { // 复制
      const copyText = await this.getText()
      copy(copyText)
      return
    } else if ((e.metaKey || e.ctrlKey) && (e.key === 'x' || e.key === 'X') && this.selectedCharRange) { // 剪切
      const copyText = await this.getText()
      copy(copyText)

      const newChar = await this.getCharPlace('DeleteChars')
      this.activeCharPlace = newChar
      this.selectedCharRange = null
    } else {
      return
    }
    this.clearSelectText()
    this.updateCursorLine()
  }

  async handleTextInput (e) {
    if (this.isToolKey && !this.isPasteKey) return

    let data = e.data

    if (this.isFirefox || isMobileDevice) {
      if (e.inputType === 'insertLineBreak') {
        data = '\n'
      } else if (this.composing || data === null) {
        return
      }
    }
    this.#editList.push(data)
    if (this.#editList.length > 1) return
    this.handleInsertText()
  }

  async handleInsertText() {
    const newChar = await this.messageHandler.sendWithPromise('InsertText', {
      editAreaPtr: this.editAreaPtr,
      char: this.activeCharPlace,
      text: this.#editList[0]
    })
    this.activeCharPlace = newChar

    this.addUndoHistory()
    this.updateCanvas()
    this.updateCursorLine()
    this.#editList.length && this.#editList.shift()
    if (this.#editList.length > 0) {
      await this.handleInsertText()
    }
  }

  // 兼容FireFox 监听中文输入
  handleCompositionstart () {
    this.composing = true
  }
  async handleCompositionend (e) {
    const newChar = await this.messageHandler.sendWithPromise('InsertText', {
      editAreaPtr: this.editAreaPtr,
      char: this.activeCharPlace,
      text: e.data
    })
    this.activeCharPlace = newChar

    this.needUndoRec = true
    this.updateCanvas()
    this.updateCursorLine()

    this.composing = false
  }

  // 获取rect
  async getRect () {
    this.frame = await this.messageHandler.sendWithPromise('GetFrame', {
      pagePtr: this.pagePtr,
      editAreaPtr: this.editAreaPtr
    })
    const { start, end } = this.getActualRect(
      this.viewport,
      this.scale,
      this.frame
    )
    this.start = start
    this.end = end

    this.oldRect = this.rect || null
    const rect = this.rectCalc(start, end)
    this.rect = rect
  }

  // 更新canvas图
  async updateCanvas(whole, oldRect, noRedraw) {
    if (!this.removed) await this.getRect()

    if (this.frameContainer && this.outerLineContainer) {
      this.updateOutline({
        start: this.start,
        end: this.end
      })

      setCss(this.frameContainer, {
        left: this.rect.left + 'px',
        top: this.rect.top + 'px',
        width: this.rect.width + 'px',
        height: this.rect.height + 'px'
      })
    }

    if (whole) {
      const { oldPoint, newPoint } = whole
      const wholeArea = this.getEntireArea(oldPoint, newPoint)

      const { start, end } = this.getActualRect(
        this.viewport,
        this.scale,
        wholeArea
      )
      let wholeAreaRect = this.rectCalc(start, end)

      const oldRect = this.rectCalc(oldPoint.start, oldPoint.end)
      const newWholeArea = this.getEntireArea(oldRect, this.rect)
      const newWholeAreaRect = {
        left: newWholeArea.left,
        top: newWholeArea.top,
        width: newWholeArea.right - newWholeArea.left,
        height: newWholeArea.bottom - newWholeArea.top
      }
      if (
        parseInt(wholeAreaRect.left) !== parseInt(newWholeArea.left) ||
        parseInt(wholeAreaRect.top) !== parseInt(newWholeArea.top) ||
        parseInt(wholeAreaRect.width) !== parseInt(newWholeArea.width) ||
        parseInt(wholeAreaRect.height) !== parseInt(newWholeArea.height)
      ) {
        wholeAreaRect = newWholeAreaRect
      }

      const imgRect = {
        left: parseInt(wholeAreaRect.left * this.ratio),
        top: parseInt(wholeAreaRect.top * this.ratio),
        right: parseInt(wholeAreaRect.left * this.ratio) + parseInt(wholeAreaRect.width * this.ratio),
        bottom: parseInt(wholeAreaRect.top * this.ratio) + parseInt(wholeAreaRect.height * this.ratio),
        width: parseInt(wholeAreaRect.width * this.ratio),
        height: parseInt(wholeAreaRect.height * this.ratio),
      }
      this.imgRect = imgRect

      this.canvas.width = this.rect.width * this.ratio
      this.canvas.height = this.rect.height * this.ratio

    } else if (oldRect) {
      const wholeAreaRect = this.getEntireArea(this.oldRect, this.rect)

      const imgRect = {
        left: parseInt(wholeAreaRect.left * this.ratio),
        top: parseInt(wholeAreaRect.top * this.ratio),
        right: parseInt(wholeAreaRect.right * this.ratio),
        bottom: parseInt(wholeAreaRect.bottom * this.ratio),
        width: parseInt(wholeAreaRect.right * this.ratio) - parseInt(wholeAreaRect.left * this.ratio),
        height: parseInt(wholeAreaRect.bottom * this.ratio) - parseInt(wholeAreaRect.top * this.ratio),
      }
      this.imgRect = imgRect

      this.canvas.width = this.rect.width * this.ratio
      this.canvas.height = this.rect.height * this.ratio

    } else {
      const imgRect = {
        left: parseInt(this.rect.left * this.ratio),
        top: parseInt(this.rect.top * this.ratio),
        right: parseInt(this.rect.left * this.ratio) + parseInt(this.rect.width * this.ratio),
        bottom: parseInt(this.rect.top * this.ratio) + parseInt(this.rect.height * this.ratio),
        width: parseInt(this.rect.width * this.ratio),
        height: parseInt(this.rect.height * this.ratio),
      }
      this.imgRect = imgRect

      this.canvas.width = imgRect.width
      this.canvas.height = imgRect.height
    }

    if (noRedraw) return

    const { imageArray } = await this.messageHandler.sendWithPromise('PushRenderTask', {
      pagePtr: this.pagePtr,
      scale: this.scale * this.ratio,
      left: this.imgRect.left,
      right: this.imgRect.right,
      bottom: this.imgRect.bottom,
      top: this.imgRect.top
    })
    this.imageArray = imageArray

    this.drawCanvas()
    this.saveEdit()
  }

  // 绘制canvas
  drawCanvas (noRedraw = false) {
    if (!this.imgRect) {
      this.updateCanvas(null, null, noRedraw)
      return
    }
    if (!this.pageViewer.canvas || noRedraw) return
    const pageCtx = this.pageViewer.canvas.getContext('2d')
    const imageData = pageCtx.createImageData(parseInt(this.imgRect.width), parseInt(this.imgRect.height))
    imageData.data.set(this.imageArray)
    pageCtx.putImageData(imageData, this.imgRect.left, this.imgRect.top)

    this.eventBus.dispatch('redrawMarkups', {
      pageIndex: this.pageViewer.pageIndex,
      afterContentEdit: true,
      rect: this.oldRect ? this.getEntireArea(this.oldRect, this.rect) : this.rect,
      pagePtr: this.pagePtr
    })
  }

  // 更新光标位置
  async updateCursorLine (char) {
    char && (this.activeCharPlace = char)

    let cursorPoints = await this.messageHandler.sendWithPromise('GetTextCursorPoints', {
      pagePtr: this.pagePtr,
      editAreaPtr: this.editAreaPtr,
      char: this.activeCharPlace
    })
    
    if (!this.cursor) return
    this.cursor.innerHTML = ''
    const cursorLine = createSvg(
      "line",
      {
        x1: cursorPoints.HighPointX * this.scale - this.rect.left,
        x2: cursorPoints.LowPointX * this.scale - this.rect.left,
        y1: cursorPoints.HighPointY * this.scale - this.rect.top,
        y2: cursorPoints.LowPointY * this.scale - this.rect.top,
        stroke: 'currentcolor'
      },
    )
    this.cursorLine = cursorLine
    this.cursor.append(cursorLine)
  }

  // 获取文本属性
  async getTextStyle () {
    const textStyle = await this.messageHandler.sendWithPromise('GetTextStyle', {
      editAreaPtr: this.editAreaPtr,
      char: this.activeCharPlace
    })

    const fontFamily = await this.messageHandler.sendWithPromise('GetBaseFontName', {
      editAreaPtr: this.editAreaPtr,
      char: this.activeCharPlace
    })

    const lineSpacing = await this.messageHandler.sendWithPromise('GetLineLeading', {
      editAreaPtr: this.editAreaPtr,
      char: this.activeCharPlace
    })

    const paragraphSpacing = await this.messageHandler.sendWithPromise('GetSectionAfterLeading', {
      editAreaPtr: this.editAreaPtr,
      char: this.activeCharPlace
    })

    await this.getEntireCharPlace()
    const charRange = this.selectedCharRange || this.entireCharRange
    const characterSpacing = await this.messageHandler.sendWithPromise('GetCharacterSpacing', {
      editAreaPtr: this.editAreaPtr,
      start: charRange.start,
      end: charRange.end
    })
    
    const style = {
      color: `rgb(${textStyle.R}, ${textStyle.G}, ${textStyle.B})`,
      opacity: Math.round(textStyle.Transparency * 100),
      fontStyle: textStyle.IsBold && textStyle.IsItalic ? 3 : !textStyle.IsBold && !textStyle.IsItalic ? 0 : textStyle.IsBold ? 1 : 2,
      fontSize: Math.round(textStyle.FontSize),
      fontFamily,
      lineSpacing: Math.round(lineSpacing * 10) / 10,
      paragraphSpacing: Math.round(paragraphSpacing * 10) / 10,
      characterSpacing: Math.max(Math.round(characterSpacing * 10) / 10, 0)
    }
    this.textStyle = style

    const alignType = await this.messageHandler.sendWithPromise('GetTextSectionAlignType', {
      editAreaPtr: this.editAreaPtr,
      char: this.activeCharPlace
    })

    this.eventBus.dispatch('contentPropertyChange', { type: 'text', ...style, alignType })
  }

  // 某个操作之后，获取光标所在字符的位置
  async getCharPlace (action) {
    const data = {
      editAreaPtr: this.editAreaPtr,
      char: this.activeCharPlace
    }

    if (action === 'DeleteChars') {
      data.start = this.selectedCharRange.start
      data.end = this.selectedCharRange.end
    }

    return await this.messageHandler.sendWithPromise(action, data)
  }

  // 根据定位点，获取选中区域的始末字符位置
  async getCharsRange (startPoint, endPoint) {
    return await this.messageHandler.sendWithPromise('SelectCharsRangeAtPos', {
      pagePtr: this.pagePtr,
      editAreaPtr: this.editAreaPtr,
      startPoint,
      endPoint
    })
  }

  // 保存编辑
  saveEdit () {
    this.messageHandler.sendWithPromise('EndEdit', this.editPagePtr)
    if (this.needUndoRec) {
      this.addUndoHistory()
      this.needUndoRec = false
    }
  }

  // 获取区域内的文本
  async getText () {
    if (this.selectedCharRange) {
      const { start, end } = this.selectedCharRange
      return await this.messageHandler.sendWithPromise('GetTextByRange', {
        pagePtr: this.pagePtr,
        editAreaPtr: this.editAreaPtr,
        start,
        end
      })
    } else {
      return await this.messageHandler.sendWithPromise('GetText', this.editAreaPtr)
    }
  }

  // 更新outerline框的位置大小
  updateOutline ({ start, end }) {
    const rect = this.rectCalc(start, end)

    this.newStart = start
    this.newEnd = end

    setCss(this.outerLine, {
      left: rect.left - this.borderWidth + 'px',
      top: rect.top - this.borderWidth + 'px',
      width: rect.width + this.borderWidth + this.pointWidth / 2 + 'px',
      height: rect.height + this.borderWidth + this.pointHeight / 2 + 'px',
    })

    this.moveRect.setAttribute('width', rect.width - this.borderWidth)
    this.moveRect.setAttribute('height', rect.height - this.borderWidth)
    
    this.LeftRect.setAttribute("y", (rect.height - this.pointHeight + this.borderWidth) / 2)

    this.bottomLeftRect.setAttribute("y", rect.height - this.pointHeight / 2 + this.borderWidth / 2)

    this.bottomRect.setAttribute("x", (rect.width - this.pointWidth + this.borderWidth) / 2)
    this.bottomRect.setAttribute("y", rect.height - this.pointHeight / 2 + this.borderWidth / 2)


    this.bottomRightRect.setAttribute("x", rect.width - this.pointWidth / 2 + this.borderWidth / 2)
    this.bottomRightRect.setAttribute("y", rect.height - this.pointHeight / 2 + this.borderWidth / 2)


    this.rightRect.setAttribute("x", rect.width - this.pointWidth / 2 + this.borderWidth / 2)
    this.rightRect.setAttribute("y", (rect.height - this.pointHeight + this.borderWidth) / 2)

    
    this.topRightRect.setAttribute("x", rect.width - this.pointWidth / 2 + this.borderWidth / 2)

    this.topRect.setAttribute("x", (rect.width - this.pointWidth + this.borderWidth) / 2)
  }

  // 获取选中区域里文本的矩形rect
  async getCharsRect (startPoint, endPoint, isUpdate) {
    if (
      startPoint.SectionIndex === endPoint.SectionIndex &&
      startPoint.LineIndex === endPoint.LineIndex &&
      startPoint.RunIndex === endPoint.RunIndex &&
      startPoint.CharIndex === endPoint.CharIndex
    ) return

    // const rectCount = await this.messageHandler.sendWithPromise('GetSelectLineRectsCounts', {
    //   editAreaPtr: this.editAreaPtr,
    //   start: startPoint,
    //   end: endPoint
    // })
    // console.log(rectCount)

    const selectedRects = await this.messageHandler.sendWithPromise('GetSelectLineRects', {
      pagePtr: this.pagePtr,
      editAreaPtr: this.editAreaPtr,
      start: startPoint,
      end: endPoint
    })

    let selectedRectList = []
    for (let i = 0; i < selectedRects.length; i++) {
      const rect = {
        left: selectedRects[i].Left,
        top: selectedRects[i].Top,
        right: selectedRects[i].Right,
        bottom: selectedRects[i].Bottom
      }
      const { start, end } = this.getActualRect(
        this.viewport,
        this.scale,
        rect
      )

      if (!isUpdate) {
        this.start = start
        this.end = end
      }

      const selectedRect = this.rectCalc(start, end, 0)
      selectedRectList.push(selectedRect)
    }
    return selectedRectList
  }

  // 更新选中文本的矩形框信息
  async updateSelectedRect (isMoving) {
    if (!this.selectedCharRange) return
    const { start, end } = this.selectedCharRange
    const selectedRectList = await this.getCharsRect(start, end)
    if (!selectedRectList?.length) return
  
    const lastSelectedRect = this.selectedRects?.[this.selectedRects.length - 1]
  
    // check if the last selected rectangle is the same as the new one
    if (
      isMoving &&
      lastSelectedRect?.left === selectedRectList[0].left &&
      lastSelectedRect?.top === selectedRectList[0].top &&
      lastSelectedRect?.width === selectedRectList[0].width &&
      lastSelectedRect?.height === selectedRectList[0].height
    ) {
      this.selectedRects = selectedRectList // store the new rectangles
      return // do not clear or redraw
    }
  
    const ctx = this.canvas.getContext('2d')

    // clear previous rectangles
    if (this.selectedRects?.length) {
      const minListLength = Math.max(this.selectedRects.length, selectedRectList.length)
      for (let i = 0; i < minListLength; i++) {
        const prevSelectedRect = this.selectedRects[i]
        const currSelectedRect = selectedRectList[i]
        if (prevSelectedRect?.left === currSelectedRect?.left &&
            prevSelectedRect?.top === currSelectedRect?.top &&
            prevSelectedRect?.width === currSelectedRect?.width &&
            prevSelectedRect?.height === currSelectedRect?.height) {
          // Skip clearing and drawing for identical rectangles
          continue
        }
  
        ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
      }
      this.drawCanvas(true)
    }
  
    this.selectedRects = selectedRectList

    this.selectText()
    this.updateCursorLine()
  }

  // 选中文本的渲染
  selectText () {
    if (this.selectedRects?.length) {
      const ctx = this.canvas.getContext('2d')
      ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)

      for (const selectedRect of this.selectedRects) {
        ctx.beginPath()
        ctx.fillStyle = 'rgba(0, 117, 255, 0.3)'
        ctx.fillRect(
          (selectedRect.left - this.rect.left) * this.ratio,
          (selectedRect.top - this.rect.top) * this.ratio,
          selectedRect.width * this.ratio,
          selectedRect.height * this.ratio
        )
      }
    }
  }

  // 取消选中文本
  clearSelectText () {
    if (this.selectedRects?.length) {
      const ctx = this.canvas.getContext('2d')
      ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
      this.updateCanvas()
    }
    this.selectedRects = null
    this.selectedCharRange = null
  }

  // 属性面板 修改属性
  async handlePropertyPanelChanged (props) {
    if (this.state === 0) return

    let changed = false

    await this.getEntireCharPlace()
    const charRange = this.selectedCharRange || this.entireCharRange

    for (const item in props) {
      if (props[item] === this.textStyle[item]) continue

      if (item === 'alignType') {
        if (!this.selectedCharRange && this.state === 2) {
          await this.messageHandler.sendWithPromise('SetTextAligningSection', {
            editAreaPtr: this.editAreaPtr,
            alignType: props.alignType,
            char: this.activeCharPlace
          })
        } else {
          await this.messageHandler.sendWithPromise('SetTextAligningRange', {
            editAreaPtr: this.editAreaPtr,
            alignType: props.alignType,
            start: charRange.start,
            end: charRange.end,
          })
        }
      }
      
      if (item === 'color') {
        await this.messageHandler.sendWithPromise('SetCharsFontColor', {
          editAreaPtr: this.editAreaPtr,
          start: charRange.start,
          end: charRange.end,
          color: props.color
        })
      }

      if (item === 'opacity') {
        await this.messageHandler.sendWithPromise('SetCharsFontTransparency', {
          editAreaPtr: this.editAreaPtr,
          start: charRange.start,
          end: charRange.end,
          opacity: props.opacity / 100
        })
      }

      if (item === 'fontSize') {
        await this.messageHandler.sendWithPromise('SetCharsFontSize', {
          editAreaPtr: this.editAreaPtr,
          start: charRange.start,
          end: charRange.end,
          fontSize: props.fontSize
        })
      }

      if (item === 'fontFamily') {
        await this.messageHandler.sendWithPromise('SetFontFromNativeTrueTypeFont', {
          editAreaPtr: this.editAreaPtr,
          start: charRange.start,
          end: charRange.end,
          fontFamily: props.fontFamily
        })
      }

      if (item === 'fontStyle') {
        const fontStyle = props.fontStyle

        await this.setCharsFontStyle('ClearCharsFontBold')
        await this.refreshSelectedRange()
        await this.setCharsFontStyle('ClearCharsFontItalic')
        await this.refreshSelectedRange()

        if (fontStyle === 1) {
          await this.setCharsFontStyle('SetCharsFontBold')
        } else if (fontStyle === 2) {
          await this.setCharsFontStyle('SetCharsFontItalic')
        } else if (fontStyle === 3) {
          await this.setCharsFontStyle('SetCharsFontBold')
          await this.setCharsFontStyle('SetCharsFontItalic')
        }
      }

      if (item === 'lineSpacing') {
        await this.messageHandler.sendWithPromise('SetLineLeading', {
          editAreaPtr: this.editAreaPtr,
          char: this.activeCharPlace,
          spacing: props.lineSpacing
        })
      }

      if (item === 'paragraphSpacing') {
        await this.messageHandler.sendWithPromise('SetSectionAfterLeading', {
          editAreaPtr: this.editAreaPtr,
          char: this.activeCharPlace,
          spacing: props.paragraphSpacing
        })
      }

      if (item === 'characterSpacing') {
        await this.messageHandler.sendWithPromise('SetCharacterSpacing', {
          editAreaPtr: this.editAreaPtr,
          start: charRange.start,
          end: charRange.end,
          spacing: props.characterSpacing
        })
      }

      this.textStyle[item] = props[item]
      changed = true
      this.needUndoRec = true
    }

    if (!changed) return

    await this.updateCanvas(null, true)

    if (this.selectedCharRange && this.state === 2) {
      await this.refreshSelectedRange()
    }
    
    if (this.state === 2) {
      this.updateSelectedRect()
      this.updateCursorLine()
    }
  }

  // 设置文本样式
  async setCharsFontStyle (action) {
    const charRange = this.selectedCharRange || this.entireCharRange
    await this.messageHandler.sendWithPromise('SetCharsFontStyle', {
      editAreaPtr: this.editAreaPtr,
      start: charRange.start,
      end: charRange.end,
      fontStyle: action
    })
  }

  // 十六进制颜色转换为rgb
  hexToRgb (hex) {
    hex = hex.replace("#", "")
  
    let shorthand = hex.length === 3
  
    hex = shorthand ? hex.repeat(2) : hex
  
    let r = parseInt(hex.substr(0, 2), 16)
    let g = parseInt(hex.substr(2, 2), 16)
    let b = parseInt(hex.substr(4, 2), 16)
  
    return {r, g, b}
  }

  // 计算编辑区域大小变化后，影响的整个区域的位置
  getEntireArea (oldPoint, newPoint) {
    let area = null
    if (oldPoint.start && newPoint.start) {
      let oldP = this.getInitialPoint(oldPoint.start, oldPoint.end)
      let newP = this.getInitialPoint(newPoint.start, newPoint.end)

      let left = Math.min(oldP.start.x, newP.start.x)
      let top = Math.min(oldP.start.y, newP.start.y)
      let right = Math.max(oldP.end.x, newP.end.x)
      let bottom = Math.max(oldP.end.y, newP.end.y)

      area = {
        left,
        top,
        right,
        bottom
      }

    } else if (oldPoint.left && newPoint.left) {
      let left = Math.min(oldPoint.left, newPoint.left)
      let top = Math.min(oldPoint.top, newPoint.top)
      let right = Math.max(oldPoint.left + oldPoint.width, newPoint.left + newPoint.width)
      let bottom = Math.max(oldPoint.top + oldPoint.height, newPoint.top + newPoint.height)

      area = {
        left,
        top,
        right,
        bottom
      }
    }
    
    return area
  }

  // 直接进入编辑模式
  goEditing () {
    this.state = 2
    this.contentContainer.selectedFrameIndex = this.editAreaIndex
    this.frameContainer.classList.add('selected')
    this.frameContainer.classList.add('editing')

    this.activeCharPlace = {
      SectionIndex: 0,
      LineIndex: 0,
      RunIndex: 0,
      CharIndex: -1
    }

    const cursor = createSvg(
      'svg',
      {
        class: 'cursor-animation'
      },
      {
        position: 'absolute',
        left: 0,
        top: 0,
        width: '100%',
        height: '100%',
      }
    )
    this.cursor = cursor
    this.textContainer.append(this.cursor)

    this.updateCursorLine()

    this.textarea.focus()
    this.textarea.addEventListener('blur', this.onBlur)
    this.textarea.addEventListener('keydown', this.onKeydown)
    this.textarea.addEventListener(this.textInput, this.onTextInput)
    if (this.isFirefox || isMobileDevice) {
      this.textarea.addEventListener('compositionstart', this.onCompositionstart)
      this.textarea.addEventListener('compositionend', this.onCompositionend)
    }

    const elements = [this.textContainer, this.outerLine]

    const editorPanel = document.querySelector('.editor-panel')
    editorPanel && elements.push(editorPanel)
    const propertyPanelButton = document.getElementById('propertyPanelButton')
    propertyPanelButton && elements.push(propertyPanelButton)
    const undo = document.getElementById('undo')
    undo && elements.push(undo)
    const redo = document.getElementById('redo')
    redo && elements.push(redo)
    const contentEditorPopup = document.querySelector('.content-editor-popup')
    contentEditorPopup && elements.push(contentEditorPopup)
    
    onClickOutsideUp(elements, this.handleOutside.bind(this))
  }

  // 删除区域
  async remove () {
    this.state = 0
    await this.handleOutside()

    await this.outerLineContainer.remove()
    this.frameContainer.remove()
    this.frameContainer = null
    this.contentContainer.selectedFrameIndex = -1
    this.removed = true

    await this.messageHandler.send('RemoveEditAreaByIndex', {
      editPagePtr: this.editPagePtr,
      editAreaIndex: this.editAreaIndex
    })
    // await this.messageHandler.send('RemoveEditArea', {
    //   editPagePtr: this.editPagePtr,
    //   editAreaPtr: this.editAreaPtr
    // })

    this.needUndoRec = true
    this.updateCanvas()
    this.contentContainer.removeEditor(this.editAreaIndex)
    this.eventBus.dispatch('contentPropertyChange', { type: 'text', isOpen: false })

    this.eventBus._off('textPropertyChanged', this.onHandlePropertyPanelChanged)
    this.eventBus._off('showContentEditorType', this.onShowContentEditorType)
    this.eventBus._off('contentEditorPopupClicked', this.onHandlePopup)
  }
  
  removeViewer () {
    this.state = 0
    // await this.handleOutside()
    
    this.outerLineContainer?.remove()
    this.frameContainer?.remove()
    this.frameContainer = null
    this.contentContainer.selectedFrameIndex = -1
    this.removed = true
    this.eventBus.dispatch('contentPropertyChange', { type: 'text', isOpen: false })
  }

  // 复制区域对象
  async copyArea () {
    const editAreaInfoPtr = await this.messageHandler.sendWithPromise('CopyArea', {
      editPagePtr: this.editPagePtr,
      editAreaPtr: this.editAreaPtr
    })
    const copyText = await this.messageHandler.sendWithPromise('GetCopyAreaContent', {
      editPagePtr: this.editPagePtr,
      editAreaInfoPtr
    })
  }

  // 获取整个区域的文本信息
  async getEntireCharPlace () {
    const { start, end } = await this.messageHandler.sendWithPromise('GetBeginAndEndCharPlace', this.editAreaPtr)
    this.entireCharRange = { start, end }
    return { start, end }
  }

  // 选中整个区域的文本
  async selectAllText () {
    const { start, end } = await this.getEntireCharPlace()
    this.selectedCharRange = { start, end }
    if (this.state === 2) this.selectedRectList = await this.getCharsRect(start, end)
  }

  // 图片模式下隐藏
  handleShow (type) {
    if (type === 'image') {
      this.hidden = true
      this.frameContainer && (this.frameContainer.style.display = 'none')
      if (this.state === 1) {
        this.handleOutside()
      }
    } else {
      this.hidden = false
      this.frameContainer && (this.frameContainer.style.display = 'block')
    }
  }

  // 添加undo记录
  addUndoHistory () {
    this.contentContainer.handleOperateList({
      editPagePtr: this.editPagePtr,
      editAreaIndex: this.editAreaIndex,
      editAreaPtr: this.editAreaPtr,
      pageIndex: this.pageViewer.pageIndex
    })
  }

  // undo/redo后 更新canvas图
  async updateCanvasAfterUndoRedo() {
    await this.getRect()

    if (this.frameContainer && this.outerLineContainer) {
      this.updateOutline({
        start: this.start,
        end: this.end
      })

      setCss(this.frameContainer, {
        left: this.rect.left + 'px',
        top: this.rect.top + 'px',
        width: this.rect.width + 'px',
        height: this.rect.height + 'px'
      })
    }

    const wholeAreaRect = this.getEntireArea(this.oldRect, this.rect)

    const imgRect = {
      left: parseInt(wholeAreaRect.left * this.ratio),
      top: parseInt(wholeAreaRect.top * this.ratio),
      right: parseInt(wholeAreaRect.right * this.ratio),
      bottom: parseInt(wholeAreaRect.bottom * this.ratio),
      width: parseInt(wholeAreaRect.right * this.ratio) - parseInt(wholeAreaRect.left * this.ratio),
      height: parseInt(wholeAreaRect.bottom * this.ratio) - parseInt(wholeAreaRect.top * this.ratio),
    }
    this.imgRect = imgRect

    this.canvas.width = this.rect.width * this.ratio
    this.canvas.height = this.rect.height * this.ratio

    let { imageArray } = await this.messageHandler.sendWithPromise('PushRenderTask', {
      pagePtr: this.pagePtr,
      scale: this.scale * this.ratio,
      left: this.imgRect.left,
      right: this.imgRect.right,
      bottom: this.imgRect.bottom,
      top: this.imgRect.top
    })
    this.imageArray = imageArray

    this.drawCanvas()
    this.saveEdit()
    if (this.contentContainer.selectedFrameIndex === this.editAreaIndex) {
      this.getTextStyle()
    }
  }

  // 修改属性后，刷新选中范围的数据
  async refreshSelectedRange() {
    const charRange = this.selectedCharRange || this.entireCharRange
    const { start, end } = await this.messageHandler.sendWithPromise('RefreshRange', {
      editAreaPtr: this.editAreaPtr,
      start: charRange.start,
      end: charRange.end,
    })

    if (this.selectedCharRange) this.selectedCharRange = { start, end }
    else this.entireCharRange = { start, end }

    if (this.activeCharPlace.CharIndex === start.CharIndex) this.activeCharPlace = start
    else this.activeCharPlace = end
  }

  // 计算坐标并显示文本悬浮窗
  async showPopup() {
    const pageRect = this.container.getBoundingClientRect()
    const rect = {
      left: this.rect.left + pageRect.left,
      top: this.rect.top + pageRect.top,
      right: this.rect.width + this.rect.left + pageRect.left,
      bottom: this.rect.height + this.rect.top + pageRect.top
    }
    this.eventBus.dispatch('showContentEditorPopup', { rect })
    this.isPopupShow = true

    const textRects = []
    const { start, end } = this.selectedCharRange || await this.getEntireCharPlace()
    const oriTextRects = await this.getCharsRect(start, end, true)

    for (let i = 0; i < oriTextRects.length; i++) {
      const textRect = oriTextRects[i]
      const { left, top, width, height } = textRect
      textRects.push({
        left,
        top,
        right: left + width,
        bottom: top + height
      })
    }

    const selectedText = await this.getText()
    this.eventBus.dispatch('contentSelected', {
      selectedText,
      pageNumber: this.pageViewer.pageIndex + 1,
      textRects
    })
  }

  hidePopup() {
    if (!this.isPopupShow) return
    this.isPopupShow = false
    this.eventBus.dispatch('showContentEditorPopup')
  }

  async handlePopup(data) {
    if (this.state === 0) return

    switch (data) {
      case 'copy':
        const copyText = await this.getText()
        copy(copyText)
        break;

      case 'delete':
        this.remove()
        break;

      // case 'paste':
      //   const text = await navigator.clipboard.readText()
      //   this.#editList.push(text)
      //   if (this.#editList.length > 1) return
      //   this.handleInsertText()
      //   break;
    }
  }
}
