import BaseAnnotation from '../annotation/base'
import { onClickOutside, convertColorToRGB } from '../ui_utils'
import { createSvg, createElement } from '../annotation/utils'
import { getActualPoint } from '../annotation/utils'

class TextAnnotation extends BaseAnnotation {
  constructor ({
    container,
    annotation,
    page,
    viewport,
    scale,
    eventBus,
    layer,
    show = false,
    enableReply = true,
    pageViewer,
    messageHandler
  }) {
    super({
      container,
      annotation,
      page,
      viewport,
      scale,
      eventBus,
      show
    })
    this.enableReply = enableReply
    this.layer = layer
    this.pageViewer = pageViewer
    this.messageHandler = messageHandler
    this.hidden = true
    this.markupContainer = null
    this.outerLineContainer = null
    this.ratio = window.devicePixelRatio || 1
    this.pageViewer.canvas && (this.ctx = this.pageViewer.canvas.getContext('2d', { willReadFrequently: true }))
    this.lineWidth = 2 * this.scale * this.ratio

    this.onHandleClick = this.handleClick.bind(this)
    this.onDelete = this.handleDelete.bind(this)
    this.onOpenReply = this.openReply.bind(this)

    this.render()
  }

  render () {
    const annotation = this.annotation
    const scale = this.scale

    const { start, end } = this.getActualRect(
      this.viewport,
      scale
    )

    const rect = this.calculate(start, end)
    this.rect = rect

    const quadPoints = annotation.quadPoints
    if (!quadPoints) return

    let positionArray = []
    for (let i = 0; (i + 3) < quadPoints.length; i+=4) {
      const x1 = parseInt(quadPoints[i].PointX * scale, 10),
        y1 = parseInt(quadPoints[i].PointY * scale, 10),
        x2 = parseInt(quadPoints[i + 1].PointX * scale, 10),
        y3 = parseInt(quadPoints[i + 2].PointY * scale, 10)
      const left = Math.min(x1, x2)
      const right = Math.max(x1, x2)
      const top = Math.min(y1, y3)
      const bottom = Math.max(y1, y3)
      const width = parseInt(right - left)
      const height = parseInt(bottom - top)
      positionArray.push({
        left,
        top,
        width,
        height
      })
    }
    this.positionArray = positionArray

    if (this.ctx) {
      this.ctx.globalCompositeOperation = 'multiply'
      this.renderCanvansRect(positionArray)
      this.markupContainer.addEventListener('click', this.onHandleClick)
    }
    
    this.outerLineContainer = document.createElement('div')
    this.outerLineContainer.className = 'outline-container'

    const rectContainer = createSvg(
      "svg"
    )
    rectContainer.style.left = (rect.left - 6) + 'px'
    rectContainer.style.top = (rect.top - 6) + 'px'
    rectContainer.style.width = (rect.width + 12) + 'px'
    rectContainer.style.height = (rect.height + 12) + 'px'

    const recttangle = createSvg(
      "rect", {
        x: 3,
        y: 3,
        width: rect.width + 6,
        height: rect.height + 6,
        fill: '#ffffff',
        'fill-opacity': 0,
        'stroke-width': 1,
        stroke: '#1460F3'
      }
    )
    rectContainer.append(recttangle)

    const deletetButton = createSvg(
      "svg",
      {
        width: "30",
        height: "30",
        viewBox: "0 0 30 30",
        fill: "none",
        class: 'delete-button'
      },
      {
        width: '30px',
        height: '30px'
      }
    )
    deletetButton.addEventListener('click', this.onDelete)
    deletetButton.innerHTML = this.deleteSvgStr
    deletetButton.style.left = (rect.left + rect.width - 30) + 'px'
    deletetButton.style.top = (rect.top + rect.height + 8) + 'px'

    this.replyButton = createElement(
      'div',
      {
        width: '30px',
        height: '30px',
        background: '#DDE9FF',
        borderRadius: '2px',
        display: 'flex',
        justifyContent: 'center',
        alignItems: 'center'
      },
      {
        class: 'reply-button'
      }
    );
    this.replyButton.addEventListener('click', this.onOpenReply)
    this.replyButton.innerHTML = this.replySvgStr
    this.replyButton.style.left = (rect.left + rect.width - 65) + 'px'
    this.replyButton.style.top = (rect.top + rect.height + 8) + 'px'

    this.deletetButton = deletetButton
    this.outerLineContainer.append(rectContainer)
    this.outerLineContainer.append(deletetButton)
    this.enableReply && this.outerLineContainer.append(this.replyButton)
  }

