import { getAbsoluteCoordinate, createElement, getClickPoint } from './annotation/utils';
import { onClickOutside, isMobileDevice } from './ui_utils';

export default class CropPage {
  constructor({
    container,
    viewport,
    scale,
    pageIndex,
    pagePtr,
    eventBus,
    messageHandler,
    pageViewer,
    annotationStore
  }) {
    this.container = container
    this.viewport = viewport
    this.scale = scale
    this.pageIndex = pageIndex
    this.pagePtr = pagePtr.pagePtr
    this.eventBus = eventBus
    this.messageHandler = messageHandler
    this.pageViewer = pageViewer
    this.annotationStore = annotationStore

    // 非原始文档点 （未缩放）
    this.startPoint = null // 绘制起始点
    this.endPoint = null // 绘制结束点

    this.listener = null
    this.mousedown = isMobileDevice ? 'touchstart' : 'mousedown'
    this.mouseup = isMobileDevice ? 'touchend' : 'mouseup'
    this.mousemove = isMobileDevice ? 'touchmove' : 'mousemove'

    this.onDrawMousedown = this.handleDrawMouseDown.bind(this)
    this.onDrawMouseup = this.handleDrawMouseUp.bind(this)
    this.onDrawMousemove = this.handleDrawMouseMove.bind(this)

    this.onMousedown = this.handleMouseDown.bind(this)
    this.onMouseup = this.handleMouseUp.bind(this)
    this.onMousemove = this.handleMouseMove.bind(this)

    this.handlePopup = this.handlePopup.bind(this)
  }

  init() {
    this.container.removeEventListener(this.mousedown, this.onDrawMousedown)

    this.container.addEventListener(this.mousedown, this.onDrawMousedown)

    setTimeout(() => {
      if (!this.cropping) this.container.style.cursor = 'crosshair'
      this.container.style.touchAction = 'none'
    })

    document.removeEventListener(this.mousemove, this.onDrawMousemove)
    document.removeEventListener(this.mouseup, this.onDrawMouseup)
  }

  resetDraw() {
    this.startPoint = null
    this.endPoint = null

    this.container.style.cursor = 'auto'

    document.removeEventListener(this.mousemove, this.onDrawMousemove)
    document.removeEventListener(this.mouseup, this.onDrawMouseup)
    this.container.removeEventListener(this.mousedown, this.onDrawMousedown)
  }

  reset() {
    this.startPoint = null
    this.endPoint = null
    if (this.cropContainer) this.cropContainer.remove()
    this.cropContainer = null
  
    this.container.style.cursor = 'auto'
    this.container.style.touchAction = ''

    document.removeEventListener(this.mousemove, this.onMousemove)
    document.removeEventListener(this.mouseup, this.onMouseup)
    this.container.removeEventListener(this.mousedown, this.onDrawMousedown)

    this.eventBus.dispatch('snipSelected', { rect: null, pageIndex: -1 })
    this.eventBus._off('cropPagePopupClicked', this.handlePopup)

    this.cropping = false
    this.annotationStore.cropping = false

    document.removeEventListener(this.mousedown, this.listener)
  }

  handleDrawMouseDown(event) {
    if(this.annotationStore.cropping) return

    const { x, y } = getAbsoluteCoordinate(this.container, event)
    this.startPoint = { x, y }

    document.addEventListener(this.mousemove, this.onDrawMousemove)
    document.addEventListener(this.mouseup, this.onDrawMouseup)
  }

  handleDrawMouseMove(event) {
    let { x, y } = getAbsoluteCoordinate(this.container, event)
    const { width, height } = this.viewport
    x = Math.min(width, x)
    x = Math.max(0, x)
    y = Math.min(height, y)
    y = Math.max(0, y)
    this.endPoint = { x, y }
    if (this.startPoint.x !== this.endPoint.x && this.startPoint.y !== this.endPoint.y) {
      if (!this.cropContainer) {
        const cropContainer = createElement(
          'div',
          {
            position: 'absolute',
            border: '1px solid #1460F3',
            cursor: 'all-scroll',
            zIndex: 2
          },
          { class: 'crop-box', 'data-id': 'move' }
        )
        this.cropContainer = cropContainer
        this.container.append(this.cropContainer)
      } else {
        const rect = this.rectCalc(this.startPoint, this.endPoint)

        const cropContainer = this.cropContainer
        cropContainer.style.top = `${rect.top}px`
        cropContainer.style.left = `${rect.left}px`
        cropContainer.style.width = `${Math.abs(rect.width - 1)}px`
        cropContainer.style.height = `${Math.abs(rect.height - 1)}px`
      }
    }
  }

