// @ts-nocheck
import copy from 'copy-to-clipboard';
import { getAbsoluteCoordinate, getInitialPoint, getActualPoint } from './annotation/utils.js';

const markupType = ['highlight', 'underline', 'squiggly', 'strikeout', '']
type Point = {
  x: number,
  y: number
}

type PagePtr = {
  doc: number
  pagePtr: number
  textPtr: number
}
export default class TextSelection {
  _selection: string | null
  #pagePtr: number | null
  #textPtr: number | null
  messageHandler: any
  container: HTMLDivElement | null
  textContainer: HTMLDivElement | null
  selected: any
  viewport: {
    width: number
    height: number
  }
  scale: number
  eventBus: any
  _tool: string | null = null
  color: string | null = null
  selecting: boolean = false
  pageViewer: any
  pageIndex: number

  constructor(options: {
    viewport: {
      width: number,
      height: number
    },
    scale: number,
    pageIndex: number,
    container: HTMLDivElement,
    pagePtr: PagePtr,
    messageHandler: any,
    eventBus: any,
    selected: any,
    tool: string,
    color: string,
    pageViewer: any
  }) {
    this._selection = null
    this.messageHandler = options.messageHandler
    this.container = options.container || null
    this.#textPtr = options.pagePtr.textPtr
    this.#pagePtr = options.pagePtr.pagePtr
    this.selected = options.selected
    this.viewport = options.viewport
    this.scale = options.scale
    this.eventBus = options.eventBus
    this.pageIndex = options.pageIndex
    this.pageViewer = options.pageViewer

    this.handleTool = this.handleTool.bind(this)
    this.handleToolMode = this.handleToolMode.bind(this)
    this.handleMouseDown = this.handleMouseDown.bind(this)
    this.handleMouseMove = this.handleMouseMove.bind(this)
    this.handleMouseUp = this.handleMouseUp.bind(this)
    this.handleKeyDown = this.handleKeyDown.bind(this)

    this.toolMode = options.toolMode
    this.tool = options.tool
    this.color = options.color
    this.eventBus._on('toolChanged', this.handleTool)
    this.eventBus._on('toolModeChanged', this.handleToolMode)
  }

  handleToolMode(mode: string) {
    if (mode === this.toolMode) return
    this.toolMode = mode

    document.removeEventListener('keydown', this.handleKeyDown)
    document.removeEventListener('mousedown', this.handleMouseDown)
    document.removeEventListener('touchstart', this.handleMouseDown)
    document.removeEventListener('mousemove', this.handleMouseMove)
    document.removeEventListener('touchmove', this.handleMouseMove)

    if (this.toolMode === 'view' || this.toolMode === 'annotation') {
      document.addEventListener('keydown', this.handleKeyDown)
      document.addEventListener('mousedown', this.handleMouseDown)
      document.addEventListener('touchstart', this.handleMouseDown)
      document.addEventListener('mousemove', this.handleMouseMove)
      document.addEventListener('touchmove', this.handleMouseMove)
    }
  }

  handleTool ({
    tool,
    color
  }: {
    tool: string,
    color: string
  }) {
    this.tool = tool
    this.color = color

    if (markupType.includes(this.tool)) {
      document.body.style.userSelect = 'auto'
      document.body.style.webkitUserSelect = 'auto'
    } else {
      document.body.style.userSelect = 'none'
      document.body.style.webkitUserSelect = 'none'
    }
  }

  get tool () {
    return this._tool
  }

  set tool (toolType: string) {
    if ((toolType === this._tool && toolType !== '')) return
    
    if (!markupType.includes(toolType) || this.toolMode === 'editor') {
      document.removeEventListener('keydown', this.handleKeyDown)
      document.removeEventListener('mousedown', this.handleMouseDown)
      document.removeEventListener('touchstart', this.handleMouseDown)
      document.removeEventListener('mousemove', this.handleMouseMove)
      document.removeEventListener('touchmove', this.handleMouseMove)
      this._tool = toolType
      return
    }
    if (!(markupType.includes(toolType) && markupType.includes(this.tool))) {
      document.addEventListener('keydown', this.handleKeyDown)
      document.addEventListener('mousedown', this.handleMouseDown)
      document.addEventListener('touchstart', this.handleMouseDown)
      document.addEventListener('mousemove', this.handleMouseMove)
      document.addEventListener('touchmove', this.handleMouseMove)
    }
    this._tool = toolType
  }

  testPoint(event: MouseEvent) {
    const { x, y, width, height } = getAbsoluteCoordinate(this.container, event)
    if (x < 0 || y < 0 || x > width || y > height) return false
    return {
      x,
      y
    }
  }

  handleKeyDown(event: KeyboardEvent) {
    if (event.key.toLocaleLowerCase() === 'c' && (event.ctrlKey || event.metaKey)) {
      const text = this._selection?.textContent
      if (text) {
        copy(text)
      }
    }
  }

