import Base from './base';
import { getActualPoint, getClickPoint, createSvg, getHtmlToText, keepLastIndex, getInitialPoint, createElement, pastePlainText } from './utils';
import { onClickOutside } from '../ui_utils'

export default class Text extends Base {
  constructor ({
    container,
    annotation,
    page,
    viewport,
    scale,
    eventBus,
    layer,
    show = false,
    enableReply = true
  }) {
    super({
      container,
      annotation,
      page,
      layer,
      viewport,
      scale,
      eventBus,
      show
    })
    this.enableReply = enableReply

    this.textNoteStr = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
      <path fill-rule="evenodd" clip-rule="evenodd" d="M2.39961 3.60004H21.5996V18H11.9996L5.99961 21.6V18H2.39961V3.60004Z" fill="currentColor"/>
      <rect x="5.99902" y="7.20001" width="7.2" height="1.8" fill="white"/>
      <rect x="5.99902" y="11.4" width="12" height="1.8" fill="white"/>
    </svg>`
    this.deleteSvgStr = `<path xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" d="M14 1.5V4.5H19V5.5H16.5V18.5H3.5V5.5H1V4.5H6V1.5H14ZM4.5 5.5V17.5H15.5V5.5H4.5ZM13 2.5V4.5H7V2.5H13ZM8.5 8V15H7.5V8H8.5ZM12.5 15V8H11.5V15H12.5Z" fill="#333333"/>`
    
    this.hidden = true
    this.initial = false

    this.rect = null
    this.start = null
    this.end = null

    this.newStart = null
    this.newEnd = null

    this.show = show

    this.popShow = false

    this.onClick = this.handleClick.bind(this)
    this.onBlur = this.handleBlur.bind(this)
    this.onDelete = this.handleDelete.bind(this)
    this.onOpenReply = this.openReply.bind(this)

    this.onMousedown = this.handleMouseDown.bind(this)
    this.onMousemove = this.handleMouseMove.bind(this)
    this.onMouseup = this.handleMouseUp.bind(this)
    this.handleStopPropagation = this.handlestopPropagation.bind(this)
    this.onPaste = this.handlePaste.bind(this)
    this.render()
  }

  render () {
    if (this.annotation.isHidden) return
    this.renderTextElement()
    this.renderTextEditor()

    this.container.append(this.annotationContainer)
  }

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

  delete () {
    this.handleOutside()

    this.textEditorContainer?.removeEventListener('click', this.handleStopPropagation)
    this.textEditorContainer?.removeEventListener('paste', this.onPaste)
    this.annotationContainer?.remove()
    this.textEditorContainer?.remove()
    this.annotation.isDelete = true
    this.layer.deleteAnnotationInLayer({
      name: this.annotation.name
    })
  }

  handlestopPropagation(event) {
    event.stopPropagation()
  }

  handleBlur () {
    const annotation = this.annotation
    const contents = getHtmlToText(this.textEditorElement)
    if (contents !== annotation.contents) {
      this.eventBus.dispatch('annotationChange', {
        type: 'modify',
        annotation: {
          operate: "mod-annot",
          name: annotation.name,
          pageIndex: this.page,
          pagePtr: annotation.pagePtr,
          annotPtr: annotation.annotPtr,
          contents
        }
      })
      annotation.contents = contents
    }

    this.textEditorContainer.removeEventListener('click', this.handleStopPropagation)
    this.textEditorContainer.removeEventListener('paste', this.onPaste)
    this.textEditorContainer.remove()
  }

  handleMouseDown (event) {
    if (event.button !== 0 && event.type === 'mousedown') return
    if (this.layer.addMode) return
    if (this.layer.tool) {
      event.stopPropagation()
    }
    this.moving = false

    const documentViewer = this.layer.documentViewer
    const scrollElement = documentViewer.getScrollViewElement()
    this.scrollTop = scrollElement.scrollTop
    this.scrollLeft = scrollElement.scrollLeft

    const { pageX, pageY } = getClickPoint(event)
    this.startState = {
      clickX: pageX,
      clickY: pageY,
    }
    document.addEventListener('mousemove', this.onMousemove)
    document.addEventListener('mouseup', this.onMouseup)
    document.addEventListener('touchmove', this.onMousemove)
    document.addEventListener('touchend', this.onMouseup)
  };

