text: /** @module */
  
  import { insertNode, deleteNode, trigger } from './topo'
  import { getFaceByPoint } from './face'
  import SpatialError from './SpatialError'
  
  /**
   * Node definition
   *
   * @typedef {Object} Node
   * @property {number} id Node ID
   * @property {module:face~Face} face Containing face
   * @property {module:coordinate~Coordinate} coordinate Coordinate
   * @property {number} minX Minimum X of bounds
   * @property {number} maxY Maximum Y of bounds
   * @property {number} minX Minimum X of bounds
   * @property {number} maxY Maximum Y of bounds
   */
  
  /**
   * Find the node at a point location.
   *
   * @param {module:topo~Topo} topo
   * @param {module:coordinate~Coordinate} coordinate
   * @return {module:node~Node}
   */
  export function getNodeByPoint (topo, coordinate) {
    const result = topo.nodesTree.search({
      minX: coordinate[0],
      minY: coordinate[1],
      maxX: coordinate[0],
      maxY: coordinate[1]
    })
    if (result.length === 0) return
    if (result.length === 1) return result[0]
    throw Error('getNodeByPoint: unexpected search result')
  }
  
  /**
   * Adds an isolated node to a face in a topology and returns the new node. If face is null, the node is still created.
   *
   * @param {module:topo~Topo} topo
   * @param {module:coordinate~Coordinate} coordinate
   * @return {module:node~Node}
   */
  export function addIsoNode (topo, coordinate) {
    const { nodesTree: tree, faces } = topo
  
    const containingFaces = getFaceByPoint(topo, coordinate, 0)
  
    const node = {
      id: topo.nodesSeq,
      face: containingFaces.length === 0 ? faces[0] : containingFaces[0],
      coordinate,
      minX: coordinate[0],
      minY: coordinate[1],
      maxX: coordinate[0],
      maxY: coordinate[1]
    }
  
    if (!tree.collides(node)) {
      insertNode(topo, node)
      trigger(topo, 'addnode', node)
      return node
    } else {
      throw new SpatialError('coincident node')
    }
  }
  
  export function removeIsoNode (topo, node) {
    if (!node.face) throw new SpatialError('not isolated node')
    deleteNode(topo, node)
    trigger(topo, 'removenode', node)
  }
  