  handleDrawMouseUp() {
    const startPoint = this.startPoint
    const endPoint = this.endPoint

    this.resetDraw()
    if (!endPoint || startPoint.x === endPoint.x || startPoint.y === endPoint.y) {
      this.container.style.cursor = 'crosshair'
      this.container.addEventListener(this.mousedown, this.onDrawMousedown)
      return
    }

    const start = {
      x: Math.min(startPoint.x, endPoint.x),
      y: Math.min(startPoint.y, endPoint.y)
    }
    const end = {
      x: Math.max(startPoint.x, endPoint.x),
      y: Math.max(startPoint.y, endPoint.y)
    }
    const rect = {
      left: start.x,
      top: start.y,
      right: end.x,
      bottom: end.y
    }

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

    this.renderCropBox()
  }

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

  renderCropBox() {
    const handleWidth = 10

    this.tl = createElement(
      'div',
      {
        position: 'absolute',
        top: `${handleWidth / -2}px`,
        left: `${handleWidth / -2}px`,
        width: `${handleWidth}px`,
        height: `${handleWidth}px`,
        background: '#1460F3',
        cursor: 'nw-resize'
      },
      { class: 'handle handle-tl', 'data-id': 'nw-resize' }
    )
    this.tm = createElement(
      'div',
      {
        position: 'absolute',
        top: `${handleWidth / -2}px`,
        left: '50%',
        transform: `translateX(${handleWidth / -2}px)`,
        width: `${handleWidth}px`,
        height: `${handleWidth}px`,
        background: '#1460F3',
        cursor: 'n-resize'
      },
      { class: 'handle handle-tm', 'data-id': 'n-resize' }
    )
    this.tr = createElement(
      'div',
      {
        position: 'absolute',
        top: `${handleWidth / -2}px`,
        right: `${handleWidth / -2}px`,
        width: `${handleWidth}px`,
        height: `${handleWidth}px`,
        background: '#1460F3',
        cursor: 'ne-resize'
      },
      { class: 'handle handle-tr', 'data-id': 'ne-resize' }
    )
    this.mr = createElement(
      'div',
      {
        position: 'absolute',
        top: '50%',
        right: `${handleWidth / -2}px`,
        transform: `translateY(${handleWidth / -2}px)`,
        width: `${handleWidth}px`,
        height: `${handleWidth}px`,
        background: '#1460F3',
        cursor: 'e-resize'
      },
      { class: 'handle handle-mr', 'data-id': 'e-resize' }
    )
    this.br = createElement(
      'div',
      {
        position: 'absolute',
        bottom: `${handleWidth / -2}px`,
        right: `${handleWidth / -2}px`,
        width: `${handleWidth}px`,
        height: `${handleWidth}px`,
        background: '#1460F3',
        cursor: 'se-resize'
      },
      { class: 'handle handle-br', 'data-id': 'se-resize' }
    )
    this.bm = createElement(
      'div',
      {
        position: 'absolute',
        bottom: `${handleWidth / -2}px`,
        left: '50%',
        transform: `translateX(${handleWidth / -2}px)`,
        width: `${handleWidth}px`,
        height: `${handleWidth}px`,
        background: '#1460F3',
        cursor: 's-resize'
      },
      { class: 'handle handle-bm', 'data-id': 's-resize' }
    )
    this.bl = createElement(
      'div',
      {
        position: 'absolute',
        bottom: `${handleWidth / -2}px`,
        left: `${handleWidth / -2}px`,
        width: `${handleWidth}px`,
        height: `${handleWidth}px`,
        background: '#1460F3',
        cursor: 'sw-resize'
      },
      { class: 'handle handle-bl', 'data-id': 'sw-resize' }
    )
    this.ml = createElement(
      'div',
      {
        position: 'absolute',
        top: '50%',
        left: `${handleWidth / -2}px`,
        transform: `translateY(${handleWidth / -2}px)`,
        width: `${handleWidth}px`,
        height: `${handleWidth}px`,
        background: '#1460F3',
        cursor: 'w-resize'
      },
      { class: 'handle handle-ml', 'data-id': 'w-resize' }
    )
    this.cropContainer.append(this.tl)
    this.cropContainer.append(this.tm)
    this.cropContainer.append(this.tr)
    this.cropContainer.append(this.mr)
    this.cropContainer.append(this.br)
    this.cropContainer.append(this.bm)
    this.cropContainer.append(this.bl)
    this.cropContainer.append(this.ml)

    this.cropContainer.addEventListener(this.mousedown, this.onMousedown)

    this.showPopup(this.rect)
    this.eventBus._on('cropPagePopupClicked', this.handlePopup)

    this.cropping = true
    this.annotationStore.cropping = true

    this.listener = onClickOutside([this.cropContainer], this.handleOutside.bind(this))
  }

