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

export default class Link extends Base {
  
  constructor ({
    container,
    annotation,
    page,
    viewport,
    scale,
    eventBus,
    layer,
    show = false,
    highlight
  }) {
    super({
      container,
      annotation,
      page,
      viewport,
      scale,
      eventBus,
      show,
      highlight
    })

    this.layer = layer
    this.hidden = true
    this.initial = false
    this.outline = null

    this.start = null
    this.end = null

    this.newStart = null
    this.newEnd = null

    this.startCircle = null
    this.endCircle = null

    this.show = show
    this.highlight = highlight

    this.onMousedown = this.handleMouseDown.bind(this)
    this.onMouseup = this.handleMouseUp.bind(this)
    this.onMousemove = this.handleMouseMove.bind(this)
    this.onDelete = this.handleDelete.bind(this)
    this.setHighlight = this.setHighlight.bind(this)
    this.render()
  }

  render () {
    this.eventBus._on('linkPropertyPanelChanged', this.handlePropertyPanel.bind(this))
    this.eventBus._on('setProperty', this.setProperty.bind(this))
    this.eventBus._on('setHighlightLink', this.setHighlight)

    for (let key in this.annotation) {
      if (key === 'background-color') {
        let newName = 'backgroundColor'
        this.annotation[newName] = this.annotation[key]
        delete this.annotation[key]
      }
      if (key === 'OnActivation') {
        if (this.annotation[key].Action && this.annotation[key].Action.Dest && this.annotation[key].Action.Dest.XYZ) {
          this.annotation.destPage = (parseInt(this.annotation.OnActivation.Action.Dest.XYZ.Page) + 1).toString()
          this.annotation.url = ''
        } else if (this.annotation[key].Action && this.annotation[key].Action.URI) {
          this.annotation.url = this.annotation.OnActivation.Action.URI.URI
          this.annotation.destPage = ''
        }
        delete this.annotation[key]
      }
      if (key === 'Dest') {
        if (this.annotation[key].XYZ && this.annotation[key].XYZ.Page) {
          this.annotation.destPage = (parseInt(this.annotation.Dest.XYZ.Page) + 1).toString()
          this.annotation.url = ''
        }
        delete this.annotation[key]
      }
    }
    if (this.annotation.url?.startsWith('af://')) return

    const annotation = this.annotation

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

    const rect = this.calculate(start, end)
    
    const annotationContainer = document.createElement('div')
    annotationContainer.id = annotation.name
    annotationContainer.className = 'annotation'
    annotationContainer.style.top = rect.top + 'px'
    annotationContainer.style.left = rect.left + 'px'
    annotationContainer.style.width = rect.width + 'px'
    annotationContainer.style.height = rect.height + 'px'
    annotationContainer.style.backgroundColor = this.highlight ? '#93B9FD' : ''
    annotationContainer.style.opacity = 0.3
    this.annotationContainer = annotationContainer
    this.annotationContainer.addEventListener('mousedown', this.handleClick.bind(this))

    let shapeElement
    shapeElement = createElement(
      'div',
      {
        left: 0,
        top: 0,
        width: `${Math.abs(rect.width)}px`,
        height: `${Math.abs(rect.height)}px`,
        position: 'relative'
      }
    )

    this.shapeElement = shapeElement

    this.annotationContainer.append(this.shapeElement)

    this.container.append(this.annotationContainer)

    this.buttonContainer = createElement(
      'a',
      {
        display: 'block',
        width: '100%',
        height: '100%',
        outline: 'none'
      }
    )
    this.shapeElement.append(this.buttonContainer)
    this.buttonContainer.addEventListener('blur', this.onBlur)
    if (this.annotation.url) {
      this.buttonContainer.setAttribute('href', this.annotation.url)
      this.buttonContainer.setAttribute('target', '_blank')
    }

    this.outerLineContainer = document.createElement('div')
    this.outerLineContainer.className = 'outline-container'
    this.deletetButton = createSvg(
      "svg",
      {
        width: "30",
        height: "30",
        viewBox: "0 0 30 30",
        fill: "none",
        class: 'delete-button'
      },
      {
        width: '30px',
        height: '30px'
      }
    );
    this.deletetButton.addEventListener('click', this.onDelete)
    this.deletetButton.innerHTML = this.deleteSvgStr

    const left = (rect.left + rect.width - 30) + 'px'
    const top = (rect.top + rect.height + 8) + 'px'
    this.deletetButton.style.left = left
    this.deletetButton.style.top = top
    
    const outerLine = document.createElementNS("http://www.w3.org/2000/svg", "svg");

    outerLine.style.position = 'absolute'
    outerLine.style.zIndex = 1
    outerLine.style.left = `${rect.left - 8}px`
    outerLine.style.top = `${rect.top - 8}px`
    outerLine.style.width = `${rect.width + 8 * 2}px`
    outerLine.style.height = `${rect.height + 8 * 2}px`
    this.outerLine = outerLine
    
    this.moveRect = createSvg(
      "rect",
      {
        class: "move",
        'data-id': "move",
        stroke: "#4982E6",
        'stroke-width': 1,
        'fill-opacity': 0,
        width: rect.width + 8,
        height: rect.height + 8,
        x: 4,
        y: 4
      }
    );
    
    this.topLeftRect = createSvg(
      "rect",
      {
        class: "nw-resize",
        'data-id': "nw-resize",
        fill: "#FFFFFF",
        stroke: "#4982E6",
        'stroke-width': 1,
        width: 6,
        height: 6,
        x: 1,
        y: 1
      }
    );

    this.LeftRect = createSvg(
      "rect",
      {
        class: "w-resize",
        'data-id': "w-resize",
        fill: "#FFFFFF",
        stroke: "#4982E6",
        'stroke-width': 1,
        width: 6,
        height: 6,
        x: 1,
        y: rect.height / 2 + 5
      }
    );

    this.bottomLeftRect = createSvg(
      "rect",
      {
        class: "sw-resize",
        'data-id': "sw-resize",
        fill: "#FFFFFF",
        stroke: "#4982E6",
        'stroke-width': 1,
        width: 6,
        height: 6,
        x: 1,
        y: rect.height + 9
      }
    );

    this.bottomRect = createSvg(
      "rect",
      {
        class: "s-resize",
        'data-id': "s-resize",
        fill: "#FFFFFF",
        stroke: "#4982E6",
        'stroke-width': 1,
        width: 6,
        height: 6,
        x: rect.width / 2 + 5,
        y: rect.height + 9
      }
    );
    
    this.bottomRightRect = createSvg(
      "rect",
      {
        class: "se-resize",
        'data-id': "se-resize",
        fill: "#FFFFFF",
        stroke: "#4982E6",
        'stroke-width': 1,
        width: 6,
        height: 6,
        x: rect.width + 9,
        y: rect.height + 9
      }
    );

    this.rightRect = createSvg(
      "rect",
      {
        class: "e-resize",
        'data-id': "e-resize",
        fill: "#FFFFFF",
        stroke: "#4982E6",
        'stroke-width': 1,
        width: 6,
        height: 6,
        x: rect.width + 9,
        y: rect.height / 2 + 5
      }
    );

    this.topRightRect = createSvg(
      "rect",
      {
        class: "ne-resize",
        'data-id': "ne-resize",
        fill: "#FFFFFF",
        stroke: "#4982E6",
        'stroke-width': 1,
        width: 6,
        height: 6,
        x: rect.width + 9,
        y: 1
      }
    );

    this.topRect = createSvg(
      "rect",
      {
        class: "n-resize",
        'data-id': "n-resize",
        fill: "#FFFFFF",
        stroke: "#4982E6",
        'stroke-width': 1,
        width: 6,
        height: 6,
        x: rect.width / 2 + 5,
        y: 1
      }
    );

    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.outerLineContainer.append(this.deletetButton)
    this.initial = true

    if (this.show) this.handleClick()
  }

  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 = (this.annotation.borderWidth || 2) * this.scale

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