  handleMouseMove (event) {
    if (event.button !== 0 && event.type === 'mousemove') return
    if (event.type === 'touchmove') {
      document.querySelector('.document-container').addEventListener('scroll', event => event.preventDefault())
    }
    let { pageX, pageY } = getClickPoint(event)

    const documentViewer = this.layer.documentViewer
    const scrollElement = documentViewer.getScrollViewElement()
    const scrollTop = scrollElement.scrollTop
    const scrollLeft = scrollElement.scrollLeft
    pageX = pageX + scrollLeft - this.scrollLeft
    pageY = pageY + scrollTop - this.scrollTop

    if (pageX === this.startState.clickX && pageY === this.startState.clickY) return
    this.moving = true
    const start = this.start
    const end = this.end
    const startState = this.startState

    const { width, height } = this.viewport

    let x1, y1, x2, y2

    let windowCoordinates = this.getMouseLocation(event);
    const page = documentViewer.getSelectedPage(windowCoordinates, windowCoordinates)
    if (page.first && page.first !== (this.page + 1)) {
      const pagePoint = documentViewer.windowToPage(windowCoordinates, page.first, false)

      const annotationHeight = Math.abs(start.y - end.y)
      x1 = pageX - (startState.clickX - this.start.x)
      y1 = page.first > (this.page + 1) ? pagePoint.y : pagePoint.y - annotationHeight
      x2 = pageX - (startState.clickX - this.end.x)
      y2 = page.first > (this.page + 1) ? pagePoint.y + annotationHeight : pagePoint.y
      this.page = page.first - 1

      this.annotationContainer.remove()
      const container = documentViewer.pdfViewer._pages[this.page].compdfAnnotationLayer.div
      container.append(this.annotationContainer)

      this.container = container
    } else {
      x1 = pageX - (startState.clickX - this.start.x);
      y1 = pageY - (startState.clickY - this.start.y);
      x2 = pageX - (startState.clickX - this.end.x);
      y2 = pageY - (startState.clickY - this.end.y);

      const windowPoint1 = documentViewer.pageToWindow({
        x: x1,
        y: y1
      }, this.page + 1, false)
      const windowPoint2 = documentViewer.pageToWindow({
        x: x2,
        y: y2
      }, this.page + 1, false)
      let x = 0, y = 0
      if (pageY > startState.clickY) {
        y = scrollElement.offsetHeight - (windowPoint2.y - scrollElement.scrollTop - scrollElement.offsetTop)
        y = y < 100 ? 5 : 0
      } else if (pageY < startState.clickY) {
        y = windowPoint1.y - scrollElement.scrollTop - scrollElement.offsetTop
        y = y < 100 ? -5 : 0
      }
      if (pageX > startState.clickX) {
        x = scrollElement.offsetWidth - (windowPoint2.x - scrollElement.scrollLeft - scrollElement.offsetLeft)
        x = x < 100 ? 5 : 0
      } else if (pageX < startState.clickX) {
        x = (windowPoint1.x - scrollElement.scrollLeft - scrollElement.offsetLeft)
        x = x < 100 ? -5 : 0
      }

      if (x || y) {
        scrollElement.scrollTop += y
        scrollElement.scrollLeft += x
      }

      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.update({
      start: { x: x1, y: y1 },
      end: { x: x2, y: y2 },
    });
    startState.clickX = pageX
    startState.clickY = pageY

  }

  handleMouseUp (event) {
    if (event.button !== 0 && event.type === 'mouseup') return
    if (this.layer.tool) {
      event.stopPropagation()
    }
    if (event.type === 'touchend') {
      document.querySelector('.document-container').style.overflow = 'auto'
    }
    document.removeEventListener('mousemove', this.onMousemove)
    document.removeEventListener('mouseup', this.onMouseup)
    document.removeEventListener('touchmove', this.onMousemove)
    document.removeEventListener('touchend', this.onMouseup)

    const documentViewer = this.layer.documentViewer
    const scrollElement = documentViewer.getScrollViewElement()
    scrollElement.style.touchAction = 'auto'
    this.scrollLeft = 0
    this.scrollTop = 0

    const annotation = this.annotation
    const start = getInitialPoint(this.start, this.viewport, this.viewport.scale)
    const end = getInitialPoint(this.end, this.viewport, this.viewport.scale)

    const rect = {
      left: start.x,
      top: start.y,
      right: end.x,
      bottom: end.y
    }
    if (rect === annotation.rect && annotation.pageIndex === this.page) return

    annotation.rect = rect
    if (annotation.pageIndex !== this.page) {
      const annotationData = {
        type: 'delete',
        annotation: {
          operate: "del-annot",
          name: annotation.name,
          pageIndex: annotation.pageIndex,
          annotPtr: annotation.annotPtr
        }
      }
      this.eventBus.dispatch('annotationChange', annotationData)

      const newAnnotationData = Object.assign({}, annotation)
      delete newAnnotationData.annotPtr
      delete newAnnotationData.pagePtr
      delete newAnnotationData.name
      newAnnotationData.operate = 'add-annot'
      newAnnotationData.pageIndex = this.page
      this.eventBus.dispatch('annotationChange', {
        type: 'add',
        show: true,
        annotation: newAnnotationData
      })

      this.annotationContainer.remove()
    } else {
      this.eventBus.dispatch('annotationChange', {
        type: 'modify',
        annotation: {
          operate: "mod-annot",
          name: annotation.name,
          pageIndex: this.page,
          pagePtr: annotation.pagePtr,
          annotPtr: annotation.annotPtr,
          rect
        }
      })
    }
  }

  update ({ start, end }) {
    const rect = this.rectCalc({start, end})

    this.start = start
    this.end = end

    this.annotationContainer.style.top = rect.top + 'px'
    this.annotationContainer.style.left = rect.left + 'px'

    this.textEditorContainer.style.top = rect.top + 28 + 'px'
    this.textEditorContainer.style.left = rect.left + 16 + 'px'
  }

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

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

  handleClickOutside (element) {
    if (element === this.annotationContainer || !this.popShow) return
    this.popShow = false

    this.annotationContainer.style.zIndex = 2
    this.handleBlur()
  }

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

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

  handleClick (event) {
    if (this.layer.addMode || this.layer.annotationStore.creating || ['redaction', 'remove'].includes(this.layer.tool) || document.querySelector('.compare-document-container').contains(event?.target)) return
    if (this.layer.tool) {
      event && event.stopPropagation()
    }
    if (this.moving) return
    event && this.eventBus.dispatch('closeTextEditor', event.target.closest('.annotation.text'))
    if (this.popShow) return
    this.popShow = true
    this.layer.pageDiv.append(this.textEditorContainer)

    this.textEditorContainer.addEventListener('click', this.handleStopPropagation)
    this.textEditorContainer.addEventListener('paste', this.onPaste)

    this.textEditorElement.focus()
    keepLastIndex(this.textEditorElement)
    onClickOutside([this.annotationContainer, this.textEditorContainer, document.querySelector('.toggle-button')], this.handleClickOutside.bind(this))
  }

  updateTool () {
    if (!this.popShow) {
      if (this.layer.selectedElementName === this.annotation.name) {
        this.layer.selectedElementName = null
      }
    } 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)
      }

