# src/Store/OperationStore.js

    import Tree from '../Util/Tree.js'
    import RootID from '../Util/ID/RootID.js'
    import { getStruct } from '../Util/structReferences.js'
    import { logID } from '../MessageHandler/messageToString.js'
    import GC from '../Struct/GC.js'

    export default class OperationStore extends Tree {
      constructor (y) {
        super()
        this.y = y
      }
      logTable () {
        const items = []
        this.iterate(null, null, function (item) {
          if (item.constructor === GC) {
            items.push({
              id: logID(item),
              content: item._length,
              deleted: 'GC'
            })
          } else {
            items.push({
              id: logID(item),
              origin: logID(item._origin === null ? null : item._origin._lastId),
              left: logID(item._left === null ? null : item._left._lastId),
              right: logID(item._right),
              right_origin: logID(item._right_origin),
              parent: logID(item._parent),
              parentSub: item._parentSub,
              deleted: item._deleted,
              content: JSON.stringify(item._content)
            })
          }
        })
        console.table(items)
      }
      get (id) {
        let struct = this.find(id)
        if (struct === null && id instanceof RootID) {
          const Constr = getStruct(id.type)
          const y = this.y
          struct = new Constr()
          struct._id = id
          struct._parent = y
          y.transact(() => {
            struct._integrate(y)
          })
          this.put(struct)
        }
        return struct
      }
      // Use getItem for structs with _length > 1
      getItem (id) {
        var item = this.findWithUpperBound(id)
        if (item === null) {
          return null
        }
        const itemID = item._id
        if (id.user === itemID.user && id.clock < itemID.clock + item._length) {
          return item
        } else {
          return null
        }
      }
      // Return an insertion such that id is the first element of content
      // This function manipulates an item, if necessary
      getItemCleanStart (id) {
        var ins = this.getItem(id)
        if (ins === null || ins._length === 1) {
          return ins
        }
        const insID = ins._id
        if (insID.clock === id.clock) {
          return ins
        } else {
          return ins._splitAt(this.y, id.clock - insID.clock)
        }
      }
      // Return an insertion such that id is the last element of content
      // This function manipulates an operation, if necessary
      getItemCleanEnd (id) {
        var ins = this.getItem(id)
        if (ins === null || ins._length === 1) {
          return ins
        }
        const insID = ins._id
        if (insID.clock + ins._length - 1 === id.clock) {
          return ins
        } else {
          ins._splitAt(this.y, id.clock - insID.clock + 1)
          return ins
        }
      }
    }