import { fabric } from 'fabric'
import { v4 as uuidv4 } from 'uuid'
import { QLine } from '@/components/fabric/QLine'
import { distanceBetweenPoints, findTopTwoIndexesByDistance, getDirectionByPoint, sortedPointLessEightPoint, sortedPoints } from '@/util/canvas-util'
import { calculateAngle, dividePolygon, drawHelpLineInHexagon } from '@/util/qpolygon-utils'

export const QPolygon = fabric.util.createClass(fabric.Polygon, {
  type: 'QPolygon',
  lines: [],
  texts: [],
  id: null,
  length: 0,
  hips: [],
  ridges: [],
  connectRidges: [],
  cells: [],
  initialize: function (points, options, canvas) {
    // 소수점 전부 제거
    points.forEach((point) => {
      point.x = Math.round(point.x)
      point.y = Math.round(point.y)
    })
    options.sort = options.sort ?? true
    if (!options.sort && points.length <= 8) {
      points = sortedPointLessEightPoint(points)
    } else {
      let isDiagonal = false
      points.forEach((point, i) => {
        if (isDiagonal) {
          return
        }
        const nextPoint = points[(i + 1) % points.length]
        const angle = calculateAngle(point, nextPoint)
        if (!(Math.abs(angle) === 0 || Math.abs(angle) === 180)) {
          isDiagonal = true
        }
      })

      if (!isDiagonal) {
        points = sortedPoints(points)
      }
    }

    this.callSuper('initialize', points, options)
    if (options.id) {
      this.id = options.id
    } else {
      this.id = uuidv4()
    }

    if (canvas) {
      this.canvas = canvas
    }

    this.init()
    this.initLines()
    this.setShape()
  },

  setShape() {
    let shape = 0
    if (this.lines.length !== 6) {
      return
    }
    //외각선 기준
    const topIndex = findTopTwoIndexesByDistance(this.lines).sort((a, b) => a - b) //배열중에 큰 2값을 가져옴 TODO: 나중에는 인자로 받아서 다각으로 수정 해야됨

    //일단 배열 6개 짜리 기준의 선 번호
    if (topIndex[0] === 4) {
      if (topIndex[1] === 5) {
        //1번
        shape = 1
      }
    } else if (topIndex[0] === 1) {
      //4번
      if (topIndex[1] === 2) {
        shape = 4
      }
    } else if (topIndex[0] === 0) {
      if (topIndex[1] === 1) {
        //2번
        shape = 2
      } else if (topIndex[1] === 5) {
        //3번
        shape = 3
      }
    }

    this.shape = shape
  },

  toObject: function (propertiesToInclude) {
    return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), {
      type: this.type,
      text: this.text,
      hips: this.hips,
      ridges: this.ridges,
      connectRidges: this.connectRidges,
    })
  },
  init: function () {
    this.addLengthText()

    this.on('moving', () => {
      this.addLengthText()
    })

    this.on('modified', (e) => {
      this.addLengthText()
    })

    this.on('selected', () => {
      Object.keys(this.controls).forEach((controlKey) => {
        if (controlKey !== 'ml' && controlKey !== 'mr') {
          this.setControlVisible(controlKey, false)
        }
      })
    })

    this.on('removed', () => {
      const thisText = this.canvas.getObjects().filter((obj) => obj.name === 'lengthText' && obj.parentId === this.id)
      thisText.forEach((text) => {
        this.canvas.remove(text)
      })
      this.texts = null
    })
  },

  initLines() {
    this.lines = []

    this.points.forEach((point, i) => {
      const nextPoint = this.points[(i + 1) % this.points.length]
      const line = new QLine([point.x, point.y, nextPoint.x, nextPoint.y], {
        stroke: this.stroke,
        strokeWidth: this.strokeWidth,
        fontSize: this.fontSize,
        direction: getDirectionByPoint(point, nextPoint),
        idx: i,
      })
      line.startPoint = point
      line.endPoint = nextPoint
      this.lines.push(line)
    })
  },

  // 보조선 그리기
  drawHelpLine(chon = 4) {
    drawHelpLineInHexagon(this, chon)
  },

  addLengthText() {
    let points = this.points

    points.forEach((start, i) => {
      const thisText = this.canvas.getObjects().find((obj) => obj.name === 'lengthText' && obj.parentId === this.id && obj.idx === i)

      const end = points[(i + 1) % points.length]
      const dx = end.x - start.x
      const dy = end.y - start.y
      const length = Math.sqrt(dx * dx + dy * dy)

      if (thisText) {
        thisText.set({
          left: (start.x + points[(i + 1) % points.length].x) / 2,
          top: (start.y + points[(i + 1) % points.length].y) / 2,
          text: length.toFixed(0),
        })
        return
      }

      const midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2)

      // Create new text object if it doesn't exist
      const text = new fabric.Text(length.toFixed(0), {
        left: midPoint.x,
        top: midPoint.y,
        fontSize: this.fontSize,
        selectable: false,
        parentId: this.id,
        idx: i,
        name: 'lengthText',
      })

      this.texts.push(text)
      this.canvas.add(text)
      this.canvas.renderAll()
    })
  },
  setFontSize(fontSize) {
    this.fontSize = fontSize
    this.text.set({ fontSize })
  },
  _render: function (ctx) {
    this.callSuper('_render', ctx)
  },
  _set: function (key, value) {
    this.callSuper('_set', key, value)
  },
  setCanvas(canvas) {
    this.canvas = canvas
  },
  fillCell(cell = { width: 50, height: 100, padding: 10 }) {
    const points = this.points

    const minX = Math.min(...points.map((p) => p.x))
    const maxX = Math.max(...points.map((p) => p.x))
    const minY = Math.min(...points.map((p) => p.y))
    const maxY = Math.max(...points.map((p) => p.y))

    const boundingBoxWidth = maxX - minX
    const boundingBoxHeight = maxY - minY

    const rectWidth = cell.width
    const rectHeight = cell.height

    const cols = Math.floor((boundingBoxWidth + cell.padding) / (rectWidth + cell.padding))
    const rows = Math.floor((boundingBoxHeight + cell.padding) / (rectHeight + cell.padding))

    //전체 높이에서 패딩을 포함하고 rows를 곱해서 여백길이를 계산 후에 2로 나누면 반높이를 넣어서 중간으로 정렬
    const tmpHeight = (boundingBoxHeight - (rectHeight + cell.padding) * rows) / 2
    //센터 정렬시에 쓴다 체크박스가 존재함 TODO: if문 추가해서 정렬해야함
    let tmpWidth = (boundingBoxWidth - (rectWidth + cell.padding) * cols) / 2

    const drawCellsArray = [] //그려진 셀의 배열

    for (let i = 0; i < cols; i++) {
      for (let j = 0; j < rows; j++) {
        const rectLeft = minX + i * (rectWidth + cell.padding) + tmpWidth
        const rectTop = minY + j * (rectHeight + cell.padding) + tmpHeight

        const rectPoints = [
          { x: rectLeft, y: rectTop },
          { x: rectLeft + rectWidth, y: rectTop },
          { x: rectLeft, y: rectTop + rectHeight },
          { x: rectLeft + rectWidth, y: rectTop + rectHeight },
        ]
        const allPointsInside = rectPoints.every((point) => this.inPolygon(point))

        if (allPointsInside) {
          const rect = new fabric.Rect({
            left: rectLeft,
            top: rectTop,
            width: rectWidth,
            height: rectHeight,
            fill: '#BFFD9F',
            stroke: 'black',
            selectable: true, // 선택 가능하게 설정
            lockMovementX: true, // X 축 이동 잠금
            lockMovementY: true, // Y 축 이동 잠금
            lockRotation: true, // 회전 잠금
            lockScalingX: true, // X 축 크기 조정 잠금
            lockScalingY: true, // Y 축 크기 조정 잠금
            opacity: 0.8,
            name: 'cell',
          })

          rect.on('mousedown', () => {
            rect.set({ fill: 'red' })
          })

          drawCellsArray.push(rect) //배열에 넣어서 반환한다
          this.canvas.add(rect)
        }
      }
    }
    this.canvas?.renderAll()
    this.cells = drawCellsArray
    return drawCellsArray
  },
  inPolygon(point) {
    const vertices = this.points
    let intersects = 0

    for (let i = 0; i < vertices.length; i++) {
      let vertex1 = vertices[i]
      let vertex2 = vertices[(i + 1) % vertices.length]

      if (vertex1.y > vertex2.y) {
        let tmp = vertex1
        vertex1 = vertex2
        vertex2 = tmp
      }

      if (point.y === vertex1.y || point.y === vertex2.y) {
        point.y += 0.01
      }

      if (point.y <= vertex1.y || point.y > vertex2.y) {
        continue
      }

      let xInt = ((point.y - vertex1.y) * (vertex2.x - vertex1.x)) / (vertex2.y - vertex1.y) + vertex1.x
      if (xInt < point.x) {
        intersects++
      }
    }

    return intersects % 2 === 1
  },

  distanceFromEdge(point) {
    const vertices = this.getCurrentPoints()
    let minDistance = Infinity

    for (let i = 0; i < vertices.length; i++) {
      let vertex1 = vertices[i]
      let vertex2 = vertices[(i + 1) % vertices.length]

      const dx = vertex2.x - vertex1.x
      const dy = vertex2.y - vertex1.y

      const t = ((point.x - vertex1.x) * dx + (point.y - vertex1.y) * dy) / (dx * dx + dy * dy)

      let closestPoint
      if (t < 0) {
        closestPoint = vertex1
      } else if (t > 1) {
        closestPoint = vertex2
      } else {
        closestPoint = new fabric.Point(vertex1.x + t * dx, vertex1.y + t * dy)
      }

      const distance = distanceBetweenPoints(point, closestPoint)
      if (distance < minDistance) {
        minDistance = distance
      }
    }

    return minDistance
  },
  getCurrentPoints() {
    const scaleX = this.scaleX
    const scaleY = this.scaleY

    const left = this.left
    const top = this.top

    // 시작점
    const point = this.points[0]

    const movingX = left - point.x * scaleX
    const movingY = top - point.y * scaleY

    return this.points.map((point) => {
      return {
        x: point.x * scaleX + movingX,
        y: point.y * scaleY + movingY,
      }
    })
  },
  setWall: function (wall) {
    this.wall = wall
  },
  setViewLengthText(isView) {
    this.texts.forEach((text) => {
      text.set({ visible: isView })
    })
  },
  divideLine() {
    dividePolygon(this)
  },
})
