import { QuillConst } from './constants';
import { NodeApi } from '../../json-crdt/model/api/nodes';
import { SliceStacking } from '../peritext/slice/constants';
import { PersistedSlice } from '../peritext/slice/PersistedSlice';
import { diffAttributes, getAttributes, removeErasures } from './util';
import { s } from '../../json-crdt';
const updateAttributes = (txt, attributes, pos, len) => {
    if (!attributes)
        return;
    const range = txt.rangeAt(pos, len);
    const keys = Object.keys(attributes);
    const length = keys.length;
    const savedSlices = txt.savedSlices;
    for (let i = 0; i < length; i++) {
        const key = keys[i];
        const value = attributes[key];
        if (value === null) {
            savedSlices.ins(range, SliceStacking.Erase, key);
        }
        else {
            savedSlices.ins(range, SliceStacking.One, key, s.con(value));
        }
    }
};
const rewriteAttributes = (txt, attributes, pos, len) => {
    if (typeof attributes !== 'object')
        return;
    const range = txt.rangeAt(pos, len);
    range.expand();
    const slices = txt.overlay.findOverlapping(range);
    const length = slices.size;
    const relevantOverlappingButNotContained = new Set();
    if (length) {
        const savedSlices = txt.savedSlices;
        slices.forEach((slice) => {
            if (slice instanceof PersistedSlice) {
                const isContained = range.contains(slice);
                if (!isContained) {
                    relevantOverlappingButNotContained.add(slice.type());
                    return;
                }
                const type = slice.type();
                if (type in attributes) {
                    savedSlices.del(slice.id);
                }
            }
        });
    }
    const keys = Object.keys(attributes);
    const attributeLength = keys.length;
    const attributesCopy = { ...attributes };
    for (let i = 0; i < attributeLength; i++) {
        const key = keys[i];
        const value = attributes[key];
        if (value === null && !relevantOverlappingButNotContained.has(key)) {
            delete attributesCopy[key];
        }
    }
    updateAttributes(txt, attributesCopy, pos, len);
};
const maybeUpdateAttributes = (txt, attributes, pos, len) => {
    const range = txt.rangeAt(pos, 1);
    const overlayPoint = txt.overlay.getOrNextLower(range.start);
    if (!overlayPoint && !attributes)
        return;
    if (!overlayPoint) {
        updateAttributes(txt, removeErasures(attributes), pos, len);
        return;
    }
    const pointAttributes = getAttributes(overlayPoint);
    const attributeDiff = diffAttributes(pointAttributes, attributes);
    if (attributeDiff)
        updateAttributes(txt, attributeDiff, pos, len);
};
export class QuillDeltaApi extends NodeApi {
    text() {
        return this.api.wrap(this.node.text());
    }
    slices() {
        return this.api.wrap(this.node.slices());
    }
    apply(ops) {
        const txt = this.node.txt;
        const overlay = txt.overlay;
        const length = ops.length;
        let pos = 0;
        for (let i = 0; i < length; i++) {
            overlay.refresh(true);
            const op = ops[i];
            if (typeof op.retain === 'number') {
                const { retain, attributes } = op;
                if (attributes)
                    rewriteAttributes(txt, attributes, pos, retain);
                pos += retain;
            }
            else if (typeof op.delete === 'number') {
                txt.delAt(pos, op.delete);
            }
            else if (op.insert) {
                const { insert } = op;
                let { attributes } = op;
                if (typeof insert === 'string') {
                    txt.insAt(pos, insert);
                    const insertLength = insert.length;
                    maybeUpdateAttributes(txt, attributes, pos, insertLength);
                    pos += insertLength;
                }
                else {
                    txt.insAt(pos, QuillConst.EmbedChar);
                    if (!attributes)
                        attributes = {};
                    attributes[QuillConst.EmbedSliceType] = insert;
                    maybeUpdateAttributes(txt, attributes, pos, 1);
                    pos += 1;
                }
            }
        }
    }
}
//# sourceMappingURL=QuillDeltaApi.js.map