  getInitialPoint () {
    const s = this.scale
    const startX = this.start.x
    const startY = this.start.y
    const endX = this.end.x
    const endY = this.end.y

    let x1, y1, x2, y2;
    x1 = startX / s;
    y1 = startY / s; 
    x2 = endX / s;
    y2 = endY / s;
    return {
      start: {
        x: x1,
        y: y1,
      },
      end: {
        x: x2,
        y: y2,
      },
    };
  }

  updateTool () {
    if (!this.initial) return
    if (this.hidden) {
      if (this.layer.annotationStore.selectedElementName === this.annotation.name) {
        this.layer.annotationStore.selectedElementName = null
      }
      this.outerLineContainer.remove()
    } else {
      if (this.layer.annotationStore.selectedElementName !== this.annotation.name) {
        const annotation = this.annotation
        this.layer.annotationStore.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)
      this.outerLine.addEventListener('mousedown', this.onMousedown)
      this.outerLine.addEventListener('touchstart', this.onMousedown)

      this.eventBus.dispatch('linkPanelChange', this.annotation)
    }
  }

  handleClick (event) {
    if (!this.hidden || document.fullscreenElement || this.layer.annotationStore.creating || ['redaction', 'remove'].includes(this.layer.tool) || document.querySelector('.compare-document-container').contains(event?.target) || this.layer.toolMode === 'editor') return
    if (this.layer.toolMode === 'annotation' && this.layer.tool === 'link') {
      this.hidden = false
      this.updateTool()

      onClickOutside([this.annotationContainer, this.outerLine, this.deletetButton, document.getElementById('linkPanel')], this.handleOutside.bind(this))
    } else {
      if (this.annotation.destPage) {
        this.eventBus.dispatch('pageNumberChanged', this.annotation.destPage)
      } else if (this.annotation.url) {
        this.buttonContainer.click()
      }
    }
  }

