/**
 * @module utils
 */

import * as ID from '../utils/ID.js'
import { ItemJSON } from '../structs/ItemJSON.js'
import { ItemString } from '../structs/ItemString.js'

/**
 * Try to merge all items in os with their successors.
 *
 * Some transformations (like delete) fragment items.
 * Item(c: 'ab') + Delete(1,1) + Delete(0, 1) -> Item(c: 'a',deleted);Item(c: 'b',deleted)
 *
 * This functions merges the fragmented nodes together:
 * Item(c: 'a',deleted);Item(c: 'b',deleted) -> Item(c: 'ab', deleted)
 *
 * TODO: The Tree implementation does not support deletions in-spot.
 *       This is why all deletions must be performed after the traversal.
 *
 */
export const defragmentItemContent = y => {
  const os = y.os
  if (os.length < 2) {
    return
  }
  let deletes = []
  let node = os.findSmallestNode()
  let next = node.next()
  while (next !== null) {
    let a = node.val
    let b = next.val
    if (
      (a instanceof ItemJSON || a instanceof ItemString) &&
      a.constructor === b.constructor &&
      a._deleted === b._deleted &&
      a._right === b &&
      (ID.createID(a._id.user, a._id.clock + a._length)).equals(b._id)
    ) {
      a._right = b._right
      if (a instanceof ItemJSON) {
        a._content = a._content.concat(b._content)
      } else if (a instanceof ItemString) {
        a._content += b._content
      }
      // delete b later
      deletes.push(b._id)
      // do not iterate node!
      // !(node = next)
    } else {
      // not able to merge node, get next node
      node = next
    }
    // update next
    next = next.next()
  }
  for (let i = deletes.length - 1; i >= 0; i--) {
    os.delete(deletes[i])
  }
}