/*
Records offset information for a set of elements, relative to an origin element.
Can record the left/right OR the top/bottom OR both.
Provides methods for querying the cache by position.
*/
export class PositionCache {
  els: HTMLElement[] // assumed to be siblings
  originClientRect: ClientRect

  // arrays of coordinates (from topleft of originEl)
  // caller can access these directly
  lefts: any
  rights: any
  tops: any
  bottoms: any

  constructor(originEl: HTMLElement, els: HTMLElement[], isHorizontal: boolean, isVertical: boolean) {
    this.els = els

    let originClientRect = this.originClientRect = originEl.getBoundingClientRect() // relative to viewport top-left

    if (isHorizontal) {
      this.buildElHorizontals(originClientRect.left)
    }

    if (isVertical) {
      this.buildElVerticals(originClientRect.top)
    }
  }

  // Populates the left/right internal coordinate arrays
  buildElHorizontals(originClientLeft: number) {
    let lefts = []
    let rights = []

    for (let el of this.els) {
      let rect = el.getBoundingClientRect()
      lefts.push(rect.left - originClientLeft)
      rights.push(rect.right - originClientLeft)
    }

    this.lefts = lefts
    this.rights = rights
  }

  // Populates the top/bottom internal coordinate arrays
  buildElVerticals(originClientTop: number) {
    let tops = []
    let bottoms = []

    for (let el of this.els) {
      let rect = el.getBoundingClientRect()
      tops.push(rect.top - originClientTop)
      bottoms.push(rect.bottom - originClientTop)
    }

    this.tops = tops
    this.bottoms = bottoms
  }

  // Given a left offset (from document left), returns the index of the el that it horizontally intersects.
  // If no intersection is made, returns undefined.
  leftToIndex(leftPosition: number) {
    let { lefts, rights } = this
    let len = lefts.length
    let i

    for (i = 0; i < len; i += 1) {
      if (leftPosition >= lefts[i] && leftPosition < rights[i]) {
        return i
      }
    }

    return undefined // TODO: better
  }

  // Given a top offset (from document top), returns the index of the el that it vertically intersects.
  // If no intersection is made, returns undefined.
  topToIndex(topPosition: number) {
    let { tops, bottoms } = this
    let len = tops.length
    let i

    for (i = 0; i < len; i += 1) {
      if (topPosition >= tops[i] && topPosition < bottoms[i]) {
        return i
      }
    }

    return undefined // TODO: better
  }

  // Gets the width of the element at the given index
  getWidth(leftIndex: number) {
    return this.rights[leftIndex] - this.lefts[leftIndex]
  }

  // Gets the height of the element at the given index
  getHeight(topIndex: number) {
    return this.bottoms[topIndex] - this.tops[topIndex]
  }

  similarTo(otherCache: PositionCache) {
    return similarNumArrays(this.tops || [], otherCache.tops || []) &&
      similarNumArrays(this.bottoms || [], otherCache.bottoms || []) &&
      similarNumArrays(this.lefts || [], otherCache.lefts || []) &&
      similarNumArrays(this.rights || [], otherCache.rights || [])
  }
}

function similarNumArrays(a: number[], b: number[]): boolean {
  const len = a.length

  if (len !== b.length) {
    return false
  }

  for (let i = 0; i < len; i++) {
    if (Math.round(a[i]) !== Math.round(b[i])) {
      return false
    }
  }

  return true
}
