// @ts-nocheck

/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import { invariant } from '@tamagui/react-native-web-internals'

export type CellRegion = {
  first: number
  last: number
  isSpacer: boolean
}

export class CellRenderMask {
  private _numCells: number
  private _regions: Array<CellRegion>

  constructor(numCells: number) {
    invariant(numCells >= 0, 'CellRenderMask must contain a non-negative number of cells')

    this._numCells = numCells

    if (numCells === 0) {
      this._regions = []
    } else {
      this._regions = [
        {
          first: 0,
          last: numCells - 1,
          isSpacer: true,
        },
      ]
    }
  }

  enumerateRegions(): ReadonlyArray<CellRegion> {
    return this._regions
  }

  addCells(cells: { first: number; last: number }): void {
    invariant(
      cells.first >= 0 &&
        cells.first < this._numCells &&
        cells.last >= -1 &&
        cells.last < this._numCells &&
        cells.last >= cells.first - 1,
      'CellRenderMask.addCells called with invalid cell range'
    )

    // VirtualizedList uses inclusive ranges, where zero-count states are
    // possible. E.g. [0, -1] for no cells, starting at 0.
    if (cells.last < cells.first) {
      return
    }

    const [firstIntersect, firstIntersectIdx] = this._findRegion(cells.first)
    const [lastIntersect, lastIntersectIdx] = this._findRegion(cells.last)

    // Fast-path if the cells to add are already all present in the mask. We
    // will otherwise need to do some mutation.
    if (firstIntersectIdx === lastIntersectIdx && !firstIntersect.isSpacer) {
      return
    }

    // We need to replace the existing covered regions with 1-3 new regions
    // depending whether we need to split spacers out of overlapping regions.
    const newLeadRegion: Array<CellRegion> = []
    const newTailRegion: Array<CellRegion> = []
    const newMainRegion: CellRegion = {
      ...cells,
      isSpacer: false,
    }

    if (firstIntersect.first < newMainRegion.first) {
      if (firstIntersect.isSpacer) {
        newLeadRegion.push({
          first: firstIntersect.first,
          last: newMainRegion.first - 1,
          isSpacer: true,
        })
      } else {
        newMainRegion.first = firstIntersect.first
      }
    }

    if (lastIntersect.last > newMainRegion.last) {
      if (lastIntersect.isSpacer) {
        newTailRegion.push({
          first: newMainRegion.last + 1,
          last: lastIntersect.last,
          isSpacer: true,
        })
      } else {
        newMainRegion.last = lastIntersect.last
      }
    }

    const replacementRegions: Array<CellRegion> = [
      ...newLeadRegion,
      newMainRegion,
      ...newTailRegion,
    ]
    const numRegionsToDelete = lastIntersectIdx - firstIntersectIdx + 1
    this._regions.splice(firstIntersectIdx, numRegionsToDelete, ...replacementRegions)
  }

  numCells(): number {
    return this._numCells
  }

  equals(other: CellRenderMask): boolean {
    return (
      this._numCells === other._numCells &&
      this._regions.length === other._regions.length &&
      this._regions.every(
        (region, i) =>
          region.first === other._regions[i].first &&
          region.last === other._regions[i].last &&
          region.isSpacer === other._regions[i].isSpacer
      )
    )
  }

  private _findRegion(cellIdx: number): [CellRegion, number] {
    let firstIdx = 0
    let lastIdx = this._regions.length - 1

    while (firstIdx <= lastIdx) {
      const middleIdx = Math.floor((firstIdx + lastIdx) / 2)
      const middleRegion = this._regions[middleIdx]

      if (cellIdx >= middleRegion.first && cellIdx <= middleRegion.last) {
        return [middleRegion, middleIdx]
      } else if (cellIdx < middleRegion.first) {
        lastIdx = middleIdx - 1
      } else if (cellIdx > middleRegion.last) {
        firstIdx = middleIdx + 1
      }
    }

    invariant(false, `A region was not found containing cellIdx ${cellIdx}`)
  }
}