  handleMouseDown (event) {
    const operatorId = event.target.getAttribute('data-id')
    const { pageX, pageY } = getClickPoint(event)

    this.startState = {
      operator: operatorId,
      clickX: pageX,
      clickY: pageY,
    }

    document.addEventListener(this.mousemove, this.onMousemove)
    document.addEventListener(this.mouseup, this.onMouseup)
  }

  handleMouseMove (event) {
    if (event.type === 'touchmove') {
      document.body.style.overscrollBehavior = 'none'
    }
    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 },
      });
    }
  }

  handleMouseUp (event) {
    if (event.type === 'touchend') {
      document.querySelector('.document-container').style.overflow = 'auto'
    }
    document.removeEventListener(this.mousemove, this.onMousemove)
    document.removeEventListener(this.mouseup, this.onMouseup)
    
    const { pageX, pageY } = getClickPoint(event)
    if (pageX === this.startState.clickX && pageY === this.startState.clickY) {
      this.showPopup(this.rect)
      return
    }

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

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

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

    this.showPopup(this.rect)
  }

  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

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

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

    this.newStart = start
    this.newEnd = end

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

    this.showPopup(rect)
  }

  // 计算坐标并显示悬浮菜单
  showPopup(rect) {
    const pageRect = this.container.getBoundingClientRect()
    const toPageRect = {
      left: rect.left + pageRect.left,
      top: rect.top + pageRect.top,
      right: rect.right + pageRect.left,
      bottom: rect.bottom + pageRect.top
    }
    this.eventBus.dispatch('snipSelected', { rect: toPageRect, pageIndex: this.pageIndex })
  }

  async handlePopup(action) {
    if (action === 'close') {
      this.reset()
      this.init()
    }
  }

  draw() {
    document.removeEventListener(this.mousedown, this.listener)

    const cropContainer = createElement(
      'div',
      {
        position: 'absolute',
        border: '1px solid #1460F3',
        cursor: 'all-scroll',
        top: `${this.rect.top}px`,
        left: `${this.rect.left}px`,
        width: `${this.rect.right - this.rect.left}px`,
        height: `${this.rect.bottom - this.rect.top}px`,
        zIndex: 2
      },
      { class: 'crop-box', 'data-id': 'move' }
    )
    this.cropContainer = cropContainer
    this.container.append(this.cropContainer)
    this.renderCropBox()
  }

  handleOutside() {
    this.reset()
    this.init()
  }

  // 更新初始属性
  updateInitialProps() {
    this.container = this.pageViewer.div
    this.viewport = this.pageViewer.viewport
    this.scale = this.pageViewer.scale
    this.pageIndex = this.pageViewer.pageIndex
    this.pagePtr = this.pageViewer.pagesPtr[this.pageViewer.pageIndex].pagePtr
    this.eventBus = this.pageViewer.eventBus
    this.messageHandler = this.pageViewer.messageHandler
    this.annotationStore = this.pageViewer.annotationStore
  }
}