  handleOutside () {
    if (!this.hidden && this.layer.annotationStore.selectedElementName === this.annotation.name) {
      this.eventBus.dispatch('linkPanelChange', null)
      this.layer.annotationStore.selectedElementName = null
    }

    this.hidden = true

    if (this.layer.annotationStore.selectedElementName === this.annotation.name) {
      this.layer.annotationStore.selectedElementName = null
    }
    this.outerLine.removeEventListener('mousedown', this.onMousedown)
    this.outerLine.removeEventListener('touchstart', this.onMousedown)
    this.outerLineContainer.remove()

    if (!this.annotation.url && !this.annotation.destPage) {
      this.handleDelete()
    }
  }

  handleMouseDown (event) {
    if (event.button !== 0 && event.type === 'mousedown') return
    if (this.layer.tool) {
      event.stopPropagation()
    }
    const operatorId = event.target.getAttribute('data-id')
  
    const { pageX, pageY } = getClickPoint(event)
    this.startState = {
      operator: operatorId,
      clickX: pageX,
      clickY: pageY,
    }

    document.addEventListener('mousemove', this.onMousemove)
    document.addEventListener('mouseup', this.onMouseup)
    document.addEventListener('touchmove', this.onMousemove)
    document.addEventListener('touchend', this.onMouseup)

    this.handleClick()
  };

  handleMouseMove (event) {
    if (event.button !== 0 && event.type === 'mousemove') return
    if (event.type === 'touchmove') {
      document.querySelector('.document-container').addEventListener('scroll', event => event.preventDefault())
    }
    this.moving = true
    const { pageX, pageY } = getClickPoint(event)
    const start = this.start
    const end = this.end
    const startState = this.startState

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

  handleDelete (event) {
    if (!this.annotationContainer) return
    if (this.layer.tool && event) {
      event.stopPropagation()
    }
    if (event) this.handleOutside()
    this.annotationContainer.remove()

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

  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'
    }
    this.moving = false
    document.removeEventListener('mousemove', this.onMousemove)
    document.removeEventListener('mouseup', this.onMouseup)
    document.removeEventListener('touchmove', this.onMousemove)
    document.removeEventListener('touchend', this.onMouseup)
    
    const { pageX, pageY } = getClickPoint(event)
    if (pageX === this.startState.clickX && pageY === this.startState.clickY) return

    this.start = this.newStart
    this.end = this.newEnd

    const annotation = this.annotation
    const { start, end } = this.getInitialPoint()

    const rect = {
     left: start.x,
     top: start.y,
     right: end.x,
     bottom: end.y
    }

    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.calculate(start, end)

    this.newStart = start
    this.newEnd = end

    this.annotationContainer.style.top = rect.top + 'px'
    this.annotationContainer.style.left = rect.left + 'px'
    this.annotationContainer.style.width = rect.width + 'px'
    this.annotationContainer.style.height = rect.height + 'px'
    
    this.annotationContainer.style.width = `${rect.width}px`;
    this.annotationContainer.style.height = `${rect.height}px`;

    this.shapeElement.style.width = `${rect.width}px`;
    this.shapeElement.style.height = `${rect.height}px`;

    this.outerLine.style.left = `${rect.left - 8}px`
    this.outerLine.style.top = `${rect.top - 8}px`
    this.outerLine.style.width = `${rect.width + 8 * 2}px`
    this.outerLine.style.height = `${rect.height + 8 * 2}px`

    const left = (rect.left + rect.width - 30) + 'px'
    const top = (rect.top + rect.height + 8) + 'px'
    this.deletetButton.style.left = left
    this.deletetButton.style.top = top
    
    this.moveRect.setAttribute('width',  rect.width + 9)
    this.moveRect.setAttribute('height',  rect.height + 9)
    
    this.LeftRect.setAttribute("y", rect.height / 2 + 5)

    this.bottomLeftRect.setAttribute("y", rect.height + 9)

    this.bottomRect.setAttribute("x", rect.width / 2 + 5)
    this.bottomRect.setAttribute("y", rect.height + 9)


    this.bottomRightRect.setAttribute("x", rect.width + 9)
    this.bottomRightRect.setAttribute("y", rect.height + 9)


    this.rightRect.setAttribute("x", rect.width + 9)
    this.rightRect.setAttribute("y", rect.height / 2 + 5)

    
    this.topRightRect.setAttribute("x", rect.width + 9)

    this.topRect.setAttribute("x", rect.width / 2 + 5)
  }