  getActualRect (viewport, s) {
    const annotation = this.annotation
    const { left: x1, top: y1, right: x2, bottom: y2 } = annotation.rect

    const start = getActualPoint(
      {
        x: x1,
        y: y1
      },
      viewport,
      s
    )
    const end = getActualPoint(
      {
        x: x2,
        y: y2
      },
      viewport,
      s
    )
    return {
      start,
      end
    }
  }

  rectCalc ({ start, end }) {
    return {
      top: start.y < end.y ? start.y : end.y,
      left: start.x < end.x ? start.x : end.x,
      width: Math.abs(end.x - start.x),
      height: Math.abs(end.y - start.y),
    };
  }

  calculate (start, end) {
    const initRect = this.rectCalc({ start, end });
    const actualBdWidth = 2

    return {
      top: initRect.top - actualBdWidth,
      left: initRect.left - actualBdWidth,
      width: initRect.width + actualBdWidth * 2,
      height: initRect.height + actualBdWidth * 2,
    }
  }

  selectAnnotation () {
    if (this.layer.selectedElementName === this.annotation.name) return
    this.handleClick()
  }

  handleClick (event) {
    if (!this.hidden || ['redaction', 'remove'].includes(this.layer.tool) || document.querySelector('.compare-document-container').contains(event?.target)) return
    this.hidden = false
    this.updateTool()
    const data = [this.markupContainer, this.outerLineContainer, this.deletetButton, this.replyButton]
    const toggleButton = document.querySelector('.toggle-button')
    toggleButton && data.push(toggleButton)
    onClickOutside(data, this.handleOutside.bind(this))
  }

  handleOutside () {
    this.hidden = true
    if (this.layer.selectedElementName === this.annotation.name) {
      this.layer.selectedElementName = null
    }
    this.outerLineContainer.remove()
  }

  updateTool () {
    if (this.hidden) {
      if (this.layer.selectedElementName === this.annotation.name) {
        this.layer.selectedElementName = null
      }
      this.outerLineContainer.remove()
    } else {
      if (this.layer.selectedElementName !== this.annotation.name) {
        const annotation = this.annotation
        this.layer.selectedElementName = annotation.name

        const annotationData = {
          ...annotation
        }
        delete annotationData.annotPtr
        delete annotationData.pagePtr
        delete annotationData.operate
        delete annotationData.targetPage

        this.eventBus.dispatch('annotationSelected', annotationData)
      }
      this.container.append(this.outerLineContainer)
    }
  }

  handleDelete (event) {
    if (this.layer.tool && event) {
      event.stopPropagation()
    }

    this.delete()
    const annotationData = {
      type: 'delete',
      annotation: {
        operate: "del-annot",
        name: this.annotation.name,
        pageIndex: this.page
      }
    }
    this.annotation.annotPtr && (annotationData.annotation.annotPtr = this.annotation.annotPtr)

    this.eventBus.dispatch('annotationChange', annotationData)
    this.layer.redrawMarkups({ pageIndex: this.page })
  }

  delete () {
    this.handleOutside()
    this.markupContainer?.remove()
    this.annotation.isDelete = true
    this.layer.deleteAnnotationInLayer({
      name: this.annotation.name
    })
  }

  setCss (ele, cssText) {
    if (!ele) return
    if (cssText) {
      for (let key in cssText) {
        ele.style[key] = cssText[key]
      }
    }
  }