      const documentViewer = this.layer.documentViewer
      const scrollElement = documentViewer.getScrollViewElement()
      scrollElement.style.touchAction = 'none'
    }
  }

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

    const annotation = this.annotation

    if (!annotation.color) {
      annotation.color = '#FF0000'
    }

    const rect = this.rectCalc({start, end})

    this.rect = rect

    let annotationContainer = document.createElement('div')
    annotationContainer.id = annotation.name
    annotationContainer.className = 'annotation text'
    this.setCss(annotationContainer, {
      position: 'absolute',
      left: rect.left + 'px',
      top: rect.top + 'px',
      width: '24px',
      height: '24px',
      color: annotation.color,
      opacity: annotation.opacity || 1
    })
    annotationContainer.innerHTML = this.textNoteStr
    annotationContainer.addEventListener('click', this.onClick)
    annotationContainer.addEventListener('mousedown', this.onMousedown)
    annotationContainer.addEventListener('touchstart', this.onMousedown)
    this.annotationContainer = annotationContainer
  }

  renderTextEditor () {
    const textEditorContainer = document.createElement('div')
    this.textEditorContainer = textEditorContainer
    this.setCss(textEditorContainer, {
      position: 'absolute',
      left: this.rect.left + 16 + 'px',
      top: this.rect.top + 28 + 'px',
      zIndex: 2,
      color: this.annotation.color,
      border: '2px solid',
      borderColor: this.annotation.color,
      outline: 'none',
      padding: '0 8px 28px',
      borderRadius: '4px',
      background: '#FFFFFF'
    })
    this.textEditorContainer.className = 'text-editor-container'

    const title = document.createElement('div')
    title.className = 'text-title'
    title.innerText = this.layer.annotationStore.$t('header.note')

    textEditorContainer.append(title)

    let textEditorElement = document.createElement('div')
    textEditorElement.setAttribute('role', 'textbox')
    textEditorElement.setAttribute('contenteditable', true)
    textEditorElement.setAttribute('spellcheck', false)
    this.setCss(textEditorElement, {
      width: '290px',
      height: '110px',
      fontSize: '16px',
      color: '#000',
      lineHeight: '1.2em',
      outline: 'none',
      overflow: 'auto',
      whiteSpace: 'pre-wrap',
      textAlign: 'left',
      '-webkit-user-select': 'text'
    })
    textEditorElement.className = 'text-editor'

    if (this.annotation.contents) {
      textEditorElement.innerHTML = this.annotation.contents
    } else if (this.annotation['contents-richtext']) {
      const parser = new window.DOMParser();
      const xmlDoc = parser.parseFromString(this.annotation['contents-richtext'], 'text/xml');
      const error = xmlDoc.getElementsByTagName("parsererror")
      if (error.length > 0) {
        textEditorElement.innerHTML = this.annotation['contents-richtext']
      } else {
        textEditorElement.innerHTML = xmlDoc.firstElementChild.innerText
      }
    }

    this.textEditorElement = textEditorElement

    textEditorContainer.append(textEditorElement)

    const deletetButton = createSvg(
      "svg",
      {
        width: "20",
        height: "20",
        viewBox: "0 0 20 20",
        fill: "none",
        class: 'delete-button'
      },
      {
        width: '20px',
        height: '20px',
        position: 'absolute',
        right: '10px',
        bottom: '10px'
      }
    );
    deletetButton.innerHTML = this.deleteSvgStr
    this.deletetButton = deletetButton
    textEditorContainer.append(deletetButton)
    this.deletetButton.addEventListener('click', this.onDelete)
    this.eventBus._on('closeTextEditor', this.handleClickOutside.bind(this))

    this.replyButton = createElement(
      'div',
      {
        position: 'absolute',
        right: '40px',
        bottom: '10px',
        width: '20px',
        height: '20px'
      },
      {
        class: 'reply-button'
      }
    );
    this.replyButton.innerHTML = this.replySvgStr
    this.enableReply && textEditorContainer.append(this.replyButton)
    this.replyButton.addEventListener('click', this.onOpenReply)
  }

  openReply () {
    this.eventBus.dispatch('openAnnotationReply', this.annotation)
  }

  handlePaste (event) {
    pastePlainText(event)
  }
}