  async handlePropertyPanel (props) {
    if (this.layer.annotationStore.selectedElementName !== this.annotation.name) return
    const annotation = this.annotation
    let updatePropsNum = 0
    for (const key in props) {
      for (const item in annotation) {
        if (item === key && annotation[item] !== props[key] && !['url', 'destPage'].includes(key)) {
          annotation[item] = props[key]
          updatePropsNum ++
        }
      }
      if (key === 'url' && annotation[key] !== props[key]) {
        this.buttonContainer.setAttribute('href', props[key])
        this.buttonContainer.setAttribute('target', '_blank')
        annotation.destPage = ''
        annotation.url = props[key]
        updatePropsNum ++
      }
      if (key === 'destPage' && annotation[key] !== props[key]) {
        annotation.url = ''
        annotation.destPage = props[key]
        updatePropsNum ++
      }
    }

    if (updatePropsNum > 0) {
      const annotationData = {
        operate: "mod-annot",
        name: annotation.name,
        pageIndex: this.page,
        pagePtr: annotation.pagePtr,
        annotPtr: annotation.annotPtr,
        url: annotation.url,
        destPage: annotation.destPage
      }
      const updateAnnot = Object.assign({}, annotationData, props)
      await this.eventBus.dispatch('annotationChange', {
        type: 'modify',
        annotation: updateAnnot
      })
    }

    this.handleOutside()
  }

  async setProperty (props) {
    if (this.layer.annotationStore.selectedElementName !== this.annotation.name) return
    const annotation = this.annotation
    let updatePropsNum = 0
    for (const key in props) {
      for (const item in annotation) {
        if (item === key && annotation[item] !== props[key] && !['url', 'destPage'].includes(key)) {
          annotation[item] = props[key]
          updatePropsNum ++
        }
      }
      if (key === 'url' && annotation[key] !== props[key]) {
        this.buttonContainer.setAttribute('href', props[key])
        this.buttonContainer.setAttribute('target', '_blank')
        annotation.destPage = ''
        annotation.url = props[key]
        updatePropsNum ++
      }
      if (key === 'destPage' && annotation[key] !== props[key]) {
        annotation.url = ''
        annotation.destPage = props[key]
        updatePropsNum ++
      }
    }

    if (updatePropsNum > 0) {
      const annotationData = {
        operate: "mod-annot",
        name: annotation.name,
        pageIndex: this.page,
        pagePtr: annotation.pagePtr,
        annotPtr: annotation.annotPtr,
        url: annotation.url,
        destPage: annotation.destPage
      }
      const updateAnnot = Object.assign({}, annotationData, props)
      await this.eventBus.dispatch('annotationChange', {
        type: 'modify',
        annotation: updateAnnot
      })
    }
  }

  isURL (str) {
    const pattern = /^(?:(?:https?|ftp):\/\/)?(?:www\.)?(?:[\w]+\.){1,3}(?:[a-z]{2,})?(?:\/[\w .-]*)*(?:\?[^\s]*)?$/;
    return pattern.test(str);
  }

  setHighlight (bool) {
    this.highlight = bool
    this.annotationContainer && (this.annotationContainer.style.backgroundColor = this.highlight ? '#93B9FD' : '')
  }
}