  renderCanvansRect (positionArray) {
    const markupContainer = document.createElement('div')
    markupContainer.className = 'markup'

    for (let i = 0; i < positionArray.length; i++) {
      const position = positionArray[i]
      const container = document.createElement('div')
      this.setCss(container, {
        position: 'absolute',
        top: position.top + 'px',
        left: position.left + 'px',
        width: position.width + 'px',
        height: position.height + 'px'
      })
      markupContainer.append(container)
    }
    this.drawCanvas(positionArray)
    this.container.append(markupContainer)
    this.markupContainer = markupContainer
  }
  
  openReply () {
    this.eventBus.dispatch('openAnnotationReply', this.annotation)
  }

  drawCanvas(positionArray) {
    const annotation = this.annotation
    if (['highlight', 'underline', 'strikeout'].includes(annotation.type)) {
      const { R, G, B } = convertColorToRGB(annotation.color)
      this.ctx.fillStyle = `rgba(${R}, ${G}, ${B}, ${annotation.opacity || 1})`
      for (let i = 0; i < positionArray.length; i++) {
        const position = positionArray[i]
        if (annotation.type === 'highlight') {
          this.ctx.fillRect(position.left * this.ratio, position.top * this.ratio, position.width * this.ratio, position.height * this.ratio)
        } else if (annotation.type === 'underline') {
          this.ctx.fillRect(position.left * this.ratio, (position.top + position.height) * this.ratio, position.width * this.ratio, this.lineWidth)
        } else if (annotation.type === 'strikeout') {
          this.ctx.fillRect(position.left * this.ratio, (position.top + (position.height / 2)) * this.ratio, position.width * this.ratio, this.lineWidth)
        }
      }
    } else if (annotation.type === 'squiggly') {
      const amplitude = 3 * this.scale  // 波动幅度
      const wavelength = 20 * this.scale // 波长
      const { R, G, B } = convertColorToRGB(annotation.color)
      this.ctx.strokeStyle = `rgba(${R}, ${G}, ${B}, ${annotation.opacity || 1})`
      this.ctx.lineWidth = this.lineWidth

      for (let i = 0; i < positionArray.length; i++) {
        const position = positionArray[i]
        this.ctx.beginPath()
        for (let x = position.left * this.ratio; x <= (position.left + position.width) * this.ratio; x++) {
          const y = (position.top + position.height) * this.ratio + amplitude * Math.sin((x / wavelength) * 2 * Math.PI)
          this.ctx.lineTo(x, y)
        }
        this.ctx.stroke()
      }
    }
  }

  // 重新获取底图并渲染
  async reRenderAfterContentEdit({ pagePtr, rect }) {
    const rect1 = this.rect
    const rect2 = rect

    if (rect1.left > rect2.left + rect2.width ||
      rect1.left + rect1.width < rect2.left ||
      rect1.top > rect2.top + rect2.height ||
      rect1.top + rect1.height < rect2.top
    ) return

    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)
    }

    const { imageArray } = await this.messageHandler.sendWithPromise('PushRenderTask', {
      pagePtr,
      scale: this.scale * this.ratio,
      top: imgRect.top,
      left: imgRect.left,
      right: imgRect.right,
      bottom: imgRect.bottom
    })
    const imageData = this.ctx.createImageData(parseInt(imgRect.width), parseInt(imgRect.height))
    imageData.data.set(imageArray)
    this.ctx.clearRect(imgRect.left, imgRect.top, imgRect.width, imgRect.height)
    this.ctx.putImageData(imageData, imgRect.left, imgRect.top)
    this.drawCanvas(this.positionArray)
  }

  // 获取两个rect交集区域
  getIntersection(rectA, rectB) {
    const left = Math.max(rectA.left, rectB.left)
    const right = Math.min(rectA.left + rectA.width, rectB.right)
    const top = Math.max(rectA.top, rectB.top)
    const bottom = Math.min(rectA.top + rectA.height, rectB.bottom)

    if (right > left && bottom > top) {
      return {
        left,
        top,
        width: right - left,
        height: bottom - top
      }
    }

    return null
  }
}

export default TextAnnotation