  handleMouseDown(event: MouseEvent) {
    this.cleanSelection()
    const tool = this.tool
    if (!markupType.includes(tool)) return
    const inPage = this.testPoint(event)
    if (!inPage) return

    const { x, y } = inPage
    const point = getInitialPoint({ x, y }, this.viewport, this.scale)
    this.startPoint = point
    this.selecting = true

    document.addEventListener('mouseup', this.handleMouseUp)
    document.addEventListener('touchend', this.handleMouseUp)
  }

  async handleMouseMove(event: MouseEvent) {
    const inPage = this.testPoint(event)
    if (!inPage || document.querySelector('.annotationContainer .outline-container')) {
      this.container?.classList.remove('text')
      return
    }
    const { x, y } = inPage
    const point = getInitialPoint({ x, y }, this.viewport, this.scale)

    const isText = await this.messageHandler.sendWithPromise('GetCharIndexAtPos', {
      pagePtr: this.#pagePtr,
      textPtr: this.#textPtr,
      point
    })
    if (isText) {
      this.container?.classList.add('text')
      this.endPoint = point
    } else {
      this.container?.classList.remove('text')
      return
    }
    if (!this.selecting) return

    const selection = await this.messageHandler.sendWithPromise('GetCharsRangeAtPos', {
      pagePtr: this.#pagePtr,
      textPtr: this.#textPtr,
      start: this.startPoint,
      end: this.endPoint
    })
    this._selection = selection
    this.updateSelection()
  }

  handleMouseUp() {
    const markupType = ['highlight', 'underline', 'squiggly', 'strikeout']
    if (this.selecting && this._selection && markupType.includes(this.tool)) {
      const annotationData = {
        operate: "add-annot",
        type: this.tool,
        pageIndex: this.pageIndex,
        date: new Date(),
        opacity: 0.5,
        quadPoints: this.quadPoints,
        rect: this.rect,
        color: this.color,
        contents: this._selection.textContent || undefined
      }

      this.eventBus.dispatch('annotationChange', {
        type: 'add',
        annotation: annotationData
      })
      this.cleanSelection()
    }
    this.startPoint = null
    this.endPoint = null
    this.selecting = false
    document.removeEventListener('mouseup', this.handleMouseUp)
    document.removeEventListener('touchend', this.handleMouseUp)
  }

  updateSelection() {
    const textRects = this._selection.textRects
    this.textContainer?.textContent = ''
    const quadPoints = []
    const topArray = []
    const bottomArray = []
    const leftArray = []
    const rightArray = []
    for (let i = 0; i < textRects.length; i++) {
      const textRect = textRects[i]
      const { Left: left, Top: top, Right: right, Bottom: bottom } = textRect
      const rect = {
        left: left * this.scale,
        top: top * this.scale,
        right: right * this.scale,
        bottom: bottom * this.scale
      }
      topArray.push(top)
      bottomArray.push(bottom)
      leftArray.push(left)
      rightArray.push(right)
      const leftTop = {
        PointX: left,
        PointY: top
      }
      const rightTop = {
        PointX: right,
        PointY: top
      }
      const leftBottom = {
        PointX: left,
        PointY: bottom
      }
      const rightBottom = {
        PointX: right,
        PointY: bottom
      }
      quadPoints.push(leftTop, rightTop, leftBottom, rightBottom)
      this.drawSelection(rect)
    }
    this.quadPoints = quadPoints
    const top = Math.min(...topArray)
    const bottom = Math.max(...bottomArray)
    const left = Math.min(...leftArray)
    const right = Math.max(...rightArray)
    this.rect = {
      left,
      top,
      right,
      bottom
    }
  }

  drawSelection(rect: {
    left: number,
    top: number,
    right: number,
    bottom: number
  }) {
    if (!this.textContainer) {
      this.textContainer = document.createElement('div')
      this.textContainer.classList.add('text-container')
      this.container?.appendChild(this.textContainer)
    }
    const { left, top, right, bottom } = rect
    const selection = document.createElement('div')
    selection.classList.add('text-selection')
    selection.style.left = `${left}px`
    selection.style.top = `${top}px`
    selection.style.width = `${right - left}px`
    selection.style.height = `${bottom - top}px`
    this.textContainer?.appendChild(selection)
  }

  cleanSelection() {
    this.textContainer?.remove()
    this.textContainer = null
    this._selection = null
  }

  destroy() {
    this.textContainer?.remove()
    this.textContainer = null
    this._selection = null
    document.removeEventListener('keydown', this.handleKeyDown)
    document.removeEventListener('mousedown', this.handleMouseDown)
    document.removeEventListener('touchstart', this.handleMouseDown)
    document.removeEventListener('mousemove', this.handleMouseMove)
    document.removeEventListener('touchmove', this.handleMouseMove)
    document.removeEventListener('mouseup', this.handleMouseUp)
    document.removeEventListener('touchend', this.handleMouseUp)

    this.eventBus._off('toolChanged', this.handleTool)
    this.eventBus._off('toolModeChanged', this.handleToolMode)
  }
}
