import { Angle } from '@antv/x6-geometry'
import { ResolveOptions, resolve } from './util'
import { NodeAnchor } from './index'

export interface OrthEndpointOptions extends ResolveOptions {
  padding: number
}

const orthogonal: NodeAnchor.ResolvedDefinition<OrthEndpointOptions> =
  function (view, magnet, refPoint, options) {
    const angle = view.cell.getAngle()
    const bbox = view.getBBoxOfElement(magnet)
    const result = bbox.getCenter()
    const topLeft = bbox.getTopLeft()
    const bottomRight = bbox.getBottomRight()

    let padding = options.padding
    if (!Number.isFinite(padding)) {
      padding = 0
    }

    if (
      topLeft.y + padding <= refPoint.y &&
      refPoint.y <= bottomRight.y - padding
    ) {
      const dy = refPoint.y - result.y
      result.x +=
        angle === 0 || angle === 180
          ? 0
          : (dy * 1) / Math.tan(Angle.toRad(angle))
      result.y += dy
    } else if (
      topLeft.x + padding <= refPoint.x &&
      refPoint.x <= bottomRight.x - padding
    ) {
      const dx = refPoint.x - result.x
      result.y +=
        angle === 90 || angle === 270 ? 0 : dx * Math.tan(Angle.toRad(angle))
      result.x += dx
    }

    return result
  }

/**
 * Tries to place the anchor of the edge inside the view bbox so that the
 * edge is made orthogonal. The anchor is placed along two line segments
 * inside the view bbox (between the centers of the top and bottom side and
 * between the centers of the left and right sides). If it is not possible
 * to place the anchor so that the edge would be orthogonal, the anchor is
 * placed at the center of the view bbox instead.
 */
export const orth = resolve<
  NodeAnchor.ResolvedDefinition<OrthEndpointOptions>,
  NodeAnchor.Definition<OrthEndpointOptions>
>(orthogonal)
